Search code examples
c#asp.net-coreserilog

Serilog ignoring SourceContext


I try to implement different logfiles with Serilog in my ASP Net Core Application, however this seems to be ignored and both loggers write into both files.

Appsettings:

"Serilog": {
   "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
   "MinimumLevel": {
     "Default": "Information",
     "Override": {
       "TCP": "Information",
       "REST": "Information"
     }
   },
   "WriteTo": [
     {
       "Name": "File",
       "Args": {
         "path": "C:\\path\\TCP.log",
         "rollingInterval": "Infinite",
         "fileSizeLimitBytes": 1073741824, //1GB
         "restrictedToMinimumLevel": "Information"
       }
     },
     {
       "Name": "File",
       "Args": {
         "path": "C:\\path\\REST.log",
         "rollingInterval": "Infinite",
         "fileSizeLimitBytes": 1073741824, //1GB
         "restrictedToMinimumLevel": "Information"
       }
     }
   ],
   "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId" ]
 },

Program.cs

    builder.Host.UseSerilog((context, services, configuration) => configuration
    .ReadFrom.Configuration(context.Configuration)
    .Enrich.FromLogContext()
    .Enrich.WithMachineName()
    .Enrich.WithProcessId()
    );

Rest Controller:

    public class Controller : ControllerBase
    {
        private readonly Serilog.ILogger _rest_logger;

        public Controller ()
        {
            _rest_logger = Log.ForContext("SourceContext", "REST");
            _rest_logger.Information("Test in Controller");
        }
     }

TCP Server:

public class TCPServer : IHostedService, IDisposable
{
    private readonly Serilog.ILogger _tcp_logger;

    public TCPServer()
    {
        _tcp_logger = Log.ForContext("SourceContext","TCP");
        _tcp_logger.Information("TCP Server starting ...");
    }
}

Can someone please explain how to fix this and what the issue is Thank you


Solution

  • The issue is that your serilog configured to write into 2 files with same settings. It doesn't see that you have different "profiles" for writing logs into a file. That's why when you write anything into a log it saves into 2 files every time. One of the option is to instantiate a logger in constructor and tell specifically where you want to write the data, but maybe other will help on this approach. Or second option is to separate your serilog configs into different blocks:

    "RestLogger": {
      "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
      "MinimumLevel": {
        "Default": "Information",
        "Override": {
          "REST": "Information"
        }
      },
      "WriteTo": [
        {
          "Name": "File",
          "Args": {
            "path": "C:\\path\\REST.log",
            "rollingInterval": "Infinite",
            "fileSizeLimitBytes": 1073741824, //1GB
            "restrictedToMinimumLevel": "Information"
          }
        }
      ],
      "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId" ]
    },
    "TcpLogger": {
      "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
      "MinimumLevel": {
        "Default": "Information",
        "Override": {
          "TCP": "Information",
        }
      },
      "WriteTo": [
        {
          "Name": "File",
          "Args": {
            "path": "C:\\path\\TCP.log",
            "rollingInterval": "Infinite",
            "fileSizeLimitBytes": 1073741824, //1GB
            "restrictedToMinimumLevel": "Information"
          }
        }
      ],
      "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId" ]
    }
    

    Then if you want to have specific loggers per type, then create wrappers for loggers:

    public interface ICustomLogger
    {
        ILogger Log { get; }
    }
    
    public interface ITcpLogger : ICustomLogger
    {
    }
    
    public interface IRestLogger : ICustomLogger
    {
    }
    
    public class RestLogger : IRestLogger
    {
        private readonly Logger _logger;
    
        public ILogger Log { get { return _logger; } }
    
        public RestLogger(Logger logger)
        {
            _logger = logger;
        }
    }
    
    public class TcpLogger : ITcpLogger
    {
        private readonly Logger _logger;
    
        public ILogger Log { get { return _logger; } }
    
        public TcpLogger(Logger logger)
        {
            _logger = logger;
        }
    }
    

    As result it allows you to configure loggers in the Program.cs next way:

    ...
    ITcpLogger tcpLogger = new TcpLogger(new LoggerConfiguration()
        .ReadFrom.Configuration(builder.Configuration, "TcpLogger")
        .CreateLogger());
    builder.Services.AddSingleton(tcpLogger);
    
    IRestLogger restLogger = new RestLogger(new LoggerConfiguration()
        .ReadFrom.Configuration(builder.Configuration, "RestLogger")
        .CreateLogger());
    builder.Services.AddSingleton(restLogger);
    ...
    

    Then your usage is controllers is simple as:

    public class WeatherForecastController : ControllerBase
    {
        private readonly IRestLogger _restLogger;
    
        public WeatherForecastController(IRestLogger restLogger)
        {
            _restLogger = restLogger;
    
            _restLogger.Log.Information("Rest");
        }
    }