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 file property of RollingFileAppender can be specified as of type log4net.Util.PatternString in the configuration, thus enabling using patterns that are replaced dynamically by RollingFileAppender. Here’s how you specify a dynamic pattern for file property of RollingFileAppender in the configuration:

    This link lists the pattern names and formats supported out of the box in PatternLayout strings.

  • PatternLayout supports a wider variety of patterns, including logger pattern name which essentially is the name of the Logger instance.
    Now because our jobs were using different names for their Logger instances, ability to use logger pattern in file configuration would have enabled us to achieve our objective of different files per Logger instance. However we found out the additional patterns in PatternLayout can be used as conversionPattern but not inside the file property which is limited to PatternString.
  • You can pass custom pattern names to file property of RollingFileAppender by using log4net.GlobalContext. For example, you can define a custom property value like:

    which would enable you to use %someproperty as a pattern inside the file property:

    The same would be replaced with its value when you initialise log4net‘s configuration. Please note you can only use log4net.GlobalContext for this purpose; specifying properties through log4net.ThreadContext or log4net.ThreadLogicalContext does not make them available as patterns to appenders.

  • Appender instances are created when you initialise the log4net configuration using:

    It is important to note this point, as this underlines why Appenders do not support per-Logger configuration, they are created at the Repository level in log4net and not at Logger instance level.
    This also means if you are specifying properties through log4net.GlobalContext, the same needs to happen before you invoke log4net.Config.XmlConfigurator.Configure(). Otherwise those custom patterns won’t be replaced by their values you defined in your code.

  • All Logger instances using the same appender-ref via your configuration file are essentially using the same Appender instance. If you dig into members of a Logger instance, you would observe it does not hold any direct reference to a Appender instance. Instead it makes an object of ILoggerRepository available, which is shared by all Logger instances in your application (unless you are manually managing custom Repository instances).

    ILoggerRepository further makes Appenders available by a call to its GetAppenders instance 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:

Share this: