This repo contains a solution with a number of projects showing how to configure Serilog in them.
Serilog works like most other logging frameworks, with one extra feature that it can serialize objects and display their public properties; private properties and public/private fields are not serialized and written to the log though.
There are a couple things to keep in mind:
- If you want an object to be serialized so that it's public properties are written (rather than
ToString()
being called on the instance itself), prefix the name with@
or$
. - Do not use string interpolation (i.e.
Log.Debug($"The time is {DateTime.Now}."
) or manual string concatenation (i.e.Log.Debug("The time is " + DateTime.Now)
) as it can severely hurt performance. Always use a template string and pass the variables in the params parameter (e.g.Log.Debug("The time is {Now}", DateTime.Now)
)
Here's an example of the various log levels and how you can log primitives and objects.
var structuredData = new StructuredData();
var simpleData = "This is a string.";
Log.Verbose("Here's a Verbose message.");
Log.Debug("Here's a Debug message. Only Public Properties (not fields) are shown on structured data. Structured data: {@sampleData}. Simple data: {simpleData}.", structuredData, simpleData);
Log.Information(new Exception("Exceptions can be put on all log levels"), "Here's an Info message.");
Log.Warning("Here's a Warning message.");
Log.Error(new Exception("This is an exception."), "Here's an Error message.");
Log.Fatal("Here's a Fatal message.");
For more information, see the official docs.
To be able to define the Serilog configuration in a json file, rather than hard-coding it, use:
- Microsoft.Extensions.Configuration.Json (to read from appsettings.json file)
- Serilog.Settings.Configuration (to read from Microsoft.Extensions.Configuration)
Then add NuGet packages for whatever sinks you want to use:
- Serilog.Sinks.Async (use to log to other sinks asynchronously to improve performance)
- Serilog.Sinks.Console (to write to the console)
- Serilog.Sinks.File (to write to a file (supports rolling files))
These NuGet packages may automatically be included by others.
For example, the Serilog.AspNetCore package depends on and will automatically include all of the packages mentioned above, except for Serilog.Sinks.Async
, so you only need to add it.
NOTE: If you forget to add the sinks NuGet packages, no logs will be written to any sinks.
The Enrichers
NuGet packages are optional and only required if you want to automatically enrich all of your log entries with additional data.
- Serilog.Enrichers.AspNetCoreHttpContext (for logging request data)
- Serilog.Enrichers.Context (for Environmental variables)
- Serilog.Enrichers.Environment (for Machine or User name)
- Serilog.Enrichers.Memory (for memory consumption)
- Serilog.Enrichers.Process (for process ID and Name)
- Serilog.Enrichers.Thread (for Thread ID and Name)
- Serilog.Exceptions (exception details)
There are many other Enricher NuGet packages for all sorts of things.
You can use the LogContext
Enricher to attach custom properties to your logs, but it requires code changes to define what you want to attach.
An example might be to include a custom TransactionId property to link all logs from a specific transaction.
There's also packages like Serilog.AspNetCore that can automatically attach web request IDs and other things to your logs.
Note: Not all sinks show enricher properties by default. See the Logging additional details
section below.
It's best practice to configure your logging in a separate file, rather than directly in code, so for all of the samples the configuration is set in the appsettings.json
file.
Note: You must set the file property Copy to Output Directory
to Copy if newer
so that it gets copied to the app's bin directory and can be read by the app.
The Enrich
and Properties
sections are pretty much the same; just the Enrich
ones are out-of-the-box properties provided by the optional NuGet packages.
Some sinks do not display Properties (used by Enrichers) by default, such as the Console
sink, and require you to explicitly define the output template and include Properties
.
e.g. the {Properties:j}
in:
"WriteTo": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception} {Properties:j}{NewLine}"
}
}
],
See the docs for more information on configuring the outputTemplate
.
When using "rollingInterval": "Day"
the date will automatically be appended to the file name.
{
"Serilog": {
"MinimumLevel": "Verbose",
"WriteTo": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:j}{NewLine}{Properties:j}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"restrictedToMinimumLevel": "Warning",
"path": "Logs\\log.txt",
"rollingInterval": "Day",
"fileSizeLimitBytes": 10240,
"rollOnFileSizeLimit": true,
"retainedFileCountLimit": 30
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithExceptionDetails" ],
"Properties": {
"ApplicationName": "SampleApp",
"Environment": "Int"
}
}
}
Logging synchronously can slow down your application, especially logging to the console, so you'll typically want to enable asynchronous logging by using the Async
sink and providing the other sinks in it's Args
, like so:
{
"Serilog": {
"MinimumLevel": "Verbose",
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:j}{NewLine}{Properties:j}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"restrictedToMinimumLevel": "Warning",
"path": "Logs\\log.txt",
"rollingInterval": "Day",
"fileSizeLimitBytes": 10240,
"rollOnFileSizeLimit": true,
"retainedFileCountLimit": 30
}
}
]
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithExceptionDetails" ],
"Properties": {
"ApplicationName": "SampleApp",
"Environment": "Int"
}
}
}
As defined in the config docs, the logging levels are defined as:
- Verbose
- Debug
- Information
- Warning
- Error
- Fatal
NOTE: While the levels logged can vary per sink via the restrictedToMinimumLevel
property, the MinimumLevel
property defines the absolute minimum level logged. So if the MinimumLevel
was set to Warning
, sinks could never log Information
, Debug
, or Verbose
logs.
This one also requires adding the Serilog.AspNetCore
NuGet package.
Using this package will automatically log additional ASP.Net information, such as information about every request that is made.
Files of interest:
- Program.cs shows how to setup the global static Serilog logger and inject it into web host, as well as creating a bootstrap logger for logging messages during initialization.
- Pages\Index.cshtml.cs shows how to bring an instance of the logger into your class via constructor dependency inject, and then use it to write logs.
- appsettings.json shows how to configure Serilog, including specifying different minimum log levels for Microsoft, Microsoft.AspNetCore, and System messages.
Shows how to use native Serilog without any abstractions to log to the console and file.
For best practice you shouldn't reference the Serilog.Log
static class in all of your classes like shown in this example.
A better alternative would be to inject an abstracted log interface instance into the class using dependency injection, or creating a new static class with similar logging methods and have only that class reference Serilog.Log
.
That way you can more easily swap out logging frameworks later if needed.
Files of interest:
- Program.cs shows how to setup the global static Serilog logger in your app.
- ClassThatLogs.cs shows how to use the global static Serilog logger.
- appsettings.json shows how to configure Serilog.
This one also requires adding the Serilog.AspNetCore
NuGet package.
Using this package will automatically log additional ASP.Net information, such as information about every request that is made.
Files of interest:
- Program.cs shows how to setup the global static Serilog logger and inject it into web host.
- Pages\Index.cshtml.cs shows how to bring an instance of the logger into your class via constructor dependency inject, and then use it to write logs.
- appsettings.json shows how to configure Serilog.
This one also required adding the Serilog.Extensions.Logging
, Microsoft.Extensions.Hosting
, and Microsoft.Extensions.DependencyInjection
NuGet packages.
This project uses the Microsoft dependency injection and logging abstractions to inject an ILogger<T>
into the class that will write the logs.
This is a more proper way to make a console app than the ConsoleAppNetCore3
project.
Files of interest:
- Program.cs shows how to set the global static Serilog logger and inject it into a host so that it can be used by dependency injection.
It also shows how to have the host run as a console application, and how you instruct it which class should be ran; in this example it is
TheApp
class. - ClassThatLogs.cs shows how to bring an instance of the logger into your class via constructor dependency injection, and then use it to write logs.
- TheApp.cs shows how to create the "entry point" of your app by having it implement
Microsoft.Extensions.Hosting.IHostedService
, which gets ran by the Host created inProgram.cs
.
This blog provides a lot of good info.