traefik/traefik
Observability
Logging, metrics, and tracing in Traefik are three connected systems that share one principle: every observability concern is implemented as a middleware that automatically wraps user-defined chains.
Three flavors
| Concern | Static config | Implementation packages |
|---|---|---|
| Logs (server + access) | Log, AccessLog |
pkg/observability/logs, pkg/middlewares/accesslog |
| Metrics | Metrics |
pkg/observability/metrics, pkg/middlewares/metrics |
| Tracing | Tracing |
pkg/observability/tracing, pkg/middlewares/observability |
Server logs
pkg/observability/logs/logs.go configures the global zerolog logger. It is the only place that mutates log.Logger. All other code uses contextual loggers via log.Ctx(ctx).
Two log streams are emitted:
- Traefik logs — internal server logs (configuration apply, provider errors, lifecycle).
- Access logs — one line per HTTP request, by the
accesslogmiddleware.
The Log static-config block controls level (DEBUG, INFO, WARN, ERROR), format (common or json), and output destination (stdout, stderr, or a file path with optional rotation via lumberjack-style settings).
The AccessLog block controls similar parameters for the access log stream.
Access log middleware
pkg/middlewares/accesslog is a stateful middleware. It captures:
- Request method, path, host, query, protocol.
- Response status, content length.
- Latency (time to first byte and total).
- Source IP (after PROXY-protocol and
X-Forwarded-*resolution). - Selected request and response headers, configurable per field.
- The router and service names that handled the request.
Output formats:
- CLF (Common Log Format): Apache-style
host - user [time] "method url proto" status bytes. - JSON: structured, friendly to log shippers.
The middleware writes asynchronously through a buffered channel; pkg/middlewares/accesslog/log_handler.go runs the writer goroutine.
Capture middleware
pkg/middlewares/capture is a special middleware injected by pkg/server/middleware/observability.go. It records bytes in/out and elapsed time and stashes them in the request context so downstream middlewares (access log, metrics, tracing) can read them without re-instrumenting.
Metrics
pkg/observability/metrics/metrics.go defines a typed metrics interface:
type Registry interface {
IsEpEnabled() bool
IsRouterEnabled() bool
IsServiceEnabled() bool
EntryPointReqsCounter() metrics.Counter
EntryPointReqsTLSCounter() metrics.Counter
EntryPointReqDurationHistogram() metrics.Histogram
EntryPointOpenConnsGauge() metrics.Gauge
RouterReqsCounter() metrics.Counter
// ...
ServiceReqsCounter() metrics.Counter
ServiceReqDurationHistogram() metrics.Histogram
ServiceServerUpGauge() metrics.Gauge
// ...
}The interface lets multiple registries coexist. Backends:
| File | Backend |
|---|---|
pkg/observability/metrics/prometheus.go |
Prometheus pull-style. |
pkg/observability/metrics/otel.go |
OpenTelemetry OTLP push. |
pkg/observability/metrics/datadog.go |
DogStatsD UDP. |
pkg/observability/metrics/statsd.go |
StatsD UDP. |
pkg/observability/metrics/influxdb2.go |
InfluxDB v2 line protocol. |
Each backend has its own Stop*() function, all called by stopMetricsClients() in pkg/server/server.go during shutdown.
The pkg/middlewares/metrics middleware reads the captured per-request data and increments counters / observes histograms. The metrics middleware lives in the chain produced by pkg/server/middleware/observability.go.
What's measured
- Per entry point: requests, requests TLS, request duration, open connections, sent/received bytes.
- Per router: requests, request duration, open connections.
- Per service: requests, request duration, open connections, retries, server up/down (one gauge per upstream).
- TLS specifics: certificate not after, certificate not after by domain.
- Configuration reload counters and timestamps.
The full surface is the Registry interface; metrics_test.go enumerates expected counters per backend.
Tracing
pkg/observability/tracing/tracing.go builds an OpenTelemetry tracer:
func NewTracer(ctx, conf *otypes.Tracing) (trace.Tracer, io.Closer, error)Configuration options accepted:
- OTLP exporter via gRPC or HTTP.
- Sampling rate.
- Resource attributes (service name, environment).
- Optional capture of headers, query, and body — be careful with PII.
The tracer is then handed to pkg/middlewares/observability/tracing.go, which:
- Starts a span for each incoming request, sets the standard HTTP server attributes.
- Propagates the W3C
traceparentheader on the upstream request. - Records middleware-level events as span events when the middleware exposes
GetTracingInformation().
Tracing in middlewares and the proxy
Each middleware that wants to add a span calls tracing.StartSpan(req.Context(), m.name, ...). The proxy (pkg/proxy/fast or pkg/proxy/httputil) instruments the upstream request automatically via the round-tripper.
Health and ping
Two endpoints round out the observability surface:
GET /ping— liveness, served bypkg/ping. ReturnsOKwhile the process is healthy.GET /metrics— metrics, served by Prometheus when enabled.
The traefik healthcheck subcommand (cmd/healthcheck) calls /ping so Docker's HEALTHCHECK can do the same with the Traefik binary itself.
Wiring summary
graph TD
StaticConf[static.Configuration] --> Logs
StaticConf --> Metrics
StaticConf --> Tracing
Logs[pkg/observability/logs] --> Logger[zerolog logger]
Metrics --> Registry[*metrics.MultiRegistry]
Tracing --> Tracer[OTel Tracer]
Logger --> Mids[middleware chain]
Registry --> Mids
Tracer --> Mids
Mids -->|Capture middleware| Request[Request handling]
Mids -->|AccessLog| AccessOut[(access log file)]
Mids -->|Metrics| Backend["Prometheus / OTLP / Datadog / StatsD / InfluxDB"]
Mids -->|Tracing| OTLP[(OTLP collector)]Entry points for modification
- Add a new metrics backend: implement
Registryinpkg/observability/metrics/<backend>.go, register it inpkg/observability/metrics/metrics.go, and add aStop<Backend>()function called fromstopMetricsClients(). - Add a new field to access logs: extend the access-log entry struct in
pkg/middlewares/accesslog/, update the encoder for both CLF and JSON. - Add tracing for a new middleware: implement
GetTracingInformation()returning the middleware name and type. The observability wrapper does the rest.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.