Search code examples
c#serilog

Serilog - Override config values


I am building a Windows 10 app (UWP) and implementing logging using Serilog.

Shown below is an appsettings.json file with which I configure Serilog to write to a Rolling File sink (amongst other sinks).

{
  "Serilog": {
    "Using": [ "Serilog.Sinks.Console" ],
    "MinimumLevel": "Debug",
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "RollingFile",
        "Args": {
          "pathFormat": "#{LogFilePath}log-{Date}.txt",
          "fileSizeLimitBytes": "3000",
          "retainedFileCountLimit": "2"
        }
      }      
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
    "Properties": {
      "Application": "Sample"
    }
  }
}

I then call the following code:

 var configuration = new ConfigurationBuilder()                        
        .AddJsonFile(sampleFile.Path)
        .Build();

    Log.Logger = new LoggerConfiguration()
        .ReadFrom.Configuration(configuration)
        .CreateLogger();

However, I need to be able to change the pathFormat attribute at runtime, so that the log is written to the application’s “LocalState” folder.

Q. Does Serilog support reading from configuration and then overriding particularly arguments at runtime?

My current workaround is to use a token in the JSON called “#{LogFilePath}” and replace this in the file at runtime.

I’ve found the following, but can’t use environment variables in my case: Specify directory for Serilog rolling file path


Solution

  • According to Serilog, you would need to use file logging – as it seems, RollingFile might be going away soon.

    Important note: the rolling functionality in this sink has been improved and merged into the Serilog.Sinks.File package. RollingFile will be maintained for the foreseeable future, however File is recommended for new applications.

    Fixed Format

    Here is a straightforward way of using a File Sink:

    appsettings.json

    {
      "Serilog": {
        "MinimumLevel": "Verbose",
        "WriteTo": [
          {
            "Name": "Console"        
          },
          {
            "Name": "File",
            "Args": {
              "path": "Logs\\log.txt",
              "fileSizeLimitBytes": 3000,
              "buffered": false,
              "rollOnFileSizeLimit": true,
              "retainedFileCountLimit": 3,
              "rollingInterval": "Hour"
            }
          }
        ]
      }
    }
    

    Program.cs

    var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();
    var loggerConfig = new LoggerConfiguration().ReadFrom.Configuration(configuration);
    var logger = loggerConfig.CreateLogger();
    

    Custom Format

    Last year there seemed to be some enthusiasm to specify some default configuration then have it overridden by the config files, in the Serilog team & the community. They created an experimental repository, and a Nuget Package - not sure where that stands today.

    But I think there is a work around – below is one of the ways how you could implement this in a little bit cleaner way, than your "token" approach.

    appsettings.json

    {
      "FileLogger": {
        //"path": "Logs\\log.txt",
      }
    }
    

    This way, if you specify the value in the config file, it would take precedence. Else, your custom format would be used. Having defaults specified in the application and then using configurations to override them (instead the other way around) is a better design in my opinion.

    Program.cs

    var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();
    
    var customLogFileFormat = configuration["FileLogger:path"] ?? $"Logs\\log_{DateTime.Now.ToString("MMddyyyy_hhmmsstt")}log.txt";
    
    var loggerConfig = new LoggerConfiguration()
                    .MinimumLevel.Debug()
                    .WriteTo.Console()
                    .WriteTo.File(
                        path: customLogFileFormat,
                        fileSizeLimitBytes: 3000,
                        buffered: true,
                        rollOnFileSizeLimit: true,
                        rollingInterval: RollingInterval.Day,
                        retainedFileCountLimit: 5);
    

    If you are interested about more details about my test app, following sequence of PoweShell commands might help:

    mkdir SerilogApp
    cd SerilogApp
    dotnet new console -f netcoreapp2.2 -n SerilogApp -o SerilogApp
    dotnet new sln -n SerilogApp.sln
    dotnet sln add .\SerilogApp\SerilogApp.csproj
    dotnet add .\SerilogApp\SerilogApp.csproj package Microsoft.Extensions.Configuration -f netcoreapp2.2
    dotnet add .\SerilogApp\SerilogApp.csproj package Microsoft.Extensions.Configuration.FileExtensions -f netcoreapp2.2
    dotnet add .\SerilogApp\SerilogApp.csproj package Microsoft.Extensions.Configuration.Json -f netcoreapp2.2
    dotnet add .\SerilogApp\SerilogApp.csproj package Serilog -f netcoreapp2.2
    dotnet add .\SerilogApp\SerilogApp.csproj package Serilog.Settings.Configuration -f netcoreapp2.2
    dotnet add .\SerilogApp\SerilogApp.csproj package Serilog.Sinks.Console -f netcoreapp2.2
     dotnet add .\SerilogApp\SerilogApp.csproj package Serilog.Sinks.File -f netcoreapp2.2 -v 4.0.0
    cd .\SerilogApp
    echo $null >> appsettings.json