Series: From Code to Cloud: Building a Production-Ready .NET Application
By: Farrukh Rehman - Senior .NET Full Stack Developer / Team Lead
LinkedIn: https://linkedin.com/in/farrukh-rehman
GitHub: https://github.com/farrukh1212cs
Source Code Backend : https://github.com/farrukh1212cs/ECommerce-Backend.git
Source Code Frontend : https://github.com/farrukh1212cs/ECommerce-Frontend.git
🎯 Introduction
Logging is the eyes and ears of your application. When things go wrong in production, a well-structured log file is often the only thing standing between a quick fix and a sleepless night. In this guide, we'll walk through how to implement Serilog in a .NET 8 Web API, configuring it to write structured JSON logs to separate files for Error, Information, and Debug levels.
Why Serilog?
Serilog is a diagnostic logging library for .NET applications. Unlike default logging, Serilog is built from the ground up to support structured data. This means your logs aren't just text strings; they are data objects that can be queried, filtered, and analyzed easily.
Step 1: Install the Essentials
First, we need to add the necessary NuGet packages to our project. These packages allow Serilog to integrate with ASP.NET Core, write to files, and format logs as JSON.
Run the following commands in your project directory:
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Formatting.Compact
- Serilog.AspNetCore: Integrates Serilog with the ASP.NET Core framework.
- Serilog.Sinks.File: Writes log events to files.
- Serilog.Formatting.Compact: Formats logs as compact JSON, saving space and making them machine-readable.
Step 2: Configuration Magic
One of the most powerful features of Serilog is its ability to be configured entirely via appsettings.json. We want to separate our logs into three distinct files based on severity:
- Error-yyyyMMdd.json: Critical issues that need immediate attention.
- Info-yyyyMMdd.json: General application flow and operational events.
- Debug-yyyyMMdd.json: Detailed diagnostic information for development and troubleshooting.
Open your
appsettings.json
and replace the default Logging section with this Serilog configuration:
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "logs/Error-.json",
"restrictedToMinimumLevel": "Error",
"rollingInterval": "Day",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
},
{
"Name": "File",
"Args": {
"path": "logs/Info-.json",
"restrictedToMinimumLevel": "Information",
"rollingInterval": "Day",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
},
{
"Name": "File",
"Args": {
"path": "logs/Debug-.json",
"restrictedToMinimumLevel": "Debug",
"rollingInterval": "Day",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
}
]
}
}
Key Configurations:
- rollingInterval: Sets the log file to roll over every Day, creating files like Error-20231025.json.
- restrictedToMinimumLevel: Ensures that the "Error" file only contains errors (and higher), while "Info" captures information and above.
- formatter: Uses the CompactJsonFormatter to output clean, structured JSON.
Step 3: Wire It Up in Program.cs
Now, let's hook Serilog into the application startup pipeline. We'll configure it to load settings from our JSON configuration and replace the default logger. We also want to wrap our application startup in a try/catch block to capture any fatal errors that prevent the app from starting.
Update Program.cs
using Serilog;
// 1. Initialize the bootstrap logger
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();
try
{
var builder = WebApplication.CreateBuilder(args);
// 2. Add Serilog to the Host
builder.Host.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext());
// ... (Rest of your service registrations) ...
var app = builder.Build();
// ... (Middleware pipeline) ...
app.Run();
}
catch (Exception ex)
{
// 3. Capture fatal startup errors
Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
// 4. Ensure logs are flushed before exit
Log.CloseAndFlush();
}
The Result
Run your application, and you will see a logs folder appear in your project root. Inside, you'll find your structured JSON log files:
logs/Error-20260206.json
logs/Info-20260206.json
logs/Debug-20260206.json
Each line in these files is a valid JSON object, making it incredibly easy to ingest into log management tools like Datadog, Splunk,Seq, or the ELK stack (Elasticsearch, Logstash, Kibana).
Conclusion
By implementing structured logging with Serilog, you've moved beyond simple text files to a robust, queryable logging infrastructure. Configuring separate files for different log levels ensures that critical errors don't get lost in the noise of information logs, helping you maintain a healthy and debuggable application.
Next Lecture Preview
Lecture 13 : Centralized Error Handling & Validation
Using middleware for error handling, implementing FluentValidation, and maintaining consistent API responses.

Top comments (0)