This episode covers Traefik access logs: enabling them with Common and JSON formats, status code filters and custom fields, centralized log aggregation with Loki and ELK, and debugging techniques ranging from DEBUG log levels and API inspection to dashboard analysis.

Metrics give the big picture, but to trace a single problematic request you need per-request detail. Episode 24 covers access logs: the complete record of every request that passes through Traefik — where it came from, where it went, how long it took, and what status was returned.
The second part of this episode is debugging: techniques for inspecting runtime configuration, the right log level, and reading all the signals Traefik gives you. A good access log combined with systematic debugging techniques is the skill that most often saves you in production incidents.
The access log is enabled in static config. Two main formats are available:
accessLog:
filePath: "/var/log/traefik/access.log"
format: common
bufferingSize: 100format: common: one text line per request, easy for humans to read.format: json: one JSON object per request, easy for machines to parse.bufferingSize: the number of lines buffered before writing — increases throughput but can delay logs.An example Common format line:
127.0.0.1 - - [10/Aug/2026:09:15:22 +0000] "GET /api/users HTTP/1.1" 200 8452 "curl/8.5.0" "-" 12msThe JSON format produces a richer structure:
{
"ClientAddr": "127.0.0.1:51432",
"ClientHost": "127.0.0.1",
"RequestMethod": "GET",
"RequestPath": "/api/users",
"RequestProtocol": "HTTP/1.1",
"RouterName": "api@docker",
"ServiceName": "api-svc@docker",
"StatusCode": 200,
"Duration": 12000000,
"RequestHost": "api.example.com"
}Note the RouterName and ServiceName fields — these are the keys to linking logs with routing configuration.
Large-volume logs are expensive. Filters and field selection help control them:
accessLog:
format: json
filters:
statusCodes:
- "200"
- "5xx"
retryAttempts: true
fields:
defaultMode: drop
names:
ClientAddr: keep
RequestHost: keep
RequestPath: keep
StatusCode: keep
Duration: keep
ServiceName: keepfilters.statusCodes: only log certain statuses — above only 200 and all 5xx.fields.defaultMode: drop: drop all fields except those registered in names.retryAttempts: true: add a field with the number of retry attempts.The result: lean logs containing exactly the data actually used for debugging and alerting.
Access logs in local files are useless if you have dozens of nodes. The standard practice: stream logs to a centralized system.
With the JSON format, ingestion into both systems is simple: JSON logs are sent as-is, and the parsing system reads the fields automatically. From Kibana or Grafana, you can search for all requests from one client, trace consecutive errors, or map latency per service.
When behavior does not match expectations, raise Traefik's log level to DEBUG:
log:
level: DEBUG
filePath: "/var/log/traefik/traefik.log"DEBUG logs show internal details: rule evaluation, load balancing decisions, and the configuration update process. The output is very large — turn it on only while debugging, then return to INFO.
Before changing the log level, start with the runtime API — the answer is usually already there:
curl -s http://localhost:8080/api/http/routers | python3 -m json.tool
curl -s http://localhost:8080/api/http/services | python3 -m json.toolThe /api/http/routers endpoint shows all routers with their rules and statuses; /api/http/services shows services and server lists. If an expected router is missing, the provider did not deliver it — check the labels or resources. The curl command is the first step of Traefik debugging.
A sequence that usually works:
DEBUG logs for failed rule evaluation.curl using the Host header.Tip
Make a habit of giving routers and services unique names — e.g. api@docker or blog@file. These are the names that appear in access logs, the API, and the dashboard. Clear names speed up all future debugging.
Key takeaways:
drop/keep fields control log volume.RouterName and ServiceName fields link logs to configuration.DEBUG logs for internal detail; return to INFO when done.In episode 25 next we will cover distributed tracing — sending request traces across services with Jaeger, Zipkin, and OpenTelemetry, sampling configuration, agent versus collector, and analyzing latency breakdown and error tracking in the tracer UI. With this, a single request can be traced from the browser to the database.