Skip to lesson
‹

Courses / System Design Interview / Chapter 19

Module 2 · Building Blocks · Chapter 19 of 39

Distributed Logging

16 sections · ~26 min✓ Fact-checked

Builds on Monitoring: Metrics, Logs, Traces and Alerts, Message Queues, Blob Stores and Distributed Search. Next up: Distributed Task Schedulers.

Part 1 · Why Logs Need One Home

Forty services, one broken order

It is 8:30 pm on a Friday, the IPL final is halfway through its first innings, and Bengaluru is ordering dinner. A food delivery app starts failing one order in twenty with "payment could not be completed". Each order passes through five of the app's forty services, each running as dozens of copies, and every copy writes its own log file on its own machine. The on-call engineer has three terminals open and is searching files one machine at a time, while autoscaling has already replaced two of the failing machines, taking their logs with them. Central logging turns that hunt into one search.

Logs are timestamped lines describing what a program did. Centralised logging ships every line, soon after it is written, to one place where it is kept for a set time and can be searched in seconds. It is one of the three views from the monitoring chapter: metrics tell you something is wrong, traces show where the time went, and logs record the details of what happened. This chapter builds that pipeline for the food delivery app.

Many people read logs, each for a different reason.

Who reads logsFor what
On-call engineersFixing an incident in the next ten minutes
DevelopersUnderstanding a bug report from last Tuesday
Security teamsFinding who accessed what, and when
Support staffChecking whether a refund or a login really happened

Each reader needs a different slice, from the last ten minutes to last year, and that spread is what makes retention a design question.

What it must do, and how much data

Pin down the requirements before drawing any boxes, then estimate the volume, because for logging the volume decides most of the design.

  • Never block the app: logging must not slow requests, and a log system outage must not take services down.
  • Lose little: keep every error line, even while the log store is down.
  • Search fast: find all lines for one request or one user, across all services, within seconds.
  • Stay fresh: a line written now should be searchable within roughly 10 to 30 seconds.
  • Stay affordable: recent logs fast, old logs cheap, and useless logs deleted.

The volume follows from the fleet: forty services at about fifty copies each.

QuantityValueHow
Service instances2,00040 services, about 50 copies each
Lines per instance50 per secondan average; far higher during incidents
Lines per second100,0002,000 × 50
Bytes per line500 Ba structured line with a dozen fields
Ingest rate50 MB/s100,000 × 500 B
Per day≈ 4.3 TB50 MB/s × 86,400 s
30 days, uncompressed≈ 130 TBbefore indexes and replicas

That can be more data than the app's own business database. Log volume grows with traffic and with every line a developer adds, so cost and retention are design questions from day one, not afterthoughts.

Part 2 · Logs Worth Searching

Structured logs

Volume is one problem; finding the right line is the other. A plain text line is easy to write and hard to search. A structured log is a line made of named fields, usually one JSON object per line, so the store can filter on level or duration_ms exactly instead of guessing with text patterns.

text
Plain text:
2026-10-16 20:31:07 ERROR payment failed for order 88231 after 3021ms

Structured, one JSON object per line:
{"ts":"2026-10-16T15:01:07.412Z", "level":"ERROR", "service":"payments",
 "version":"4.12.0", "host":"pay-7f9c", "request_id":"r-5f2a9c",
 "order_id":88231, "event":"charge_failed", "reason":"gateway_timeout",
 "duration_ms":3021}

The same event. The structured line stores time in UTC (20:31 IST is 15:01 UTC) and can be filtered on any field.

A few fields earn their place in every line:

FieldWhy it earns its place
tsUTC time with milliseconds, so lines from different machines sort together
levelFilter to errors instantly; drop debug lines cheaply
service, version, hostWhich code on which machine wrote it; a bad release shows up as one version
request_idTies together every line from one user action, across services
eventA fixed name like charge_failed that is easy to count and alert on
duration_ms, statusNumbers you can sort, chart and compare
Agree on field names

If one team logs requestId, another req_id and a third trace, cross-service search breaks. Put the common fields in a shared logging library so every service gets them without thinking.

One request id across every service

Of those fields, one ties the services together. A request id (also called a correlation id) is a unique id created when a request first enters the system, usually at the gateway. Every service writes it into each log line and passes it on in a header, such as X-Request-Id or the standard traceparent header used by tracing tools.

Following request r-5f2a9c
  1. 1Gatewaycreates r-5f2a9c and logs: order request received
  2. 2Order servicelogs: order 88231 created
  3. 3Payment servicelogs: charge_failed, reason gateway_timeout
  4. 4Order servicelogs: order 88231 marked payment_failed
  5. 5Notification workerreads the id from the queue message and logs: failure SMS sent

