Logging with Castle.Facilities.Logging and log4net
Asked Answered
O

6

9

I'm trying to get log4net integration for Castle Windsor working. I wrote my class with an public property of type ILogger and took the configuration in my app.config like following.

<configuration>
  <configsections>
    <section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configsections>

  <castle>
    <facilities>
      <facility id="logging" type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging" loggingApi="log4net" />
    </facilities>
    <components>
      <component id="form1" type="WinFormsActiveRecordSample.Form1, WinFormsActiveRecordSample" />
    </components>
  </castle>
  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="FileAppender" />
    </root>
    <appender name="FileAppender" type="log4net.Appender.FileAppender">
      <file value="main.log" />
      <appendToFile value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date{dd.MM.yy HH:mm:ss} %-5level %logger - %message%newline" />
      </layout>
    </appender>
  </log4net>
</configuration>

In my eyes this should be working, but it doesn't. When I set loggingApi="console" it logs correctly. When I change it to log4net it does nothing. The log4net configuration was taken from another project where the block is working. What do I have to do that the log file is used? Must there be a special log4net configuration?

Thanks for any hint

Boris

Orly answered 19/2, 2010 at 15:11 Comment(1)
After a little change in my code, yes it does :)Orly
M
9

Move your log4net configuration to a separate file log4net.config, then refer that file from the facility configuration:

<facility id="loggingfacility" configfile="log4net.config" loggingapi="log4net" type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging"/>

If you want to have your log4net configuration in a section of your app.config:

public class MyLog4NetFactory: Log4netFactory {
    public MyLog4NetFactory() {
        XmlConfigurator.Configure();
    }

    public override ILogger Create(String name) {
        ILog log = LogManager.GetLogger(name);
        return new Log4netLogger(log.Logger, this);
    }

    public override ILogger Create(String name, LoggerLevel level) {
        throw new NotSupportedException("Logger levels cannot be set at runtime. Please review your configuration file.");
    }
}

then register the facility as:

<facility 
  id="loggingfacility" 
  loggingapi="custom" 
  customLoggerFactory="[fully qualified type name of MyLog4NetFactory]" 
  type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging"/>
Mesothorium answered 19/2, 2010 at 19:53 Comment(10)
That is the way i was thinking about but i like the idea of having the configuration all in a single place. Ithink i must look into the factory to get a way to load the configuration from the app.config... but when its only the logging configuration which is static most of the time... Thanks for your tipOrly
curious, must configfile="..." not be the app config? @Boris what happens if you specify [application.name].exe.config instead?Triumphal
Thanks for the new sample. I was looking into the source to make something like this when i saw your answer. The only thing was when i added the class to my project i had to use a own copy of the Log4netLogger class. When i used the code from here and set the return for Create to return new Castle.Services.Logging.Log4netIntegration.Log4netLogger(log, this); I get 2 argument errors first argument could not be converted from log4net.ILog to Log4net.Core.ILoggerOrly
Second argument could not be converted from "WinFormsActiveRecordSample.MyLog4netFactory" to "Castle.Services.Logging.Log4netIntegration.Log4netFactory" Sry for incorrect messages but i translated it freely from my german errors. The second argument could i satisfy when i change AbstractLoggerFactory to Log4netFactory. But the Ilog to Ilogger.... i dont know how to change the code to use the existing Logger implementation.Orly
@Boris, double-check your namespaces, Log4netLogger implements ILogger, not ILog: api.castleproject.org/html/…Mesothorium
@Boris: did you specify loggingapi="custom"?Mesothorium
After i copied the Log4netLogger to MyLog4netLogger and used this i could use the whole logging. The problem was not that the facility was misconfigured but the code did not compile. Ill try to get an ILogger instance to remove the copied implementation of the logger.Orly
Found the solution in the factory class. I changed to inherit from Log4netFactory and changed the return from the Create method to use return new Log4netLogger(log.Logger, this); Now it works like a charm.Orly
You have a good solution, but it won't compile. See Boosers's last comment for fixes. I'll up-vote if you fix the answer.Mcandrew
Can you help me to resolve my problem? #19247215 Thank you!Bursitis
M
6

You can use App.config for your log4net configuration without creating a custom logging factory. Simply provide the App.config file as an argument to the LoggingFacility constructor:

