Logging
Docker logging: stdout / stderr capture, log drivers, rotation, and the patterns for aggregating + searching container logs.
Docker — logging
EXAMPLE
# ===== The model =====
# Containers write to stdout / stderr.
# Docker captures via a LOG DRIVER and forwards or stores.
# ===== Default driver: json-file =====
# Logs stored in /var/lib/docker/containers/<id>/<id>-json.log
# Unbounded by default — configure rotation!
docker run -d --log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
--name api myapp
# Or set defaults in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# ===== View =====
docker logs <container>
docker logs -f <container> # follow
docker logs --tail 100 <container>
docker logs --since 30m <container>
docker logs --timestamps <container>
# Multi-container (compose):
docker compose logs -f
docker compose logs -f api db
# stern for fancier multi-container tail (works for docker too via plugins; Kubernetes-native).
# ===== Other drivers =====
# - none drop logs (last resort)
# - syslog send to local syslog daemon
# - journald send to systemd journal
# - fluentd ship to Fluentd / Fluent Bit
# - awslogs CloudWatch Logs
# - gcplogs Google Cloud Logging
# - splunk Splunk
# - gelf Graylog Extended Log Format
# - local binary, compressed JSON (smaller than json-file)
# Pick driver via --log-driver or daemon.json.
# ===== Structured JSON logs =====
# Inside containers, log structured JSON:
{
"ts": "2024-04-10T03:14:00Z",
"level": "info",
"msg": "request handled",
"user_id": "u-1",
"route": "/api/users"
}
# Shippers parse and index by field; humans grep less.
# ===== Fluent Bit example =====
# Ship docker container logs to Loki:
docker run -d --log-driver fluentd \
--log-opt fluentd-address=fluentbit:24224 \
myapp
# fluentbit.conf:
[INPUT]
Name forward
Listen 0.0.0.0
Port 24224
[OUTPUT]
Name loki
Host loki
Match *
Labels job=docker
# ===== Logs on Kubernetes =====
# Don't configure log drivers in Kubernetes; let kubelet capture stdout/stderr,
# then run Fluent Bit / Vector as a DaemonSet.
# ===== Best practices in app code =====
# - Write to stdout / stderr; not to files inside the container (lost on restart)
# - Structured JSON; one record per line
# - Include trace / request id
# - Log level filter at the source
# ===== Sensitive data =====
# - Never log secrets, tokens, passwords, full request bodies
# - Mask PII at the producer
# - Shorten retention for high-volume logs
# ===== Patterns =====
# - log-opt max-size + max-file (avoid filling disk)
# - JSON logs from every app
# - Ship to a central store (Loki / Elastic / CloudWatch)
# - Distinct logs for ERROR vs INFO; alert on ERROR rate
# ===== Pitfalls =====
# - Unbounded json-file driver -> disk full -> docker down
# - Logging to files inside the container (data loss + no rotation)
# - Plain-text logs that need regex parsing
# - Same log shipper config across vastly different services -> noisy
Why it matters
Docker logging: stdout/stderr -> log driver -> store. Always rotate (max-size + max-file), prefer JSON output, ship to a central store (Loki/Elastic/CloudWatch). On Kubernetes let kubelet capture and Fluent Bit ship. Mask PII at the producer; do not let logs become your secret leak path.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
# Default JSON-file driver; switch to syslog / fluentd / awslogs in prod. docker run --log-driver json-file --log-opt max-size=10m my-apiTry it Yourself »
Discussion
Loading…