This episode makes your application observable: structured logging with Serilog, metrics, tracing, and OpenTelemetry integration, health checks and monitoring endpoints, and incident management, diagnostics, and production support readiness.

In production, you cannot guess what is happening inside the application — you must see it. Episode 20 covers observability: the ability to understand a system's state from the outside through three pillars — logs, metrics, and traces.
You will strengthen logging with Serilog, integrate OpenTelemetry for tracing and metrics, install health checks, and prepare incident management and diagnostics procedures. Observability is not an add-on — it is your eyes in production.
Serilog writes logs as structured events with named properties — not just strings. Install and configure:
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.ConsoleThen enable it in the host:
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.Enrich.FromLogContext()
.CreateLogger();
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddSerilog();WriteTo.Console() sends logs to the console in a structured format, and Enrich.FromLogContext() adds context such as trace ids to every event. Serilog replaces plain console logging with output that can be parsed and aggregated.
Structured logs are sent to many sinks: console for development, Elasticsearch or Seq for aggregation, and files for archiving. A single log statement with structured properties enables queries like "all errors for user X in 10 minutes" — something impossible with free-form text logs.
OpenTelemetry unifies metrics, traces, and logs with a single cross-language SDK. In .NET:
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocolEnable automatic instrumentation for HTTP, database, and HttpClient:
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddEntityFrameworkCoreInstrumentation())
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddMeter("Microsoft.AspNetCore.Hosting"));WithTracing captures the path of every request from the API to the database, and WithMetrics records metrics such as request counts and durations. Everything is exported to backends like Prometheus, Grafana, or an OTLP collector.
A trace consists of spans that tell the story of one request: the HTTP middleware, the database call, the call to another service. When a user reports slowness, the trace shows in which span the time was lost — no guessing.
Health checks tell the orchestrator and operations team that the application is healthy. Add an endpoint:
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>();
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/ready");AddDbContextCheck verifies the database connection — a real check, not just "the app is still alive". The /health/ready endpoint is used by Kubernetes as a readiness probe (episode 19), and /health/live for liveness.
Metrics without alerts are just pretty graphs. Set alert rules on key metrics: rising error rate, p99 latency above target, or repeatedly failing health checks. A good alert says what is wrong and points to the relevant dashboard.
When an incident happens, follow an agreed flow: detection (alert), severity classification, team communication, diagnosis, mitigation, then post-mortem. The main goal is reducing recovery time — MTTR — not just finding the root cause.
When you need to investigate a running process, the diagnostic tools from episode 15 work in production:
dotnet-dump collect --process-id 12345
dotnet-dump analyze dump.dmpdotnet-dump collect records a snapshot of the process memory, and dotnet-dump analyze examines it interactively — looking for stalled threads, large objects, or deadlocks. Combining dumps with traces and logs gives a complete picture of an incident.
Tip
Structure your logs with structured properties from the start. Changing the logging system after production is running is far more expensive than doing it correctly now.
Key takeaways:
In the next episode 21 we will discuss desktop and cross-platform apps — .NET MAUI for cross-platform mobile and desktop applications, WPF and WinForms for Windows, Blazor WebAssembly and Blazor Server, and packaging, distribution, and deployment of desktop and mobile applications.