container
    .AddFacility("logging",
        new LoggingFacility(LoggerImplementation.Log4net,
            System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
        )

Unlike Mauricio Scheffer's answer, the default factory does ConfigureAndWatch. This works just fine for me with the App.config file, though I'm not running on IIS or anything else that restricts permissions.

I'm doing this in code because you can't reliably use the Windsor Xml configuration to load log4net configuration from App.config. This is because the location of App.config can be modified when creating a new AppDomain.

Using my solution means the log file configuration will be compiled into your code. But you can mitigate this by using a Windsor Installer to configure logging, and specify the installer (or installer assembly) from the App.config file:

public class LoggingInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container
            .AddFacility("logging",
                new LoggingFacility(LoggerImplementation.Log4net,
                    System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
                );
    }
}

...

<castle>
    <installers>
        <install assembly="MyAssemblyName" />
    </installers>
</castle>

If in the future (maybe in your test cases) you have to load the logging configuration from a different file, and can't or don't want to recompile, simply change the Xml to point to Windsor Installers in a different assembly:

<castle>
    <installers>
        <install assembly="SomeOtherAssemblyName" />
    </installers>
</castle>
Mcandrew answered 14/4, 2011 at 23:33 Comment(0)
E
3

Note that you can use the following in recent Castle versions:

container.AddFacility<LoggingFacility>(f => f.UseLog4Net().WithAppConfig());

This will use Log4net for logging and search for the log4net config section in the application configuration file.

Examen answered 10/11, 2013 at 12:33 Comment(0)
J
1

This is the entire configuration for the sample given here Home » MicroKernel/Windsor » Getting Started » Part 1 - The basics

<configuration>
  <configSections>
    <section
        name="castle"
        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
  </configSections>
  

  <castle>
    <facilities>
    <facility
         id="logging"
         type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging"
         loggingApi="log4net"
         configFile="D:\\Backup-E\\My Work\\.NET\\CastleWindsorPOC\\CastleWindosorApp\\CastleWindosorApp\\Log4Net.xml" />
    </facilities>
    <components>
      <component
          id="httpservicewatcher"
          type="CastleWindosorApp.HttpServiceWatcher, CastleWindosorApp" >
        <parameters>
          <notifiers>
            <array>
              <item>${email.notifier}</item>
              <item>${alarm.notifier}</item>
            </array>
          </notifiers>
          <Url>test url</Url>
          <!--<Logger>${logger.component}</Logger>-->
        </parameters>
      </component>
      
      <component
          id="email.notifier"
          service="CastleWindosorApp.IFailureNotifier, CastleWindosorApp"
          type="CastleWindosorApp.EmailFailureNotifier, CastleWindosorApp" />

      <component
          id="alarm.notifier"
          service="CastleWindosorApp.IFailureNotifier, CastleWindosorApp"
          type="CastleWindosorApp.AlarmFailureNotifier, CastleWindosorApp" />
     <!--<component
          id="logger.component"
          service="Castle.Core.Logging.ILogger, Castle.Core"
          type="Castle.Services.Logging.Log4netIntegration.Log4netLogger, Castle.Services.Logging.Log4netIntegration" />-->
      <component
          id="form.component"
          type="CastleWindosorApp.Form1, CastleWindosorApp" />
 

    </components>

  </castle>

The mistake I did was (as you can see the sections commented in the config file), tried to assign the Logger property once again in my class by registering the component in castle. this is not required as Castle does this automatically for you.

Cheers Badal

Jewel answered 19/2, 2010 at 15:11 Comment(0)
P
0

Following recent Castle conventions (correct me if wrong), I have solved it this way:

using Castle.Facilities.Logging;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;

namespace EVRM.Aspects.Container.Installers
{
   public class LoggingInstaller : IWindsorInstaller
    {
       public void Install(IWindsorContainer container, IConfigurationStore store)
       {
           container.AddFacility<LoggingFacility>(f => f.UseLog4Net(System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
       }
    }
}

I have simply adapted the method provided here: http://docs.castleproject.org/Windsor.Windsor-Tutorial-Part-Five-Adding-logging-support.ashx

and used the overload of UseLog4Net() that accepts an configuration file parameter.

Proper answered 30/10, 2013 at 20:11 Comment(0)
O
0

I was missing the following NuGet package.

Castle.Windsor-log4net

Install-Package Castle.Windsor-log4net

Fixed it.

Ostrich answered 2/2, 2017 at 17:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.