A logging and metrics platform that cannot take production down with it
The platform you use to debug an incident receives its heaviest load during that incident. Designing it means deciding, in advance, what it drops.
The brief
Every service ships logs and metrics to a central platform where engineers search logs, graph metrics and alert on them.
The platform serves a few hundred services, and its traffic rises sharply whenever something is going wrong.
Requirements
Functional
- Collect logs from every service and search them by time, service and fields
- Collect metrics and graph them over time
- Alert on metric thresholds and rates
- Keep recent data fast and older data cheap
Non-functional
- A slow or unavailable platform must never slow or stop the services that send to it
- Metrics and alerting must keep working when log ingestion is overloaded
- Search over the last hour returns in seconds during an incident
Back-of-envelope
Assume
- 300 services, 3,000 instances
- Each instance logs 100 lines a second on a normal day, 1,000 during an incident; 500 bytes a line
- Each instance exposes 1,000 time series, scraped every 15 seconds
- Logs kept searchable 7 days, archived 90; metrics kept 13 months
Therefore
- Normal log ingest: 3,000 × 100 × 500 B = 150 MB/s ≈ 13 TB a day raw. Seven days searchable, with indexing overhead and replication, is on the order of 150–200 TB of hot storage.
- During an incident, error logging across affected services can rise tenfold: 1.5 GB/s. The platform's peak load and the moment you most need it are the same moment.
- Metrics: 3,000 × 1,000 = 3 million active series, 200,000 samples a second. Metrics volume is set by the number of series, not by traffic — it stays flat during the incident that multiplies logs.
- One user-ID label on one latency histogram with 10 buckets, in one service with 100,000 users: a million new series from a single line of code — a third of the platform's entire metric footprint.
The second and third lines are why logs and metrics are separate pipelines. The fourth is why the metrics pipeline needs its own guard, since its failure mode is a code change rather than a traffic change.
The interface
What is stored
one index per day · fields: timestamp · service · instance · level · trace_id · message · selected structured fieldsDaily indexes make retention a matter of dropping a whole index rather than deleting documents. Only fields people filter on are indexed; the rest are stored but not indexed, which is most of the size saving.
compressed files in object storage by service/date/hourNinety days of logs nobody searches daily. Restoring an hour into the hot tier for an investigation is slow and rare, and priced accordingly.
series = metric name + label set → (timestamp, value) samples, downsampled after 15 daysEach distinct label combination is a series with its own storage and index entry, which is why label cardinality — not sample rate — decides the cost.
The design
The decisions
Each of these could go the other way. The choice, the reason, and what it costs — a design that lists only what it chose teaches the choice; one that lists what it gave up teaches the judgement.
What breaks first
In order. Each names what you would actually observe, and each fix carries its cost.
When something fails
Scaling it
Each step is triggered by a number, not a feeling — and carries what it costs.
What gets probed
The design is the easy half. These are where the conversation goes, and each has a defensible answer above.
- The logging platform becomes slow. Show why no service's request latency changes.
- An incident multiplies log volume by ten. What is dropped first, and how would an engineer know something was dropped?
- Why is a user ID an acceptable log field and a dangerous metric label?
- The monitoring system itself fails. What do your alerts do?