DDSA Solutions
Case Study6 min read·

Design a Distributed Logging System

How to design distributed logging for interviews: agents, collectors, Kafka buffers, indexing, retention, query APIs, and alert hooks.

Every microservice wants to printf into the void. A logging platform turns that void into searchable history. Interviewers like this prompt because it is a write-heavy ingest pipeline with cheap retention tiers — cousin to metrics monitoring, but with fatter, less structured payloads.

Scope with the framework: agents on hosts, central collection, index + cold storage, search UI/API. Skip building a full SIEM product; mention security log immutability as a stretch.

Functional requirements

  • Collect logs from thousands of services/hosts.
  • Parse common formats (JSON, syslog); attach service, host, trace_id.
  • Near-real-time search and filter (last 15 minutes hot).
  • Retention: hot days, warm weeks, cold months.
  • Optional: live tail, anomaly alerts, PII redaction.

Non-functional

  • Ingest durability: accepted logs should not vanish on collector restart.
  • Search freshness: seconds to low minutes for indexed fields.
  • Backpressure: when indexers lag, buffer — do not crash app servers.
  • Multi-tenant isolation so one noisy customer cannot starve others.

Logs ≠ metrics ≠ traces

Metrics are aggregates; traces are request graphs; logs are events with text. Share transport ideas (Kafka) but store differently. Do not shove raw logs into a TSDB or Prometheus.

Capacity sketch

Order-of-magnitude: 10K hosts averaging tens of KB/s is already multi‑GB/min; a chatty host at ~500 KB/s makes the math ugly fast (10K × 500 KB/s ≈ 5 GB/s before compression). With ~5–10× compression you still land on terabytes/day. Indexing every field forever is unaffordable — index a curated set (service, level, trace_id) and keep raw blobs for selective scan.

High-level architecture

  1. Agent (Fluent Bit / OpenTelemetry) — tail files or receive via SDK; batch + compress.
  2. Load-balanced collectors — validate, enrich, apply rate limits per tenant.
  3. Kafka (or Pulsar) — durable buffer; partition by tenant/service.
  4. Indexer workers — parse, write hot index (OpenSearch/Elasticsearch).
  5. Object storage — compressed raw segments for cold retention.
  6. Query API — fan out to hot index; fall back to cold scan jobs for old ranges.
  7. UI / alerts — dashboards and threshold notifications.

Ingest path

  1. App writes structured JSON logs (prefer this over free-text soup).
  2. Agent batches (e.g. 1–5 MB or 1–2 s), sends HTTPS to collectors.
  3. Collector acks after Kafka produce with required acks.
  4. Indexer commits offsets only after durable index/blob write (at-least-once → dedupe by event_id if needed).

At-least-once is the honest default. Exactly-once across agent→Kafka→ES is painful; use idempotent event ids for critical audit logs and accept rare duplicates in debug logs.

Storage tiers

TierStoreQuery
Hot (0–7d)Elasticsearch / OpenSearchInteractive search
Warm (7–30d)Fewer replicas / spin-down nodesSlower search
Cold (30d+)S3/GCS compressedAsync recreate or Athena-style scan

Query path

Parse a Lucene-like query: service:checkout AND level:ERROR AND trace_id:X. Time range prunes indices (daily index pattern). Cap result size; paginate. Expensive queries go to an offline cluster or require sampling — protect the hot cluster like you protect an origin behind a CDN.

Scaling and multi-tenancy

  • Kafka partitions and consumer groups scale indexer throughput.
  • Per-tenant quotas on ingest bytes/day and indexed fields.
  • Shard Elasticsearch by time + tenant hash (sharding).
  • Drop or sample DEBUG under pressure; never silently drop ERROR without a metric.

Worked example

  1. Checkout service emits {"level":"ERROR","trace_id":"abc","msg":"payment timeout"}.
  2. Agent batches; collector writes to Kafka topic logs.checkout.
  3. Indexer writes to index logs-2026-07-16 and archives the raw batch to S3.
  4. On-call searches service:checkout level:ERROR; finds the line within seconds; jumps to trace system via trace_id.

Interview narrative

Draw agent → collector → Kafka → indexer → hot/cold stores. Emphasize backpressure and retention economics. Contrast with metrics (numbers) and claim the win condition is “debuggable production,” not “index everything forever.”

More in this series