Search code examples
c#.net-coreloggingserilog

Configure Serilog to filter certain logs to a different file using appsettings.json


I have an application which uses Serilog to write log-data to a file. I'm now trying to configure my logger with sub-loggers in appsettings.json in such a way so that I can filter certain logs to a different file. I'm aware that I could configure the logger in code, but I want to do it via appsettings.json.

Basically, I want all logs to go to the same file, except a certain type of log. I used the Serilog wiki and a few blog posts and stackoverflow entries, mainly this one, to achieve my goal.From what I've read using the following should allow me to filter for this type of log entry:

using (LogContext.PushProperty("SpecialLogType", true)) {
    _logger.LogInformation("MyLogEntry {MyParam}", myParam);
}

I configured two sinks, one for normal logs and one for this special type of log. Using filters, I should now be able to filter logs using this property. But I can't figure out how exactly I need to configure the subloggers in appsettings.json. Right now, my appsettings.json looks like this:

"Serilog": {
    "Using": [ "Serilog.Sinks.File", "Serilog.Settings.Configuration", "Serilog.Expressions" ],
    "MinimumLevel": {
      "Default": "Information",
    },
    "WriteTo": [
      {
        "Name": "Logger",
        "Args": {
          "configureLogger": {
            "Filter": [
              {
                "Name": "ByExcluding",
                "Args": {
                  "expression": "@p['SpecialLogType'] = 'true'"
                }
              }
            ],
            "WriteTo": [
              {
                "Name": "File",
                "Args": {
                  "path": "Logs/NormalTypeLog_.txt",
                  // other configuration
                }
              }
            ]
          }
        }
      },
      {
        "Name": "Logger",
        "Args": {
          "configureLogger": {
            "Filter": [
              {
                "Name": "ByIncludingOnly",
                "Args": {
                  "expression": "@p['SpecialLogType'] = 'true'"
                }
              }
            ],
            "WriteTo": [
              {
                "Name": "File",
                "Args": {
                  "path": "Logs/SpecialTypeLog.json",
                  // other configuration
                }
              }
            ]
          }
        }
      }
    ]
  }

I've tried several different things with some results but I can't get it to work properly and would appreachiate some advice. Does anyone have any tipps?

Edit: Found this question much later which also provides an alternative answer: Serilog and Filters from Filters.Expressions in appsettings.json


Solution

  • So I managed to figure this out a few days later and figured I'd post my complete solution here if anyone with a similar issue stumbles upon it.

    I think I was on the right path but my main issue was my incorrect use of of Serilog's expressions package. I had mixed things I've found in multiple examples together, which didn't end up working. More reading in wikis and nblumhardt's blog posts guided me to my eventual solution.

    Filtering log events to different files via Serilog's appsettings.json configuration

    Logs can be enriched with properties. These properties can be used later in the pipeline to filter logs to different subloggers. Properties can be added to logs via the LogContext class.

    using (LogContext.PushProperty("SpecialLogType", true)) {
        _logger.LogInformation("This is a log entry that will be filtered to a different file");
    }
    

    To actually configure the loggers, you need to create two sub-loggers. The filtering has to happen at the sub-logger level. Each sublogger has to filter it's own events. Filtering at the top-level logger will filter out log events for all it's subloggers. Filtering is done via Serilog's expressions package.

    To only include log event's with my property attached, I did the following

    "Name": "ByIncludingOnly",
    "Args": {
      "expression": "SpecialLogType is not null"
    }
    

    Since I wanted all other events to go to the "standard" file, for my other sublogger I just excluded all log events with the property attached

    "Name": "ByExcluding",
    "Args": {
      "expression": "SpecialLogType is not null"
    }
    

    Full relevant appsettings.json section

      "Serilog": {
        "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Settings.Configuration", "Serilog.Expressions" ],
        "WriteTo": [
          { "Name": "Console" },
          { "Name": "Debug" },
          {
            "Name": "Logger",
            "Args": {
              "configureLogger": {
                "Filter": [
                  {
                    "Name": "ByExcluding",
                    "Args": {
                      "expression": "SpecialLogType is not null"
                    }
                  }
                ],
                "WriteTo": [
                  {
                    "Name": "File",
                    "Args": {
                      "path": "Logs/NormalLogEvents_.txt",
                      // other irrelevant file configuration
                    }
                  }
                ]
              }
            }
          },
          {
            "Name": "Logger",
            "Args": {
              "configureLogger": {
                "Filter": [
                  {
                    "Name": "ByIncludingOnly",
                    "Args": {
                      "expression": "SpecialLogType is not null"
                    }
                  }
                ],
                "WriteTo": [
                  {
                    "Name": "File",
                    "Args": {
                      "path": "Logs/SpecialLoggingType_.log",
                      // other irrelevant file configuration
                    }
                  }
                ]
              }
            }
          }
        ],
        "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
      }
    

    This worked for me and I hope it helps anyone having the same issue.

    Also useful: Serilog settings configuration readme