Searching request_id = r-5f2a9c now returns the whole story as one list. The id must also travel through queues and background jobs: put it inside each message and have the consumer log it. Otherwise the trail stops at the first queue.

Clocks are not perfectly in sync

Two machines' clocks can differ by a few milliseconds or more, so sorting by timestamp can place a reply before its request. Use timestamps for rough order, and the parent-child links of a trace (see the monitoring chapter) when exact order matters.

Log levels and sampling

Not every line deserves to be stored, let alone stored for months. Log levels mark how important a line is, and each service decides which levels it writes.

LevelUse it forIn production
DEBUGStep-by-step detail for developersOff, or on for one service for a short time
INFONormal events: order placed, user logged inOn, often sampled
WARNSomething odd that the code handled, like a retry that workedOn
ERRORA request failedOn, never sampled
FATALThe process cannot continueOn, and usually raises an alert

Levels decide what is written; sampling decides how much of it is kept. Sampling keeps only a fraction of routine lines: keeping 5% of successful requests cuts their volume twentyfold while still showing what normal looks like. Decide per request, not per line, so a kept request keeps all its lines and you never read half a story.

  • Head sampling decides when the request starts. It is simple and cheap, but it may drop the very request that later failed.
  • Tail sampling holds a request's lines briefly and decides at the end: keep every failed or slow request, sample the rest. Better results, more memory.
  • Collapse repeats: a bug that logs the same error 10,000 times a second should produce one line with a count.
  • Change levels at runtime: switching DEBUG on for one service for 15 minutes beats a redeploy.
  • Never sample audit events: records of who logged in or changed a payout account go to a separate stream, covered near the end of this chapter.

Part 3 · The Pipeline

Agents, buffering and shipping

With the lines worth keeping defined, the next question is how they leave the machine. Services should not send logs over the network themselves. They write to standard output or a local file, which is fast and never waits on the network. A small collection agent on each machine, or on each node of a container cluster, does the rest.

What the agent does
  1. 1Tailfollow log files as they grow
  2. 2Enrichadd host, region, container and app version
  3. 3Batch and compressgroup lines for about 1 s or 1 MB, whichever comes first
  4. 4Shipsend the batch to the queue; retry with backoff if it fails
  5. 5Checkpointonce the queue confirms a batch, save the file position, so a restart resumes there

When the network or the queue is down, the agent keeps reading into a buffer on local disk with a fixed size limit. If the buffer fills, it must drop something, and the sensible order is DEBUG first, then INFO, and errors last. Shipping is at least once: after a crash the agent resends from its last checkpoint, so a few lines may arrive twice, which is usually harmless for logs.

Logging must never take the app down

Cap the agent's CPU, memory and disk use. A disk filled by runaway logs can crash the very service you are trying to debug, so rotate log files and delete local copies once they are shipped.

Watch: a log storm meets a queue

Agents could write straight into the log store, but large pipelines put a queue, a replicated log as in the message queue chapter, in the middle. Watch one failed order's lines travel from two machines to the on-call engineer's screen, then watch an incident push the store behind.

Predict before you watch: during an incident, the log store indexes more slowly than new lines arrive. What happens to the lines written meanwhile?
  • They wait in the queue and show up in search late.
  • None are lost, as long as the store catches up within the queue's retention, 3 days here.
Animated sceneFrom two log files to one story0:21

Watch for r-5f2a9c: one failed order leaves lines on two machines

Read it as steps

Lines from two services meet in one search, and a queue holds them while the store falls behind.

The request id is what turns files on separate machines into one story. The queue is what lets the store fall behind without losing lines: the queue turns a store outage into a delay instead of data loss. The next page sizes it.

Sizing the queue

The scene showed what the queue absorbs. It earns its place in four ways:

  • It absorbs bursts: an incident can multiply log volume five times over, exactly when you need the logs most.
  • It decouples: the store can be upgraded, slowed or restarted while agents keep shipping.
  • It feeds many readers: the search index, a cheap archive in a blob store and security tools each read the same stream at their own pace.
  • It allows replay: if a parser bug mangled an hour of lines, fix the parser and read that hour again.

Now the numbers. One partition of a log-based queue handles about 10 MB/s (the message queue chapter). At the app's 50 MB/s, that is at least 5 partitions on an ordinary evening, and a storm at 250 MB/s needs 25, so plan for about 32. Spread lines across partitions by host rather than by service, or the busiest service makes one partition hot. Three days of retention at 50 MB/s is 50 MB × 259,200 s ≈ 13 TB per copy, about 39 TB with three replicas. Sizing on raw bytes leaves headroom, since agents compress batches 5 to 10 times.

