Hey there, fellow code warriors! π Today, we're diving deep into proper logging in ASP.NET Core. If you've ever found yourself lost in a sea of mysterious bugs or scratching your head over your application's behaviour, this guide is your lighthouse! ποΈ
Why Logging Matters π€
Before we jump into the code, let's talk about why logging is the unsung hero of software development. Proper logging is like giving your future self (and your team) a roadmap to understand what's happening in your application. It's not just about catching errors; it's about gaining insights, improving performance, and making your development life easier.
Here's why logging is crucial for your projects:
- Application Behavior Monitoring: It's like having a 24/7 surveillance camera on your code.
- Event Capturing: Every important moment in your app's life, documented.
- Performance Insights: Because sometimes, your app needs a health check.
But logging isn't just about writing stuff down. It's part of a trio of concepts that make your applications robust, maintainable, and scalable:
The Holy Trinity of Application Observability π οΈ
-
Logging: Your app's diary π
- Records events, errors, and important information
- Helps debug issues quickly
- Provides insights into application behaviour
-
Traceability: Following the breadcrumbs of your code π
- Tracks the flow of application processes
- Follows the lifecycle of requests or commands
- Especially helpful for distributed systems or microservices
- Facilitates debugging and enhances reliability
-
Metrics: Measuring what matters π
- Provides measurable data about application performance
- Helps make data-driven decisions
- Enhances user experiences through targeted optimizations
Implementing Logging with Serilog in ASP.NET Core π οΈ
Let's roll up our sleeves and get our hands dirty with some code! We'll be using Serilog, a powerful logging library for .NET.
Setting Up Serilog
First, add the Serilog.AspNetCore NuGet package to your project. If you're planning to use Azure Application Insights, grab Serilog.Sinks.AzureApp too.
In your Program.cs
, set up Serilog like this:
builder.Logging.ClearProviders();
builder.Host.UseSerilog(((context, configuration) =>
configuration.ReadFrom.Configuration(context.Configuration)));
This code clears any existing logging providers and tells Serilog to read its configuration from the appsettings.json
file.
Configuring Serilog
In your appsettings.json
, you can configure Serilog like a boss:
{
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/log-.txt",
"rollingInterval": "Day",
"rollOnFileSizeLimit": true,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
"fileSizeLimitBytes": 1073741824
}
},
{
"Name": "AzureApp",
"Args": {
"instrumentationKey": "your_instrumentation_key"
}
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithThreadId"
]
}
}
This configuration sets up Serilog to:
- Use both console and file sinks
- Set default log levels (Information for production, Debug for development)
- Write logs to the console and a file
- Send logs to Azure Application Insights
Logging in Action: GlobalExceptionHandler π
Our GlobalExceptionHandler
class is like a superhero for exceptions. It catches them, logs them, and sends back a nicely formatted response to the user.
public async ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
{
// ... (exception handling logic)
_logger.LogError(exception, "An error occurred while processing the request {DateTime} {Path}",
DateTime.UtcNow, httpContext.Request.Path);
// ... (return problem details)
}
This class inherits from IExceptionHandler
and is registered in our dependency injection setup. It formats errors based on their type and logs them in a structured way before returning a user-friendly response.
Request Logging with LoggingBehaviour π΅οΈ
The LoggingBehaviour
class is our secret agent, tracking every request that passes through our application.
public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken)
{
Stopwatch stopWatch = Stopwatch.StartNew();
logger.LogInformation("Handling request {Name}, {DateTime} ", request.GetType().Name, DateTime.UtcNow);
// ... (request handling)
if (stopWatch.ElapsedMilliseconds > SlowRequestThresholdMilliseconds)
{
logger.LogWarning("Request took too long to handle {Name}, {DateTime}, {ElapsedMilliseconds}ms",
request.GetType().Name, DateTime.UtcNow, stopWatch.ElapsedMilliseconds);
}
if (result.IsSuccess)
{
logger.LogInformation("Request handled successfully {Name}, {DateTime}", request.GetType().Name, DateTime.UtcNow);
}
else
{
logger.LogError("Request failed to handle {Name}, {DateTime}, {@Error}", request.GetType().Name, DateTime.UtcNow, result.Errors);
}
// ... (log success or failure)
}
This class implements IPipelineBehavior
from the MediatR library, acting as a pipeline for each request. It measures request duration, logs warnings for slow requests, and records the outcome of each request.
Pro Tips for Logging Like a Boss π
- Use Structured Logging: Always include relevant context in your logs. Serilog makes this easy with templates.
- Log Levels Matter: Use the right log level for the right situation. Not everything is an error!
- Performance Matters: Logging can impact performance, so use it wisely.
- Sensitive Data Alert: Be careful not to log sensitive information. Your logs shouldn't be a security risk!
-
Use the "@" Symbol: When logging objects with Serilog, use the "@" symbol (e.g.,
{@Error}
) to serialize the entire object. This makes querying logs easier, especially with tools like Seq.
Wrapping Up π
Proper logging is an essential skill for any developer working with ASP.NET Core. It's not just about catching errors; it's about gaining insights, improving performance, and making your development life easier.
Remember, your logs are your best friend when things go wrong (and even when they don't). They're your application's black box, recording every important event and helping you understand what's happening under the hood.
So, the next time you're debugging a tricky issue or trying to understand why your app is behaving weirdly, remember: that your logs are there to help. Happy logging, developers! π
The source code is available in my Github repo.
Top comments (0)