Skip to content
Back to Operations & SRE

Log Sampling

Sample high-volume non-error logs to control cost while keeping 100% of errors and warnings.

Why

A service handling 10,000 requests/s that logs every request at INFO produces 600,000 logs/min — that's 30B logs/day, which costs $30k/month in storage and makes queries slow. But you don't need every INFO log; you need all ERRORs (100%, no sampling — an error is rare and important), all WARNs (100%, they're signals of degradation), and a sample of INFOs (1-10%, enough to see traffic patterns and debug specific requests via trace_id). This is tail-based sampling: sample by level, not randomly. The cost drops 90% (you keep 10% of INFOs) while keeping 100% of the logs that matter for incidents. The key rule: never sample errors. If you sample ERRORs at 1%, you'll miss 99% of the errors and the on-call will see a clean dashboard while the service is failing.

When

When log volume exceeds budget or query latency. Start with 100% retention; sample only when cost or speed forces it.

How

1. Never sample ERROR or WARN logs
   - These are rare and important; sampling them defeats the purpose of logging
   - Configure: level=ERROR → keep 100%; level=WARN → keep 100%

2. Sample INFO logs by rate or by trace_id
   - Rate: keep 10% of INFO logs (random sampling)
   - Trace-based: keep 100% of traces that have an ERROR, sample the rest (tail-based)
   - Tail-based is better — it keeps the full trace for failed requests

3. Use probabilistic sampling with a consistent hash
   - Hash the trace_id; keep if hash % 100 < sample_rate
   - This ensures all logs for a kept trace_id are kept (no partial traces)

4. Adjust the sample rate by service
   - High-volume service (checkout): 1% INFO
   - Low-volume service (admin): 100% INFO
   - The admin service's logs are cheap to keep and useful for debugging

5. Monitor the sampling rate and adjust
   - Track "logs kept / logs emitted" per service
   - If a service's error rate rises, temporarily raise its INFO sample rate to 100% for diagnosis

Example

# Sampling config (Loki/Fluentd):
sampling:
  - match: { level: "ERROR" }
    keep: 1.0          # 100% — never sample errors
  - match: { level: "WARN" }
    keep: 1.0          # 100% — never sample warnings
  - match: { level: "INFO", service: "checkout" }
    keep: 0.01         # 1% — high-volume service
    strategy: "trace_hash"  # consistent by trace_id
  - match: { level: "INFO", service: "admin" }
    keep: 1.0          # 100% — low-volume, cheap to keep

# Tail-based sampling (OpenTelemetry Collector):
processors:
  tail_sampling:
    decision_wait: 10s
    policies:
      - name: errors
        type: status_code
        status_code: { status_codes: [ERROR] }  # keep 100% of error traces
      - name: sample-rest
        type: probabilistic
        probabilistic: { sampling_percentage: 1 }  # 1% of the rest

# Result:
# - All ERROR/WARN logs kept (100%) → incidents are fully visible
# - 1% of checkout INFO logs kept → cost drops 90%, traffic patterns still visible
# - 100% of admin INFO logs kept → cheap, useful for debugging
# - Any trace with an ERROR → 100% of that trace's logs kept (tail-based)

Common Problems

  • ⚠Sampling errors — a 1% error sample means 99% of errors are invisible; the on-call sees a clean dashboard while the service fails.
  • ⚠Random sampling without trace awareness — half a trace's logs are kept, so the on-call sees the start but not the failure, and can't reconstruct the request.
  • ⚠100% retention of everything — costs $30k/month and queries are slow; sample the high-volume INFOs and keep 100% of what matters.
  • ⚠Same sample rate for all services — the high-volume checkout service drowns the aggregator while the low-volume admin service is over-sampled.

Best Practices

  • ✓Never sample ERROR or WARN logs (keep 100%); sample only INFO, and use tail-based sampling to keep 100% of traces with errors.
  • ✓Use consistent-hash sampling by trace_id so all logs for a kept trace are kept (no partial traces that confuse the on-call).
  • ✓Adjust sample rate per service: 1% for high-volume (checkout), 100% for low-volume (admin); monitor 'logs kept / emitted' and adjust.
  • ✓Temporarily raise sampling to 100% during incidents or rising error rates; sample for cost, not for signal loss.