Mastering Serilog in ASP.NET Core: A Developer’s Guide to Modern Logging
A Practical Guide to Implementing Structured Logging with Serilog in ASP.NET Core
In modern software development, effective logging isn’t just a good-to-have feature—it’s a necessity. Whether you're debugging tricky production bugs or monitoring performance bottlenecks, a well-integrated logging framework is your best ally.
In this article, we’ll break down how logging works, introduce Serilog—one of the most powerful structured logging libraries in the .NET ecosystem—and walk through how to integrate it cleanly into an ASP.NET Core application.
🔍 What Is Logging?
Logging is the act of recording diagnostic and operational data generated by an application. These logs can be written to various targets like console output, log files, databases, or even centralized log management systems.
Why is logging essential?
Debugging & Troubleshooting: Logs help trace back what happened before a bug occurred or a system failure took place.
Auditing & Compliance: Keeping a record of user actions and system events can be essential for security audits.
Performance Tracking: Logs can reveal response times, load times, and other metrics critical for tuning application performance.
📊 Log Levels in .NET
To manage the verbosity and importance of logs, we categorize them using log levels. Here are the standard ones in .NET Core:
Trace
– The most granular level, used for very low-level diagnostics.Debug
– Useful for developers when debugging logic.Information
– Tracks general application flow and successful operations.Warning
– Flags potential issues that aren’t necessarily errors (yet).Error
– Captures failures that prevent normal operations.Critical
– For severe issues that cause complete application failure.None
– Disables logging entirely.
Understanding log levels allows you to fine-tune what gets logged in development vs production environments.
⚙️ Integrating Serilog in ASP.NET Core
Serilog is a structured logging library that goes beyond plain text logs. It enables logs to be emitted in a machine-readable format like JSON, which is invaluable when working with log aggregators such as ELK (Elasticsearch, Logstash, Kibana) or Seq.
🔧 Step 1: Install Required Packages
To get started, add the following NuGet packages:
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Enrichers.Environment
These packages integrate Serilog with the ASP.NET Core pipeline and enable file-based logging.
🛠 Step 2: Configure Serilog in Program.cs
Update your Program.cs
file (or wherever your application bootstraps) to initialize Serilog:
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.CreateLogger();
builder.Logging.ClearProviders(); // Optional: Remove default logging
builder.Logging.AddSerilog(logger);
This setup tells ASP.NET Core to use Serilog as its logging provider and read configurations (like file paths and log levels) from appsettings.json
.
📝 Step 3: Serilog configuration in the app settings file.
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {RequestId} {SourceContext} {EnvironmentName}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "logs/log-.txt",
"rollingInterval": "Day",
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {RequestId} {SourceContext} {EnvironmentName}{NewLine}{Exception}"
}
}
]
}
}
📝 Step 4: Use ILogger in Controllers or Services
Serilog works with the standard ILogger<T>
abstraction, so you don’t need to use any special API in your code:
[ApiController]
[Route("api/[controller]")]
public class SampleController : ControllerBase
{
private readonly ILogger<SampleController> _logger;
public SampleController(ILogger<SampleController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
_logger.LogInformation("GET endpoint was called.");
return Ok("Request succeeded.");
}
}
With this approach, Serilog quietly handles all the heavy lifting behind the scenes.
🧾 Sample Log Output
If you're using a plain text sink, the output might look like this:
[18:38:05 INF] GET endpoint was called. 0HNEC8LP75O19:00000007 SerilogDemo.Controllers.SampleController Development
🧾 Log File
🚀 Benefits of Using Serilog
Serilog isn’t just another logging library—it’s purpose-built for structured, modern logging. Here’s why it stands out:
✅ Structured Logging
Instead of plain strings, you can log structured data like:
_logger.LogInformation("User {UserId} placed an order for {Product}", userId, productName);
This allows your log analysis tools to query logs by fields (e.g., all logs for a specific UserId
).
🔍 Logging HTTP Requests Automatically
One powerful feature of Serilog in ASP.NET Core is the ability to automatically log HTTP request information, such as:
HTTP method (GET, POST, etc.)
Request path and response status code
Execution time
Correlation ID (
RequestId
)
To enable this, simply add the following middleware in your application's pipeline:
app.UseSerilogRequestLogging();
Place it typically after UseRouting()
and before UseEndpoints()
in your Program.cs
or Startup.cs
:
app.UseRouting();
app.UseSerilogRequestLogging(); // Logs incoming HTTP requests with status and duration
This middleware hooks into the request lifecycle and automatically emits structured logs for each incoming request. Here’s an example of the output:
[18:44:05 INF] HTTP GET /api/Sample responded 200 in 147.6759 ms 0HNEC8P4P5CGJ:00000007 Serilog.AspNetCore.RequestLoggingMiddleware Development
This single line gives you:
The HTTP method (
GET
)The request path (
/api/sample
)The status code (
200
)How long the request took (
34 ms
)
🎨 Flexible Formatting
Serilog lets you control how logs are output whether it's compact JSON, simple text, or colored console logs. You can fine-tune the output to suit different environments.
🔌 Extensible Sinks
Serilog supports a vast ecosystem of sinks—plugins that send log data to various destinations like:
Files
Databases
Seq
Azure Application Insights
Elasticsearch
You can add multiple sinks at once depending on your need.
🧠 Context Enrichers
You can enrich logs with contextual data like request IDs, user info, or environment name automatically.
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithEnvironmentName()
⚡ Asynchronous Logging
Heavy logging can block the main thread. Serilog supports non-blocking, asynchronous logging to avoid performance hits.
🔧 Configuration Flexibility
Serilog configurations can be handled via code or external files (appsettings.json
), enabling dynamic control over log levels, sinks, and more.
📚 Wrapping Up
Logging is your window into the internal workings of your app. With Serilog, .NET developers get a robust, extensible, and developer-friendly logging solution out of the box.
Start with simple file logging and grow into more advanced use cases like centralized logging, log filtering, and custom enrichers.
🧭 Next Steps:
Explore Serilog’s official documentation
Add console or cloud-based sinks
Integrate with ELK or Seq for better log visualization
✅ TL;DR: Serilog offers structured, performant, and extensible logging for ASP.NET Core developers. It integrates seamlessly with the logging system and brings serious improvements in observability and diagnostics.
📁 GitHub Example
👉 Full working code available at:
🔗 https://github.com/KanaiyaKatarmal/SerilogDemo
I hope you found this guide helpful and informative.
Thanks for reading!
If you enjoyed this article, feel free to share it and follow me for more practical, developer-friendly content like this.