Structured Logging with Serilog in ASP.NET WEB API
WebAPI
19 Articles
In this article, let's learn about how to do Structured Logging of errors using Serilog in Web API in ASP.NET Core.
Note: If you have not done so already, I recommend you read the article on Global Exception Handling in ASP.NET WEB API.
Table of Contents
- Introduction
- Why Structured Logging ?
- Write to Sink
- Implementing Logging with Serilog
- Enriching Logs with additional information
- Log Scope using Serilog
- Advantages
- Summary
Introduction
Logging is needed in all applications. This will help to find root cause or track user activity in any environment. By default logging is baked into ASP.NET Core. It is very easy to use and logs are stored in plain text format. This will be difficult to read and analyse.
Serilog is a popular structured logging library for .NET applications. It is very easy to use and configure. It has many sinks available to store the logs in different places. It has many enrichers available to enrich the logs with additional information. It also supports scoping logs events to enrich logs for a particular scope.
This is the second thing that I do when I create new Web API Projects or when I work on existing code base. Let's focus on how to implement structured logging in ASP.NET Web API.
Why Structured Logging ?
Traditional plain-text logs can be hard to read and analyze. Structured logging is a practice where you apply the same message format (JSON Structure) to all of your application logs. The end result is that all your logs will have a similar structure, allowing them to be easily searched and analyzed.
Write to Sink
A Sink in Serilog is a destination where your log events are sent. There are sinks for various outputs like the console, files, databases, and even other logging platforms like Seq, which we'll explore in more detail.
The most commonly used Sinks are,
- Console - To log to console
- File - To log to file
- Seq - To log to Seq (useful in docker based development)
- Application Insights - To log to Azure Application Insights
- Database - To log to database
- Log Group - To log to AWS cloud watch log groups
Implementing Logging with Serilog
Let's see how to implement structured logging with Serilog in ASP.NET Web API.
Install the Serilog.AspNetCore Nuget Packages.
The next step is to pass Configuration to and use it to create Serilog Logger. These can be done programmatically or using appsettings.json. I prefer appsettings.json as it gives more flexibility.
Code Sample - App Settings for Serilog in ASP.NET Core
The above configuration will create a Serilog logger with File sink.
The next step is to create log configuration using above configuration and use it to create Serilog logger as shown below.
Code Sample - Use Configuration and Create logger
Finally UseSerilog() in app to replace default Logger in .NET
Code Sample - Use Serilog as default logger
Enriching Logs with additional information
So far so good. But wouldn't it be nice if we can add extra information to our logs?? Serilog enrichers are packages that add properties to log events. Enrichers are a great way to add context to your logs. For example, you can add properties that contain the current user's ID, the current request path, or the current machine name.
Most commonly used enrichers are machine name, process id, thread id, environment, etc. Let's see how to add these enrichers to Serilog.
Install the following Nuget Packages.
- Serilog.Enrichers.Environment - To add / enrich with app environment in logs
- Serilog.Enrichers.Process - To add / enrich with process id in logs
- Serilog.Enrichers.Thread - To add / enrich with thread id in logs
Update the Serilog logger configuration to enrich log events with these properties.
Code Sample - Enriching logs with additional details
From the above code snippet, you can see that we are enriching the log events with MachineName, ProcessId, ThreadId, Environment and ApplicationName.
Log Scope using Serilog
Now what if we want to add some properties to all the logs events in a request in a single place?? Serilog supports logging scopes. Scopes are a way to add properties to log events that are in effect for a limited period of time. Scopes provide a global way to add properties to all log events in a particular request. For example, you can add a scope for a particular request with User ID or a Tenant ID. All log events within that scope will have the User ID or Tenant ID property in log output.
Code Sample - Configuration to enrich log with scopes
To do that we need to enrich the configuration with FromLogContext() as shown above.
Note: If you have not done so already, I recommend you read the article on Introducing Middleware in ASP.NET.
Now we need to create a Middleware to do the actual work of adding the scope. Let's create a middleware called LogScopeMiddleware and use it in request pipeline. This will add the scope to all the log events in a request.
Code Sample - Log Scope Middleware to enrich all logs in a request
That's it now all you need to do is to inject ILogger<T> in your apps and start logging.
Advantages
The advantages of using Serilog are,
- Structured Logging - Simple JSON structure for all log events
- Multiple Sinks - Can write / output to multiple sinks
- Enrichers - Can enrich logs with additional information
- Scopes - Can enrich logs with additional information for a particular scope
- Easy to use - Very easy to use and configure
- Improved Searching and readbility - This is more powerful when linked with Seq or Elastic Search
- Improved Analytics - Because of the structured nature easy to get analytics
Summary
In this article, we learnt about how to do structured logging using SeriLog in ASP.NET Core. We learnt about Sinks and Enrichers. We also learnt about how to use Serilog Scopes to enrich logs for a particular request. We also learnt about the advantages of using Serilog. I hope you enjoyed reading it.