Exploring Serilog v2 - Configuration
This is the next post in a series re-discovering Serilog v2.
In the introduction to the Serilog Console Sink I mentioned about how you can use a number of ways to configure Serilog. Below is a simple guide on which approach you may like to take when setting up your configuration.
The diagram is a little simple… so you may want to consider what other features are available. To take a closer look, checkout the following GitHub repositories.
Serilog.Settings.Configuration
This is the package to use if you are considering a JSON approach to configuration, for example in ASP.NET Core. Under the hood this package currently uses the Microsoft.Extensions.DependencyModel
& Microsoft.Extensions.Configuration.Abstractions
packages. The library works by infering the configuraion of key method overloads to setup Serilog. This includes:
- Minimum Level of events to be logged
- Enrichment of events
- Filtering of events
- Sink configuration
- Audit Sinks
A Quick Example
Let’s create a quick console app, that logs to both the console and a file sinks whilst using a appsetting.json
file for configuration.
dotnet new console -n example-console
cd example-console
dotnet add package Serilog.Settings.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Enrichers.Process
touch appsettings.json
- Open the
Program.cs
and add the following.
using System;
using System.IO;
using Serilog;
using Serilog.Events;
using Serilog.Core;
using Microsoft.Extensions.Configuration;
namespace example_console
{
class Program
{
static void Main(string[] args)
{
// Read the appsettings file
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
// Apply the config to the logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
Log.Verbose("This is a verbose statement");
Log.Debug("This is a debug statement");
Log.Information("This is a info statement");
Log.Warning("This is a warning statement");
Log.Error(new IndexOutOfRangeException(), "This is an error statement");
Log.Fatal(new AggregateException(), "This is an fatal statement");
}
}
}
- Then open
appsettings.json
and add the following setup.
{
"Serilog": {
"Using": ["Serilog.Sinks.Console"],
"MinimumLevel": "Information",
"Enrich": ["WithProcessId"],
"WriteTo": [
{ "Name": "File", "Args": { "path": "serilog-configuration-sample.txt" } },
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp} [{Level}] {Message} {Exception} {Properties} {NewLine}",
"theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Literate, Serilog.Sinks.Console"
}
}],
"Properties": {
"Application": "Exploring Serilog v2"
}
}
}
A couple of gotchas to remember.
- Make sure your using/namespace declarations are correct, Sinks will not be discovered if there is a typo. e.g.
"Using": ["Serilog.Sinks.Console", "Not A Sink Namespace"]
- Ensure the args you pass to sinks are exposed via the fluent configuration. I have been bitten by slight inconsistencies in how Sinks are configured. If you are interested how some of the internals work, check out ConfigurationReader.
Links
Serilog.Settings.AppSettings
If you are looking for a more traditional XML based approach, then check out Serilog.Settings.AppSettings
. This library is the best option when using an <appsettings>
block and uses a similar approach to the Serilog.Settings.Configuration
package.
A Quick Example
Once again, let’s create a quick console app. Following the previous example this will also log to both the console and a file sinks however this time a example.config
file will be targeted.
dotnet new console -n example-console
cd example-console
dotnet add package System.Configuration.ConfigurationManager
dotnet add package Serilog.Settings.AppSettings
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Enrichers.Process
touch appsettings.config
- Open the
Program.cs
and add the same following code.
using System;
using System.IO;
using Serilog;
using Serilog.Events;
using Serilog.Core;
namespace example_console
{
class Program
{
static void Main(string[] args)
{
var basePath = AppDomain.CurrentDomain.BaseDirectory;
var configFile = Path.GetFullPath(Path.Combine(basePath, "appsettings.config"));
// Apply the config to the logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.AppSettings(filePath: configFile)
.CreateLogger();
Log.Verbose("This is a verbose statement");
Log.Debug("This is a debug statement");
Log.Information("This is a info statement");
Log.Warning("This is a warning statement");
Log.Error(new IndexOutOfRangeException(), "This is an error statement");
Log.Fatal(new AggregateException(), "This is an fatal statement");
}
}
}
- Then open
.csproj
and add the following to ensure the config file is copied.
<ItemGroup>
<None Update="appsettings.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
- Finally open
appsettings.config
and add the following setup. Here we setup the console and file sinks along with a process enricher.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="serilog:using:Console" value="Serilog.Sinks.Console" />
<add key="serilog:write-to:Console"/>
<add key="serilog:write-to:Console.outputTemplate" value="[{Timestamp} [{Level}] {Message} {Exception} {Properties} {NewLine}" />
<add key="serilog:using:File" value="Serilog.Sinks.File" />
<add key="serilog:write-to:File"/>
<add key="serilog:write-to:File.path" value="serilog-configuration-sample.txt" />
<add key="serilog:using:Process" value="Serilog.Enrichers.Process" />
<add key="serilog:enrich:WithProcessId"/>
</appSettings>
</configuration>
Links
The is another new kid on the block I did forget to mention. It is early days, however using the lessons of logs past, this library attempts to unify the configuration of Serilog and onboard users more seemlessly.