asp.net-coreserilogserilog-aspnetcoreserilog-sinks-seq

Toggle Serilog sink at runtime


I'm using code-based Serilog config (not appsettings). I want to toggle a sink at runtime; specifically Seq, but others too.

I know of the WriteTo.Conditional feature:

var isFooEnabled = true;
var isSeqEnabled = true;

services.AddSerilog((services, config) => config
  // ...
  .WriteTo.Console()
  .WriteTo.Conditional(x => isFooEnabled, x => x.Foo())
  .WriteTo.Conditional(x => isSeqEnabled, x => x.Seq(SEQ_URL))
);

But that code is located in the composition root (i.e. it's run during app start). I need to toggle a sink much later.

I'd probably need to resolve some service which can make the toggling decision. But how would I connect that to the above config code, at runtime? I couldn't find a hook for such functionality.

(Please note that switching to the appsettings approach is not feasible.)


Solution

  • I found a solution, based on this and this.

    Add a type that encapsulates LoggingLevelSwitch:

    public class SinkSwitch
    {
    
      private readonly LoggingLevelSwitch _switch;
      private LogEventLevel _level;
    
      public SinkSwitch(LogEventLevel level)
      {
        if (!Enum.IsDefined(level)) throw new ArgumentOutOfRangeException(nameof(level));
        _level = level;
        _switch = new LoggingLevelSwitch(level);
      }
    
      public LoggingLevelSwitch Switch => _switch;
    
      public bool IsEnabled => _switch.MinimumLevel != LevelAlias.Off;
    
      public void Toggle()
      {
        _switch.MinimumLevel = _switch.MinimumLevel == LevelAlias.Off
          ? _level
          : LevelAlias.Off;
      }
    
      public void Change(LogEventLevel level)
      {
        if (!Enum.IsDefined(level)) throw new ArgumentOutOfRangeException(nameof(level));
        _level = level;
        _switch.MinimumLevel = level;
      }
    
    }
    

    During startup, before configuring Serilog, create the switch and store it as a singleton:

    var sinkSwitch = new SinkSwitch(LogEventLevel.Information);
    services.AddSingleton(sinkSwitch);
    

    Then configure Serilog:

    services.AddSerilog((services, config) => config
      // ...
      .WriteTo.Console(levelSwitch:sinkSwitch.Switch)
      .WriteTo.Seq(SEQ_URL, controlLevelSwitch:sinkSwitch.Switch)
    );
    

    Notes: