Search code examples
c#.net-standardserilogsink

Serilog : choose which sink to log at runtime


I'm going to implement Serilog within .net standard 2.0 library. I'm looking for a way to choose which sink(s) should be used for each log line.

Lets say that we define 2 sinks within the configuration (console & file) :

    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Debug()
        .WriteTo.Console()
        .WriteTo.File("c:\\temp\\SerilogTets.txt")
        .CreateLogger();

After this, we are going to write one log rule :

Log.Information("Hello, world!");  //<- how can we define which sink we want to use

I'm looking for a way howto define for which sink(s) those lines should be logged for :

  • Console & File
  • Console only
  • File only

Without relying on what loglevel it is.

Thank you in advance!

Kind regards, Kurt


Solution

  • In Serilog you can do this separation via sub-loggers using filters or via Serilog.Sinks.Map, using context properties to decide which logger will include or exclude certain messages.

    The example below defines that all log events will be written to both the Console and to a File, by default, however if the log event has a property called FileOnly in the log context, it will not be written to the Console, and likewise if the log event has a property called ConsoleOnly, it will not be written to the File.

    using Serilog;
    using Serilog.Context;
    using Serilog.Filters;
    // ...
    
    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Verbose()
        .Enrich.FromLogContext()
        .WriteTo.Logger(c =>
            c.Filter.ByExcluding(Matching.WithProperty("FileOnly"))
                .WriteTo.Console())
        .WriteTo.Logger(c =>
            c.Filter.ByExcluding(Matching.WithProperty("ConsoleOnly"))
                .WriteTo.File("C:\\Temp\\SerilogTests.txt"))
        .CreateLogger();
    
    // Writes to both Console & File
    Log.Information("Hello, world! (Console and File)");
    
    using (LogContext.PushProperty("ConsoleOnly", value: true))
    {
        // Writes only to the Console
        Log.Information("Hello, world! (Console)");
    }
    
    using (LogContext.PushProperty("FileOnly", value: true))
    {
        // Writes only to the File
        Log.Information("Hello, world! (File Only)");
    }
    
    Log.CloseAndFlush();
    

    N.B.: Ideally you'll come up with better property names that are more generic, so that when you are writing logs within your application it doesn't have to know anything about "Console" or "File". e.g. You could have a property called Secret or Classified and decide where to write the logs based on the presence of this property or not.

    There are different ways to add properties to a log event, including adding the property in the message template directly when you Log.Information, etc., via LogContext.PushProperty as in the example above, and vi Log.ForContext.

    You can see other examples of filtering, sub-loggers, and Serilog.Sinks.Map here: