Logging in TaskHub.Shared is built on Serilog and standardized across every microservice. The goal is structured, queryable logs β every event is a typed record with named properties, not a printf-style string.
service.name, correlation.id, and user.id are first-class properties, not interpolated strings.TraceId/SpanId from OpenTelemetry, so logs and traces are linkable in Grafana.| Module | Purpose |
|---|---|
TaskHub.Observability.Logger |
Serilog bootstrap, ILogService abstraction, request-logging middleware. |
TaskHub.Observability.OpenTelemetry |
Provides the Activity context that the logger enriches against. |
See Logger usage for hands-on registration and examples.
"Serilog": {
"Using": [ "Serilog.Sinks.Grafana.Loki" ],
"MinimumLevel": "Information",
"WriteTo": [
{ "Name": "Console" },
{
"Name": "GrafanaLoki",
"Args": {
"uri": "http://loki:3100",
"labels": [
{ "key": "app", "value": "task-service" },
{ "key": "env", "value": "production" }
]
}
}
]
}
The FullHostBuilder calls AddAppSerilog() automatically β services donβt need to wire Serilog by hand.
| Level | When |
|---|---|
Verbose |
Tight inner loops, payload dumps. Off by default. |
Debug |
Local diagnostic detail. |
Information |
One-per-request summaries, lifecycle events (started, completed). Default minimum. |
Warning |
Recoverable problems β retries fired, fallback used, validation rejected input. |
Error |
A request failed unexpectedly. Pair with an exception. |
Fatal |
The host cannot continue. Reserved for startup failures. |
log.Information("Job {JobId} published", id) β not $"Job {id} published". Templates preserve JobId as a queryable field.{app="task-service"} | json | result.code != 0 | line_format " "
The result.code and result.isSuccess fields come from the Response system β every Result instance auto-tags the active log scope.
For deep configuration (custom enrichers, sink chaining, async wrapping) see Logger usage and Logger architecture.