Logging in C# — Complete Guide
In this tutorial, you will learn about Logging in C#. We cover key concepts, practical examples, and best practices to help you master this topic.
Hook
Logging is the eyes and ears of your application in production. When something goes wrong at 3 AM, well-structured logs are often the only tool you have to diagnose the issue. C# applications benefit from a mature logging ecosystem built on Microsoft.Extensions.Logging.
Learning Path
graph LR A[Logging] --> B[ILogger] A --> C[Log Levels] B --> D[Providers] B --> E[Structured Logging] D --> F[Serilog NLog] style A fill:#4a90d9,color:#fff style B fill:#4a90d9,color:#fff style C fill:#4a90d9,color:#fff style D fill:#4a90d9,color:#fff style E fill:#4a90d9,color:#fff style F fill:#4a90d9,color:#fff
ILogger Basics
The ILogger<T> interface from Microsoft.Extensions.Logging is the standard logging abstraction in .NET.
using Microsoft.Extensions.Logging;
public class OrderProcessor
{
private readonly ILogger<OrderProcessor> _logger;
public OrderProcessor(ILogger<OrderProcessor> logger)
{
_logger = logger;
}
public void ProcessOrder(int orderId)
{
_logger.LogInformation("Processing order {OrderId}", orderId);
try
{
// Business logic
_logger.LogDebug("Order {OrderId} processed successfully", orderId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to process order {OrderId}", orderId);
throw;
}
}
}
Log Levels
Choose the right level for each message.
_logger.LogTrace("Fine-grained diagnostic information");
_logger.LogDebug("Debugging information for developers");
_logger.LogInformation("General application flow information");
_logger.LogWarning("Unexpected but recoverable situations");
_logger.LogError("Errors that prevent an operation from completing");
_logger.LogCritical("System failures requiring immediate attention");
Use LogLevel enum to configure minimum logging level in configuration.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"MyApp.OrderProcessor": "Debug"
}
}
}
Structured Logging
Always use structured logging with placeholders instead of string concatenation.
// Bad - string interpolation loses structure
_logger.LogInformation($"User {userId} logged in at {DateTime.UtcNow}");
// Good - structured logging preserves fields
_logger.LogInformation("User {UserId} logged in at {LoginTime}", userId, DateTime.UtcNow);
Structured logging allows log aggregation tools (Elasticsearch, Datadog, Seq) to index and search individual fields.
Setting Up Logging
Configure logging in a console application.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var services = new ServiceCollection();
services.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddConsole();
builder.AddDebug();
builder.SetMinimumLevel(LogLevel.Information);
});
services.AddTransient<OrderProcessor>();
var provider = services.BuildServiceProvider();
var processor = provider.GetRequiredService<OrderProcessor>();
processor.ProcessOrder(42);
Serilog
Serilog is the most popular structured logging library for C#.
using Serilog;
using Serilog.Formatting.Json;
// Configure Serilog
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File("logs/app-.log",
rollingInterval: RollingInterval.Day,
formatProvider: new JsonFormatter())
.WriteTo.Seq("http://localhost:5341")
.Enrich.WithProperty("Application", "MyApp")
.Enrich.WithMachineName()
.CreateLogger();
try
{
Log.Information("Application starting up");
var processor = new OrderProcessor();
processor.ProcessOrder(100);
}
catch (Exception ex)
{
Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
NLog
NLog is another powerful logging framework.
using NLog;
// NLog requires a NLog.config or programmatic configuration
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public void DoWork()
{
Logger.Info("Starting work with {Parameter}", "value");
Logger.Error(new Exception("Something broke"), "Error details");
}
Log Enrichment
Add context to every log message automatically.
using var scope = _logger.BeginScope(new Dictionary<string, object>
{
["CorrelationId"] = Guid.NewGuid(),
["UserId"] = currentUser.Id
});
// All logs within this scope include CorrelationId and UserId
_logger.LogInformation("Processing payment");
_logger.LogWarning("Payment amount exceeds threshold");
Common Mistakes
String interpolation in log messages: Always use structured placeholders (
{Field}) instead of$"...". Structured logging preserves field names for searching.Logging sensitive data: Never log passwords, credit card numbers, or personal information. Use data masking or omit sensitive fields.
Not using async logging: Synchronous logging can block your application. Most frameworks support async logging by default.
Over-logging at Information level: Use Debug/Trace for verbose diagnostics and Information for meaningful business events.
Ignoring log levels in production: Configure appropriate minimum log levels per namespace to avoid performance impact from excessive logging.
Practice Questions
Set up a console application with console logging and file logging using Serilog.
Create a custom
ILoggerProviderthat writes logs to a database table.Implement a logging middleware for ASP.NET Core that logs request duration and status code.
Challenge: Build a log aggregation dashboard using SignalR that streams logs to a web client in real time.
FAQ
Mini Project: Request Logging Middleware
Create an ASP.NET Core middleware that logs request information with timing.
using System.Diagnostics;
using Microsoft.Extensions.Logging;
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;
public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var sw = Stopwatch.StartNew();
_logger.LogInformation("HTTP {Method} {Path} started",
context.Request.Method, context.Request.Path);
await _next(context);
sw.Stop();
_logger.LogInformation("HTTP {Method} {Path} completed with {StatusCode} in {Duration}ms",
context.Request.Method, context.Request.Path,
context.Response.StatusCode, sw.ElapsedMilliseconds);
}
}
// In Program.cs
// app.UseMiddleware<RequestLoggingMiddleware>();
Output (sample):
info: RequestLoggingMiddleware[0]
HTTP GET /api/products started
info: RequestLoggingMiddleware[0]
HTTP GET /api/products completed with 200 in 45ms
Effective logging is essential for production C# applications. By using structured logging with .NET's ILogger abstraction and tools like Serilog, you gain deep visibility into your application's behavior at runtime.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro