Serilog is a very popular library for .NET when it comes to logging. It will give you a nice, and flexible way to log events in your application. If you are looking for my tutorials/posts about Serilog, they are just below. If you are only checking it out, just continue and read along. 🙌
Tutorials

Key features of Serilog
Below is a list that makes up some key features of Serilog (my personal opinion).
Structured logging
A normal logger is only writing text strings, and that is perfect as that is what logs really are. With Serilog you can do the same, but this time as structured data. This will give you a new level of options when it comes to analyse your logs. Why? Because Serilog can log objects and properties that you can query. 🙌 Wanna see how easy it is?
var user = new { Id = 123, Username = "john_doe", Email = "john@example.com" };
var orderTotal = 450.75m;
// The object properties are captured as structured data
Log.Information("User {@User} placed order for {OrderTotal:C}", user, orderTotal);
// Output includes the full user object structure, not just "User.ToString()"
// OrderTotal is formatted as currencyThe @ operator destructures the object, and :C is a format specifier. Imagine that you can query logs like "show me all orders over $1000 that failed" is incredibly powerful for debugging production issues. 🔥
Sinks
With the many available sinks for Serilog, you can easily make it possible to "ship" your logs to multiple destinations at the same time. This could be:
- The console.
- A single- or rolling file.
- A database.
- A cloud service like:
- Azure Monitor.
- Seq by Datalust.
- etc...
Message templates
A feature that I really like are the templating syntax for separating my message from the data I am feeding to it. Here is an example.
Log.Information("User {Username} logged in from {IpAddress}", username, ipAddress);Minimum level filtering
When developing the application I enjoy having my environment in either verbose or debug as that provides me a lot of logs and I can easily trace what is going on in the application.
With Serilog you can easily configure which log levels are captured at runtime. This can vary from Verbose, Debug, Information, Warning, Error, and up to Fatal.
Easy integration
If your applications are already using the built-in logging and DI (dependency injection), you can easily "enable" Serilog. It can be used in any .NET application and works well with the built-int logging system.
No need for rewriting your whole application, which also makes a great selling point. I am personally using Serilog in console apps, APIs, worker services, and much more.
My favorite things about Serilog
Below is a list with some of the coolest things I think that Serilog provides. If you got any suggestions that I should add, please let me know in the comments! ✌️
A super simple basic setup
All you have to do is install the Serilog package, and add the following code to your Program.cs file.
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/myapp.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Log.Information("Application starting up");Multiple Sinks
In the market for a solution that can ship your logs to multiple places at the same time? This is how you can do it with Serilog.
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("logs/app.txt")
.WriteTo.Seq("http://localhost:5341")
.WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces)
.CreateLogger();Enrichers
If you would like to automatically add context to your logs, this can easily be done using the configuration. Here is an example where I am adding machine name, thread ID, and application name to all logs.
Log.Logger = new LoggerConfiguration()
.Enrich.WithMachineName()
.Enrich.WithThreadId()
.Enrich.WithProperty("Application", "MyApp")
.Enrich.FromLogContext() // Important for correlation IDs
.WriteTo.Console()
.CreateLogger();Log context with scoped properties
You can create a scope and include necessary details within that scope when the logs are created. Here is an example where I make sure that everything logged within this scope will include OrderId.
using Serilog.Context;
public async Task ProcessOrder(int orderId)
{
// All these logs will have OrderId attached
using (LogContext.PushProperty("OrderId", orderId))
{
Log.Information("Starting order processing");
await ValidateOrder();
await ChargePayment();
Log.Information("Order processing complete");
}
}Conditional Logging & Filters
Tired of logs from some providers? With the option for conditional logging and filters in Serilog, we can setup the configuration to suppress noise 😅
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) // Suppress noisy MS logs
.MinimumLevel.Override("System", LogEventLevel.Warning)
.Filter.ByExcluding(logEvent =>
logEvent.Properties.ContainsKey("RequestPath") &&
logEvent.Properties["RequestPath"].ToString().Contains("/health"))
.WriteTo.Console()
.CreateLogger();In the example above, I am suppressing Microsoft logs in the application.
Sub-loggers for Different Components
This is cool! Especially if you got something very specific like a component in your application and you would like to show exactly what class they come from. This is how you can do it.
public class OrderService
{
private readonly ILogger _logger;
public OrderService()
{
// Creates a logger specifically for this class
_logger = Log.ForContext<OrderService>();
// or
_logger = Log.ForContext("SourceContext", "Orders.Processing");
}
public void ProcessOrder()
{
_logger.Information("Processing order");
}
}Logs will show they came from the OrderService.
Exception Logging
It's easy to log your exceptions. Serilog can log our full exceptions with the stack trace 🔥
try
{
// risky operation ;)
ProcessPayment();
}
catch (Exception ex)
{
Log.Error(ex, "Payment processing failed for order {OrderId}", orderId);
throw;
}Configuration from appsettings.json
No need for hardcoding the configuration. I am often working with containers and I really want to have the option to configure the containers from environment variables. This is of course also an option in Serilog, as we can configure it from appsettings.json.
{
"Serilog": {
"Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File"],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": {
"path": "logs/log-.txt",
"rollingInterval": "Day"
}
}
],
"Enrich": ["FromLogContext", "WithMachineName"]
}
}The End
Serilog is my go-to for logging. I think it is one of the most adopted logging frameworks in the .NET ecosystem, and I think it is because of its great performance, flexibility, and many options it provides like sinks and enrichers making it easy to query logs in a structured way.
If you got any suggestions for this page, please let me know in the comments. Happy logging! ✌️
