Search code examples
c#loggingnunitabstraction

Log to multiple Logfiles with Microsoft.Extensions.Logging.Abstractions


I would like to log to different log files depending on the iformation.

The application log file should contain general information about the application or service behavior and exceptions.

Inside a service I would like to log to the application logfile and for stuff which is not suitable for the general logfile to a service logfile. An example for a service logfile could be the communication of a serial port.

I have a .net core application where I initialize the Log4Net Logger:

public void Configure(..., ILoggerFactory loggerFactory, ...)
{
    ...
    loggerFactory.AddLog4Net();
    ...
}

My log4net.config for the application log looks like this:

 <log4net>
  <root>
    <level value="ALL" />
    <appender-ref ref="console" />
    <appender-ref ref="file" />
  </root>
  <appender name="console" type="log4net.Appender.ConsoleAppender">
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date %level %logger - %message%newline" />
    </layout>
  </appender>
  <appender name="file" type="log4net.Appender.RollingFileAppender">
    <file value="logs/application.log" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
  <datePattern value="-yyyyMMdd" />
    <maxSizeRollBackups value="5" />
    <maximumFileSize value="5MB" />
  <preserveLogFileNameExtension value="true" />
    <staticLogFileName value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
    </layout>
  </appender>
 </log4net>

I use dependency injection to use the logger in services which are implemented in .net standard 2.0 projects. These projects have a reference to the Microsoft.Extensions.Logging.Abstractions nuget package. https://github.com/aspnet/Extensions/tree/master/src/Logging/Logging.Abstractions/src

Example Service

public void MyService(ILogger<MyService> logger)
{
   logger.Log(LogLevel.Information, $"application log info");
   // How to log to the service log file?
   // logger.LogServiceLog(LogLevel.Information, $"service log info")
}

I know that i can configure log4net to log to different log files depending on the namespace. However I want to be able to use both log files.

Is there a way to achieve this with Microsoft.Extensions.Logging.Abstractions?

Can I log a scope to a different log file?

using (_logger.BeginScope("Service Scope"))
{
    _logger.LogInformation("Service info");
}

Solution

  • Finally I found a solution which satisfies my needs. I will share it in case someone else wants to do the same.

    As I alread supposed there is a possibility to achieve this with logging in a scope. The scope properties have to be set in order that they can be used to log to a different file.

    For convenience I wrote an extension method:

    private const string PropertyKey = "ServiceLogger";
    private const string ValueSuffix = "ServiceLogger";
    
    public static void ServiceLog<TCategoryName>(this ILogger<TCategoryName> logger, LogLevel level, string message)
    {
        using (logger.BeginScope(new[] { new KeyValuePair<string, object>(PropertyKey, $"{typeof(TCategoryName).Name}{ValueSuffix}") }))
        {
            logger.Log(level, message);
        }
    }
    

    And now in the service I can use:

    public void MyService(ILogger<MyService> logger)
    {
       // application log file
       logger.Log(LogLevel.Information, $"application log info");
       // service log file
       logger.ServiceLog(LogLevel.Information, $"service log info");
    }
    

    In order that this works the log4net config has to be adjusted as I want to log to a different file if the key is set. Therfore I added a new appender whith a filter for Property Key=ServiceLogger and value=MyServiceServiceLogger.

    Note if this logs should only be in one logfile we have to set an ignore filter on the general appender for the Property Key=ServiceLogger and value containing ServiceLogger.

    Example config:

    <log4net>
      <root>
        <level value="ALL" />
        <appender-ref ref="console" />
        <appender-ref ref="app_logger" />
        <appender-ref ref="service_myservice_logger" />
      </root>
      <appender name="console" type="log4net.Appender.ConsoleAppender">
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%date [%2thread] %-5level %.50logger - %message%newline" />
        </layout>
      </appender>
      <appender name="app_logger" type="log4net.Appender.RollingFileAppender">
        <file value="logs/application.log" />
        <appendToFile value="true" />
        <rollingStyle value="Date" />
        <datePattern value="-yyyyMMdd" />
        <maxSizeRollBackups value="5" />
        <maximumFileSize value="5MB" />
        <preserveLogFileNameExtension value="true" />
        <staticLogFileName value="false" />
        <filter type="log4net.Filter.PropertyFilter">
          <key value="ServiceLogger" />
          <regexToMatch value="ServiceLogger" />
          <acceptOnMatch value="false" />
        </filter>
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%date [%2thread] %-5level %.50logger - %message%newline" />
        </layout>
      </appender>
      <appender name="service_myservice_logger" type="log4net.Appender.RollingFileAppender">
        <file value="logs/my_service.log" />
        <appendToFile value="true" />
        <rollingStyle value="Date" />
        <datePattern value="-yyyyMMdd" />
        <maxSizeRollBackups value="5" />
        <maximumFileSize value="5MB" />
        <preserveLogFileNameExtension value="true" />
        <staticLogFileName value="false" />
        <filter type="log4net.Filter.PropertyFilter">
          <Key value="ServiceLogger" />
          <StringToMatch value="MyServiceServiceLogger" />
        </filter>
        <filter type="log4net.Filter.DenyAllFilter" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%date [%2thread] %-5level %.50logger - %message%newline" />
        </layout>
      </appender>
    </log4net>