Search code examples
windows-serviceslog4nettopshelf

How do I stop TopShelf creating multiple log files using log4net?


One of my TopShelf-hosted Windows services is creating duplicate log files that look like this:

myapp.20140729.log
myapp.20140729.log.20140729.log

A similar problem has been described on StackOverflow before - the solutions in 10639682 didn't work for me, but 579688 suggests it could be caused by initialising the logging system twice.

I'm using the TopShelf Log4Net plugin, but I'm also doing my own log4net logging and need access to the ILog instance before the service has actually started, so my code looks like this:

public static void Main(string[] args) {
    XmlConfigurator.Configure();
    var log = LogManager.GetLogger(typeof(MyService));
    var container = new Container();
    RegisterComponents(container, log);
    log.InfoFormat("Starting MyService");
    RunService(container);
}

public static void RunService(Container container) {
    HostFactory.Run(x => {
        x.Service<PortalAdaptor>(s => {
            s.ConstructUsing(name => container.Resolve<MyService>());
            s.WhenStarted(f => f.Start());
            s.WhenStopped(f => f.Stop());
        });
        x.RunAsLocalService();
        x.SetDescription("My Service");
        x.SetDisplayName("My Service");
        x.SetServiceName("MyService");
        x.UseLog4Net();
    });
}

I've done a little digging, and it appears that calling x.UseLog4Net() in my RunService method is actually running XmlConfigurator.Configure() again - see Log4NetLogWriterFactory, line 62 in the TopShelf code.

So...

  1. Am I correct in thinking that calling XmlConfigurator.Configure() twice could be the cause of my duplicate log file issues?
  2. If so - how can I either inject an existing config into TopShelf, OR get the TopShelf initialization to run before I start my service so I can start logging before the service starts?

Solution

  • Call Hostfactory.New instead of HostFactory.Run to get a reference to the host, do your logging, then call the Run() method on the host:

    var host = HostFactory.New(configureService);
    var log = LogManager.GetLogger(typeof(MyService));
    log.InfoFormat("Starting MyService");
    host.Run();
    

    An alternative approache would be that once you have done your initial logging, call LogManager.ResetConfiguration to clear the configuration and allow TopShelf to reload it:

    Resets all values contained in the repository instance to their defaults. This removes all appenders from all loggers, sets the level of all non-root loggers to null, sets their additivity flag to true and sets the level of the root logger to Debug. Moreover, message disabling is set to its default "off" value.