The advice flips for small systems. With a few dozen machines, agents with disk buffers can write straight to the store, and a queue would be one more system to run for little gain. It pays off once storms, several readers or replays matter.

Part 4 · Storing, Searching and Running It

Indexing and searching logs

Lines leave the queue for a store that must find them fast. Logs are written once, almost never changed, and nearly always searched by time first: "errors from payments in the last 15 minutes". Log stores exploit this with time-based partitions, one per hour or per day. A query opens only the partitions in its time range, and deleting old data means dropping whole partitions. Two indexing styles compete:

Index every wordIndex labels only
What is indexedEvery field and word of every lineA few labels: service, level, host, time
Finding a rare wordMillisecondsSeconds or more: scan the compressed lines that match the labels
Storage costHigh: the index can rival the data in sizeLow: a tiny index, lines stay compressed
Good forSecurity investigations and free-text diggingHuge volumes where queries filter by service and time

Indexing every word is the inverted index from the distributed search chapter, applied to log lines. Indexing only labels and scanning the rest sounds slow, but the haystack is small: one service's lines for 15 minutes come to about 1 GB before compression (50 copies × 50 lines a second × 500 B × 900 s), which a few machines can scan in seconds. Many teams mix both: full indexing for a few days of hot data, labels only for older data.

Always give a time range

A query without a time range must open every partition. Good log tools apply a default window, such as the last hour, and ask before scanning a whole month.

Retention and cost tiers

Most log searches look at the last few hours. Hardly anyone reads a line from eight months ago unless a security team or an auditor asks. So logs move through tiers, from fast and expensive to slow and cheap, and are deleted at the end.

TierAgeWhere it livesSearch speedCost
Hot0 to 7 daysSSD, fully indexedSecondsHigh
Warm7 to 30 daysCheaper disks, fewer replicasTens of secondsMedium
Cold30 days to 1 yearCompressed files in a blob storeMinutes; restore firstLow
DeletedAfter the retention periodNowhereNot possibleNone
Hot

Age0 to 7 days

Where it livesSSD, fully indexed

Search speedSeconds

CostHigh

Warm

Age7 to 30 days

Where it livesCheaper disks, fewer replicas

Search speedTens of seconds

CostMedium

Cold

Age30 days to 1 year

Where it livesCompressed files in a blob store

Search speedMinutes; restore first

CostLow

Deleted

AgeAfter the retention period

Where it livesNowhere

Search speedNot possible

CostNone

The hot tier is where the money goes. Seven days at 4.3 TB a day is about 30 TB of raw lines. With every field indexed, assume the stored lines plus the index take about twice that, plus one replica so a dead node loses nothing: 30 × 2 × 2 ≈ 120 TB of SSD. At an assumed $0.10 per GB-month for network SSD (check current prices), that is about $12,000 a month for the disks alone, before the machines.

Logs compress well, often 5 to 10 times, because lines repeat the same field names and values. Indexing labels only and keeping lines compressed 10× shrinks that week to about 6 TB, about $600 a month. A year of cold archives at 10× is about 160 TB, roughly $3,200 a month at about $0.02 per GB-month for object storage.

Retention differs by kind: debug lines for days, request logs for weeks, audit trails for years. The law can set a floor: in India, CERT-In's 2022 directions ask many organisations to keep logs of their systems for a rolling 180 days, stored in India, so check what applies to yours.

Cut volume at the source first

The cheapest log line is the one never written. Dropping noisy debug lines and sampling successful requests usually saves more than any storage trick.

Privacy: what never goes in a log

Long retention has a dark side. Logs are copied to many machines, kept for months and read by many people, so anything sensitive written into a line leaks to all of them. Treat the log store as a place that holds no secrets and as little personal data as possible.

Never logLog this instead
Passwords, OTPs, PINsOnly the outcome, such as otp_verified=false
Access tokens, API keys, session cookiesA token id, or the last 4 characters
Full card numbers, CVV, bank detailsThe last 4 digits of the card
Aadhaar or PAN numbersAn internal user id
Phone numbers, emails, addressesAn internal user id; a keyed hash if lines must be matched
Whole request and response bodiesSelected, named fields only

