I was recently working on a bulk processor application which was using log4net for its logging needs (specifically the RollingFileAppender). There were multiple jobs executing in independent threads each having its own Logger instance. We wanted the RollingFileAppender to use an independent file for each logger instance. In my attempts to achieve the same, here’s what I learned about log4net:
- The
fileproperty ofRollingFileAppendercan be specified as of typelog4net.Util.PatternStringin the configuration, thus enabling using patterns that are replaced dynamically byRollingFileAppender. Here’s how you specify a dynamic pattern forfileproperty ofRollingFileAppenderin the configuration:This link lists the pattern names and formats supported out of the box in
PatternLayoutstrings. PatternLayoutsupports a wider variety of patterns, includingloggerpattern name which essentially is the name of theLoggerinstance.
Now because our jobs were using different names for theirLoggerinstances, ability to useloggerpattern infileconfiguration would have enabled us to achieve our objective of different files perLoggerinstance. However we found out the additional patterns inPatternLayoutcan be used asconversionPatternbut not inside thefileproperty which is limited toPatternString.- You can pass custom pattern names to
fileproperty ofRollingFileAppenderby usinglog4net.GlobalContext. For example, you can define a custom property value like:log4net.GlobalContext.Properties["someproperty"] = "some value";which would enable you to use
%somepropertyas a pattern inside thefileproperty:The same would be replaced with its value when you initialise
log4net‘s configuration. Please note you can only uselog4net.GlobalContextfor this purpose; specifying properties throughlog4net.ThreadContextorlog4net.ThreadLogicalContextdoes not make them available as patterns to appenders. Appenderinstances are created when you initialise thelog4netconfiguration using:log4net.Config.XmlConfigurator.Configure();It is important to note this point, as this underlines why
Appendersdo not support per-Logger configuration, they are created at theRepositorylevel inlog4netand not atLoggerinstance level.
This also means if you are specifying properties throughlog4net.GlobalContext, the same needs to happen before you invokelog4net.Config.XmlConfigurator.Configure(). Otherwise those custom patterns won’t be replaced by their values you defined in your code.- All
Loggerinstances using the sameappender-refvia your configuration file are essentially using the sameAppenderinstance. If you dig into members of aLoggerinstance, you would observe it does not hold any direct reference to aAppenderinstance. Instead it makes an object ofILoggerRepositoryavailable, which is shared by allLoggerinstances in your application (unless you are manually managing customRepositoryinstances).ILoggerRepositoryfurther makes Appenders available by a call to itsGetAppendersinstance method.
The gist of all these observations and findings is that Appender instances are managed by an ILoggerRepository instance which in itself is shared by all Logger instances, effectively meaning Appenders themselves are also shared between Logger instances.
So the only way to have 2 Logger instances use different output files for a RollingFileAppender would be to define 2 different <appender> configurations having different names in your log4net configuration file and then configuring your loggers in the same configuration file to use their respective appender instances by suitably referencing an appender via appender-ref.
The following sample configuration demonstrates how to achieve the same: