Structured Logging & Observability
Logging is the part of a system that nobody designs and everybody relies on at three in the morning. It usually compiles, looks fine in development, and falls over the first time a real incident requires correlating two services across a single request. The reason is almost always the same: ILogger is a façade over a pipeline of opt-in behaviour, and the defaults make it look like more is wired up than actually is.
The articles in this collection are written from the angle of what logs have to prove during an incident or an audit, not what looks pretty in the console. Structured properties are the foundation. A log line is not text — it is a record with typed fields, semantic names, and a level that survives transport to Application Insights, Loki, or whatever sink is downstream. The difference between LogInformation($"User {id} did X") and a message template with {UserId} is the difference between a string you can grep and a property you can group, alert on, and join with telemetry.
BeginScope, correlation IDs through Activity.Current, request enrichment middleware, and LoggerMessage source-generated callsites are where the practical work lives. They are also where the silent failures lurk: scopes that do nothing because the provider does not flow them, allocations from interpolated debug messages on filtered hot paths, exception chains that lose their stack because somebody logged ex.Message instead of ex. Each one compiles, ships, and degrades observability without raising an error.
Then there is the half-forgotten side of audit logging. ISO/IEC 27001 Control A.8.15 is explicit that events must be produced, retained, and reviewed — and most production logs cannot answer “who authenticated as whom from where over the last six months” because the schema was never designed to. The articles here cover how to make logs forensic-grade without turning every request into a 50 MB blob nobody reads.

Six Ways ILogger Silently Fails in Production

Who Ran That Migration?
