Log Aggregation¶
Pipeline¶
Alloy connects to the Docker socket, discovers all running containers, and ships their stdout/stderr logs to Loki. No per-container configuration needed — new containers are picked up automatically.
How It Works¶
Alloy (Collector)¶
- Config:
monitoring/alloy/config.alloy - Discovers containers via
/var/run/docker.sock - Extracts labels: Docker Compose project name, service name
- Forwards to Loki at
http://loki:3100/loki/api/v1/push - Internal state stored at
/var/lib/alloy/data/
Loki (Storage & Query)¶
- Config:
monitoring/loki/loki-config.yml - Storage: filesystem-based (chunks in
/loki/chunks/) - Retention: 7 days (compactor deletes after 2h grace period)
- Schema: v13 (TSDB backend), 24-hour index periods
- Single node (no clustering)
Querying Logs¶
In Grafana → Explore → select Loki datasource.
Common LogQL queries¶
# All logs from a specific container
{compose_service="aletheia-prod-web"}
# Error-level logs across all Aletheia containers
{compose_project="aletheia-prod"} |= "ERROR"
# Celery task failures
{compose_service=~"aletheia-.*-celery"} |= "Task" |= "raised"
# Nginx access logs with 5xx status
{compose_service="nginx-proxy"} |~ "HTTP/[12].\" 5[0-9]{2}"
# Slow requests (> 1 second)
{compose_service="aletheia-prod-web"} | json | response_time > 1000
Useful filters¶
| Operator | Purpose | Example |
|---|---|---|
\|= |
Contains string | \|= "ERROR" |
\|~ |
Matches regex | \|~ "5[0-9]{2}" |
!= |
Does not contain | != "healthcheck" |
\| json |
Parse JSON logs | \| json \| level="error" |
Retention & Storage¶
- Logs are kept for 7 days
- Storage is filesystem-based — check disk usage if Loki volume grows unexpectedly
- No log parsing/structuring is applied by Alloy (logs are shipped as-is)
- No remote write — logs are lost if the Loki container is destroyed