Logging with Serilog

লেসন ৪৬ / ৫০

কেন Serilog?

Built-in ILogger থাকলেও Serilog structured log লেখে — JSON, Elasticsearch, Seq-এ analyze সহজ।

Setup

dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Sinks.Seq
dotnet add package Serilog.Enrichers.Environment

// Program.cs
using Serilog;

builder.Host.UseSerilog((ctx, lc) => lc
    .ReadFrom.Configuration(ctx.Configuration)
    .Enrich.FromLogContext()
    .Enrich.WithMachineName()
    .Enrich.WithEnvironmentName()
    .WriteTo.Console()
    .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
    .WriteTo.Seq("http://localhost:5341"));

var app = builder.Build();
app.UseSerilogRequestLogging();   // HTTP request log automatic

appsettings.json Configuration

{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "Microsoft.EntityFrameworkCore": "Information",
        "System": "Warning"
      }
    },
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "File",
        "Args": {
          "path": "logs/log-.txt",
          "rollingInterval": "Day",
          "retainedFileCountLimit": 30
        }
      }
    ]
  }
}

Structured Logging ⭐

public class ProductService {
    private readonly ILogger<ProductService> _logger;
    public ProductService(ILogger<ProductService> l) => _logger = l;

    public async Task<Product?> GetAsync(int id) {
        // ❌ Bad — string interpolation
        _logger.LogInformation($"Fetching product {id}");

        // ✅ Good — structured
        _logger.LogInformation("Fetching product {ProductId}", id);

        try {
            var p = await _ctx.Products.FindAsync(id);
            _logger.LogInformation("Found product {ProductId} {Name}", id, p?.Name);
            return p;
        }
        catch (Exception ex) {
            _logger.LogError(ex, "Failed to fetch product {ProductId}", id);
            throw;
        }
    }
}

Log Levels

  • LogTrace — খুব detailed
  • LogDebug — debugging info
  • LogInformation — normal flow
  • LogWarning — unexpected but not error
  • LogError — error হয়েছে কিন্তু app চলছে
  • LogCritical — app crash সম্ভাবনা

Scope (Correlation)

using (_logger.BeginScope("Order {OrderId}", orderId)) {
    _logger.LogInformation("Processing");
    _logger.LogInformation("Charging card");
    // সব log-এ OrderId যুক্ত হবে
}

Seq — Log Server

// Docker
docker run -d -p 5341:80 -e ACCEPT_EULA=Y datalust/seq

// Browser: http://localhost:5341
// Filter: ProductId = 42, Level = Error

Elasticsearch + Kibana

dotnet add package Serilog.Sinks.Elasticsearch

.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200")) {
    IndexFormat = "myapp-logs-{0:yyyy.MM}",
    AutoRegisterTemplate = true
})

Application Insights (Azure)

builder.Services.AddApplicationInsightsTelemetry(
    builder.Configuration["ApplicationInsights:ConnectionString"]);