4

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

Howard
  • 694
  • 5
  • 21

2 Answers2

5

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
ablaze
  • 722
  • 7
  • 30
  • 1
    I've kept this answer a bit basic. If you're using DI, this post might help https://stackoverflow.com/q/55551654/799593. – ablaze Apr 07 '19 at 19:15
  • Hi @ablaze. Thanks for the info. So it looks like Serilog doesn't support this and you have to amend the values read from the config file? If so, I'd rather create my own config.settings, read them and configure Serilog in code. – Howard Apr 08 '19 at 14:32
  • @Howard yep you can either leverage any of the existing .config or .json setting file from your project template or you can create a separate one and refer it in your primay config - which helps with "separation of concerns" esp. if you've a large project. If my answer solved your query, please consider marking it as an answer for others and optionally upvoting it. Thx! – ablaze Apr 08 '19 at 14:50
  • 3
    But is there a way for Serilog to auto-init based on `appsettings.json` but in `Startup.cs` override certain settings in code ? – BaltoStar Jan 26 '20 at 03:40
3

You can change the configuration after it's read from json and before you use it to create the logger. Assuming the following json:

{
  "Serilog": {
    "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
    "MinimumLevel": {
      "Default": "Debug"
    },
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
        }
      },
      {
        "Name": "File",
        "Args": {
          "path": "placeholder",
          "buffered": false,
          "outputTemplate": "[{Timestamp:HH:mm:ss.fff} {Level:u3}] {Message}{NewLine}{Exception}",
          "rollingInterval": "Day"
        }
      }
    ],
    "Enrich": [ "FromLogContext" ]
  }
}

Use this code to update the "placeholder" in the json file to "abc.log" prior to the call to CreateLogger.

 var configuration = new ConfigurationBuilder()
         .AddJsonFile(AppSettingsPath, 
             optional: false, reloadOnChange: true)
         .Build();

 configuration["Serilog:WriteTo:1:Args:path"] = "abc.log";

 Log.Logger = new LoggerConfiguration()
     .ReadFrom.Configuration(configuration)
     .WriteTo.SerilogSink(MessageBroker.Broker)
     .CreateLogger();
balrob
  • 585
  • 6
  • 7