The safest data is data never written. Four habits make that stick:

  • Allow-list, do not block-list: the logging library writes only fields known to be safe, instead of trying to spot every dangerous one.
  • Redact in the pipeline too: a second check in the agents or indexers masks patterns, such as card numbers, that slipped through.
  • Control who can search: restrict access, record every search of the logs, and keep production logs off laptops.
  • Remember the law: privacy laws such as India's Digital Personal Data Protection Act, 2023 give people rights over their data, and erasing one person from months of logs is very hard. Not logging it is far easier.

Audit logs: a separate stream

Some lines are evidence, not debugging help: who logged in, who changed a restaurant's payout account, who exported customer data, which admin approved a refund. Security teams and auditors may need them years later, and they must trust that nobody edited them. These audit logs travel in a separate stream with stricter rules.

Application logsAudit logs
PurposeDebugging and incidentsEvidence of who did what, and when
SamplingRoutine lines sampled; debug often offNever sampled; every event kept
How it is writtenBest effort: an agent may drop debug lines when fullRecorded as part of the action, such as an outbox row in the same database transaction, then shipped
ChangesDeleted by tier on a scheduleAppend-only: no edits, no early deletes
Who can readEngineers on the teamA small security group; every read is itself recorded
Kept forDays to a yearYears, as long as the law or contracts require

An action and its audit record succeed or fail together, so no payout change can happen without its record. Keep audit events in write-once storage, such as a blob-store bucket set to refuse deletes until a retention date. Link each batch to the one before it with a hash, so a missing or edited batch shows up, and keep the latest hash where the log writers cannot change it, such as a separate account or a published daily digest: anyone who can rewrite the whole chain can also recompute every hash. An audit line still records the user id and the action, never the card number.

What goes wrong

Logging pipelines fail in predictable ways, and most failures peak during incidents, when the logs matter most.

ProblemEffectFix
Log storm in an incidentThe pipeline falls behind exactly when neededQueue buffer, collapse repeated lines, drop debug lines first
Agent buffer fullLines dropped on that hostBounded disk buffer, an alert on drops, priority by level
Log store slow or downSearch results are staleKeep a day or more in the queue; replay when the store is back
Missing request idsTrails stop at a queue or an old serviceShared library, ids inside messages, a check in code review
A secret in the logsA security incidentAllow-listed fields, pipeline redaction, rotate the leaked secret
Runaway costThe log bill grows faster than trafficSampling, tiers, per-team volume reports and quotas

Almost every fix either sheds low-value lines first or buys time with buffers. Three numbers catch most of these early: lines received per service, queue lag, and lines dropped by agents. A busy service that suddenly sends zero lines means the pipeline is broken, not the service.

In the interview

Logging usually comes up inside a bigger design, and a few sentences show that you know the whole path from the line in the code to the search box.

Say
"Services write JSON lines locally and an agent ships 1 s or 1 MB batches to a queue, so logging never blocks a request; a few lines may arrive twice."
Say
"At 50 MB/s, a fully indexed week is about 120 TB of SSD, so after 7 days logs move to cheaper tiers, and after 30 to compressed files at a tenth the size."
Say
"Errors are never sampled; successful requests are kept at 5%, a twentyfold cut. Audit events go to their own unsampled, append-only stream."
Avoid
"Every service writes its logs straight into the database." One slow store would then slow every request.
Avoid
One retention rule for everything: debug lines kept for a year waste money, and audit trails deleted after 30 days fail an audit.

Wrap-up

TermMeaning
Structured logA line of named fields, usually one JSON object
Request idAn id created at the edge that every service logs and passes on
Collection agentA process on each host that tails, batches and ships logs
Tail samplingKeeping or dropping a request's lines after seeing how it ended
Hot, warm and cold tiersStorage that gets slower and cheaper as logs age
Audit logAppend-only evidence of who did what, kept for years

Next comes Distributed Task Schedulers: work that runs later and on time, built on the same at-least-once thinking you used here to ship logs.

Part 5 · Check yourself

Quick check

Q1Why put a queue between the agents and the log store?

Think it through, then check.

Q2Search shows no logs from the payments service for the last 20 minutes, yet its dashboards show normal traffic. What could be wrong, and how would you catch it sooner?

Think it through, then check.

Q3In an incident, log volume jumps from 50 to 250 MB/s for 30 minutes, but the store indexes only 100 MB/s. How much piles up in the queue, and how long until search catches up?

Think it through, then check.

Q4To chase a payment bug, a developer asks to log full request bodies for a week. What do you decide, and what does each option cost?

Think it through, then check.

Q5A teammate wants a user_id label on the payment-errors metric so support can see each user's failures. Using the monitoring chapter, what do you say?

Think it through, then check.

Next chapter · 20 of 39Distributed Task Schedulers~32 min · 2 animated scenes