0

I'm using the slf4net.log4net nuget package to handle logging in a project. Because it must be possible for the loglevel to change at runtime, I made the configuration in code. The issue is that this code works fine in slf4net.log4net version 0.1.32.1 but when I upgrade it to version 1.0.0, the logfile is created, but the logs are not present on the logfile. I've created a dummy project to show this issue. I do not see how I can add a zip file here, so I'll just post the code here. It is a console app in net framework 4.7.2;

class Program
{
    private static string GetLoggingPath()
    {
        var path = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData,
                Environment.SpecialFolderOption.DoNotVerify), "LoggingTesting");
        Directory.CreateDirectory(path);
        return path;
    }
    static void Main(string[] args)
    {
        var layout = new PatternLayout
        {
            ConversionPattern = "%d{ABSOLUTE}: %message %newline"
        };
        layout.ActivateOptions();

        var fileAppender = new RollingFileAppender();
        fileAppender.RollingStyle = log4net.Appender.RollingFileAppender.RollingMode.Date;
        fileAppender.Layout = layout;
        var path = GetLoggingPath();
        fileAppender.File = path + System.IO.Path.DirectorySeparatorChar + "LISlogging_.txt";
        fileAppender.AppendToFile = true;
        fileAppender.PreserveLogFileNameExtension = true;
        fileAppender.StaticLogFileName = false;
        fileAppender.DatePattern = "yyyy-MM-dd";
        fileAppender.MaxSizeRollBackups = 10;
        fileAppender.ActivateOptions();
        ILoggerRepository repository = log4net.LogManager.GetRepository(Assembly.GetCallingAssembly());
        BasicConfigurator.Configure(repository, fileAppender);
        var root = (repository as Hierarchy)?.Root;
        if (root == null) return;
        root.Level = log4net.Core.Level.All;
        //  Create log4net ILoggerFactory and set the resolver
        var factory = new slf4net.log4net.Log4netLoggerFactory();
        var resolver = new SimpleFactoryResolver(factory);
        slf4net.LoggerFactory.SetFactoryResolver(resolver);
        // trigger logging
        var log = slf4net.LoggerFactory.GetLogger(typeof(Program));
        log.Info("log this line");
    }
}

public class SimpleFactoryResolver : IFactoryResolver
{
    private readonly slf4net.ILoggerFactory _factory;

    public SimpleFactoryResolver(slf4net.ILoggerFactory factory)
    {
        _factory = factory;
    }
    public slf4net.ILoggerFactory GetFactory()
    {
        return _factory;
    }
}

This dummy project was created in .net framework, but I need this in a .net core project. That is why I need to version 1.0.0 . I've also post this issue on the github page of slf4net (because it looks like a bug) : https://github.com/ef-labs/slf4net/issues/6

My main question for here on stack overflow is if there is a workaround so this can work with slf4net.log4net version 1.0.0

Gert Hermans
  • 769
  • 1
  • 9
  • 30

1 Answers1

0

I've found a workaround for this. Maybe not the cleanest solution but it works. If anyone knows a cleaner solution please add it here.

When looking at the slf4net.log4net code I found out that when it tries to configure log4net it uses xml files or config files, which is a nightmare if you want to set the loglevel at runtime. You can pass a customconfigurator as parameter of the Log4netLoggerFactory . This customconfigurator needs to implement IXmlConfigurator. The CustomConfigurator I've made accepts an IAppender and a loglevel (log4net.Core.Level). In the implementation of the Configure(ICollection(ILoggerRepository repository) method. I've set the root log level and Configured with the BasicConfigurator. The CustomConfigurator looks like this:

public class CustomConfigurator: IXmlConfigurator
{
    private readonly IAppender _appender;
    private readonly log4net.Core.Level _logLevel;

    public CustomConfigurator(IAppender appender, log4net.Core.Level logLevel)
    {
        _appender = appender;
        _logLevel = logLevel;
    }

    public ICollection Configure(ILoggerRepository repository)
    {
        var root = (repository as Hierarchy)?.Root;
        if (root != null)
        {
            root.Level = _logLevel;
        }
        return BasicConfigurator.Configure(repository, _appender);
    }

    public ICollection Configure(ILoggerRepository repository, XmlElement element)
    {
        return XmlConfigurator.Configure(repository, element);
    }

    public ICollection Configure(ILoggerRepository repository, FileInfo configFile)
    {
        return XmlConfigurator.Configure(repository, configFile);
    }

    public ICollection ConfigureAndWatch(ILoggerRepository repository, FileInfo configFile)
    {
        return XmlConfigurator.ConfigureAndWatch(repository, configFile);
    }
}

Now you can create an appender in code like shown in the question (until fileappender.ActivateOptions) Then when constructing the log4netLoggerFactory you pass an instance of CustomConfigurator which takes the fileAppender and a loglevel as parameter.

var factory = new slf4net.log4net.Log4netLoggerFactory(new CustomConfigurator(fileAppender, Level.All));
var resolver = new SimpleFactoryResolver(factory);
slf4net.LoggerFactory.SetFactoryResolver(resolver);

This should work.

Gert Hermans
  • 769
  • 1
  • 9
  • 30