Search code examples
c#loggingservicelog4net

Unique log file for each instance of class


I am currently running a windows service that creates multiple instances of a class.

At the top of the service class and every other class in my solution, I have something like this:

private static readonly ILog _log = LogManager.GetLogger(typeof(SomeClassTypeHere));

In my App.config, I have Log4Net configured for a single file:

<log4net debug="true">
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
      <file value="Logs\SomeLogFileName.xml" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <countDirection value="1" />
      <maxSizeRollBackups value="30" />
      <maximumFileSize value="10MB" />
      <staticLogFileName value="true" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.XmlLayoutSchemaLog4j">
        <locationInfo value="true" />
      </layout>
    </appender>

    <root>
      <level value="INFO" />
      <appender-ref ref="RollingFileAppender" />
    </root>
  </log4net>

This works great in most respects, and everything logs to a single file. However, I'd really like to create a separate log file for each instance of a particular class that my service creates.
This is a class that we often need to monitor for support and we can have a handful of instances running at the same time.
We don't know which instances will be running at a given time, so it makes creating static files in the configuration kinda painful.

I tried taking off the readonly modifier and setting the following in the class constructor:

_log = LogManager.GetLogger("DataCollectionClass_" + deviceName + "_" + DateTime.Now.ToString("MMddyyyy"), typeof(SomeClassTypeHere));

But that requires that I define an appender manually in the configuration, which would be cumbersome and tough to keep up with.

Any thoughts on doing this in L4N? I have seen links here but don't really know if that much frameworking is necessary.


Solution

  • The code below shows how you can programatically configure log4Net without using a configuration file to achieve the effect you're looking for. Basically, it just involves creating a named logger and adding to the hierarchy.

    I used as a starting point one of the answers from here.

    using log4net;
    using log4net.Appender;
    using log4net.Layout;
    using log4net.Repository.Hierarchy;
    
    namespace LoggerTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                DeviceConnection dev1 = new DeviceConnection("Device1");
                DeviceConnection dev2 = new DeviceConnection("Device2");
    
                dev1.DoSomething();
                dev2.DoSomething();
            }
        }
    
        public class DeviceConnection
        {
            private string name;
            private readonly ILog logger;
    
            public DeviceConnection(string _name)
            {
                name = _name;
    
                logger = TestLogger.AddNamedLogger(name);
    
                logger.Info("----  Begin Logging for DeviceConnection: " + name);
            }
    
            public void DoSomething()
            {
                logger.Info("Doing something for device connection " + name);
            }
        }
    
        public static class TestLogger
        {
            private static PatternLayout _layout = new PatternLayout();
            private const string LOG_PATTERN = "%d [%t] %-5p %m%n";
    
            public static string DefaultPattern
            {
                get { return LOG_PATTERN; }
            }
    
            static TestLogger()
            {
                _layout.ConversionPattern = DefaultPattern;
                _layout.ActivateOptions();
    
                Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
                hierarchy.Configured = true;
            }
    
            public static PatternLayout DefaultLayout
            {
                get { return _layout; }
            }
    
            public static ILog AddNamedLogger(string name)
            {
                Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
                Logger newLogger = hierarchy.GetLogger(name) as Logger;
    
                PatternLayout patternLayout = new PatternLayout();
                patternLayout.ConversionPattern = LOG_PATTERN;
                patternLayout.ActivateOptions();
    
                RollingFileAppender roller = new RollingFileAppender();
                roller.Layout = patternLayout;
                roller.AppendToFile = true;
                roller.RollingStyle = RollingFileAppender.RollingMode.Size;
                roller.MaxSizeRollBackups = 4;
                roller.MaximumFileSize = "100KB";
                roller.StaticLogFileName = true;
                roller.File = name + ".log";
                roller.ActivateOptions();
    
                newLogger.AddAppender(roller);
    
                return LogManager.GetLogger(name);
            }
        }
    }