Lesson 26 of 40
DevOps
Intermediate
30 min
Health Checks & Observability
Implement health endpoints, liveness/readiness probes, structured logging with Serilog, and distributed tracing for production observability.
Part 1: ASP.NET Core Health Checks
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>()
.AddRedis(redisConnectionString)
.AddUrlGroup(new Uri("https://api.external.com/ping"), "external-api");
app.MapHealthChecks("/healthz");
app.MapHealthChecks("/ready");
.AddDbContextCheck<AppDbContext>()
.AddRedis(redisConnectionString)
.AddUrlGroup(new Uri("https://api.external.com/ping"), "external-api");
app.MapHealthChecks("/healthz");
app.MapHealthChecks("/ready");
Part 2: Structured Logging with Serilog
builder.Host.UseSerilog((ctx, cfg) => cfg
.ReadFrom.Configuration(ctx.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console(LogEventLevel.Debug)
.WriteTo.ApplicationInsights(telemetryClient, TelemetryConverter.Traces));
.ReadFrom.Configuration(ctx.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console(LogEventLevel.Debug)
.WriteTo.ApplicationInsights(telemetryClient, TelemetryConverter.Traces));
Part 3: Log Scopes for Correlation
using _logger.BeginScope(new Dictionary<string, object>
{
["OrderId"] = orderId,
["UserId"] = userId
})
{
// All logs inside scope include OrderId and UserId
_logger.LogInformation("Processing order");
}
{
["OrderId"] = orderId,
["UserId"] = userId
})
{
// All logs inside scope include OrderId and UserId
_logger.LogInformation("Processing order");
}
Part 4: Application Insights Integration
builder.Services.AddApplicationInsightsTelemetry();
// Custom events
_telemetry.TrackEvent("OrderPlaced", new Dictionary<string, string>
{
["OrderId"] = order.Id.ToString(),
["Value"] = order.Total.ToString()
});
// Custom events
_telemetry.TrackEvent("OrderPlaced", new Dictionary<string, string>
{
["OrderId"] = order.Id.ToString(),
["Value"] = order.Total.ToString()
});