Add Error Logging
.NET has built-in structured logging via the ILogger interface
If you want centralized error handling, you can use middleware
This will log any unhandled exception, and return a generic 500 error to the client
In Program.cs, add before app.Run()
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
var exception = context.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>()?.Error;
logger.LogError(exception, "Unhandled exception occurred");
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(new { error = "An unexpected error occurred." });
});
});
Serilog is a powerful structured logging library that allows us to create structured logs and save them to a file
First, we need to install some required Nuget packages
Serilog.AspNetCore
Serilog.Sinks.Console
Serilog.Sinks.File
Update Program.cs to use Serilog. Add the following to the beginning of the file
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("Logs/log.txt", rollingInterval: RollingInterval.Day)
.Enrich.FromLogContext()
.CreateLogger();
Add the following to the builder
builder.Host.UseSerilog();
Then finally, add this to the very end of the file
Log.CloseAndFlush();
This line ensures that if the application crashes, the error will still be logged to the log file
The completed Program.cs file should look like this
using CrudAppAPI.Data;
using CrudAppAPI.Endpoints;
using CrudAppAPI.Services;
using Serilog;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("Logs/log.txt", rollingInterval: RollingInterval.Day)
.Enrich.FromLogContext()
.CreateLogger();
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();
builder.Services.AddOpenApi();
builder.Services.AddScoped<TaskService>();
builder.Services.AddScoped<IDbConnectionFactory, SqlConnectionFactory>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.MapTaskEndpoints();
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
var exception = context.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>()?.Error;
logger.LogError(exception, "Unhandled exception occurred");
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(new { error = "An unexpected error occurred." });
});
});
app.Run();
Log.CloseAndFlush();
app.MapPost("/api/tasks", async (
CreateTaskDto dto,
TaskService service,
IValidator<CreateTaskDto> validator,
ILogger<Program> logger) =>
{
var validationResult = await validator.ValidateAsync(dto);
if (!validationResult.IsValid)
{
logger.LogWarning("Validation failed for CreateTaskDto: {@Errors}", validationResult.Errors);
return Results.BadRequest(validationResult.Errors);
}
await service.AddTaskAsync(dto);
return Results.Ok();
});