Search code examples
c#asp.net-coreloggingserilog

How do I categorize Serilog logs by custom identifiers and setup specific logging levels for them?


I am aware that it is possible to have MinimumLevel.Override which then defines a specific logging level per namespace or type, but what if I want to log it by something more specific to my context or requirements?

For example, in my application I want to have a specific logging group for anything related to Users entity. This is not bound to a specific namespace or class though, it could be code in controllers, repository or any custom service. When debugging something related to Users I would like to turn on verbose logging for it, but not increase it for any other logs, services or controllers even if they are sharing the namespace with Users. Again, "Users" is just an example - it could be related to a specific type of SQL queries I'm making, whenever some service is used, only when a specific condition deep within some code gets run, etc - maybe I want all SQL queries to be logged at Error only except for a specific few that should be Verbose.

I have first tried doing this with switches

Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(builder.Configuration)
    .MinimumLevel.Override("Case1", SerilogSwitches.Case1)
    .MinimumLevel.Override("Case2", SerilogSwitches.Case2)
    .CreateLogger();

The issue with this is that "Case1" and "Case2" have to match some specific namespace or type which is not good enough for me.

I have also looked into creating a specific logger context for each of these cases using Log.ForContext

var logger1 = Log.ForContext("Context", "Case1");
var logger2 = Log.ForContext("Context", "Case2");

While this would let me select which logger to use at any point and then later more easily find and filter logs for that specific context, they all still get logged at the same level - I don't think I can change the logging level for a specific context, for example have Case1 at Information but Case2 at Error.

Any help is appreciated


Solution

  • In your example:

    Log.Logger = new LoggerConfiguration()
        .ReadFrom.Configuration(builder.Configuration)
        .MinimumLevel.Override("Case1", SerilogSwitches.Case1)
        // ...
    

    Using SourceContext will cause the level override to be matched and specific levels to be applied:

    var logger1 = Log.ForContext("SourceContext", "Case1");
    

    (There's a constant for this, called Serilog.Core.Constants.SourceContextPropertyName.)

    You'll lose the type name which would otherwise normally appear in this value, but that might be an acceptable trade-off if your scenario requires this - and you can still record the type name additionally, using something like:

    var logger1 = Log
        .ForContext("SourceContext", "Case1")
        .ForContext("SourceType", typeof(MyClass));