What you'll learn: By the end of this lesson you will be able to choose between logs, metrics and traces for a given question, apply the RED and USE methods to a service, define an SLI, SLO and error budget for a user journey, and write alerts that fire on user-visible symptoms instead of noise.
Imagine you are a doctor and a patient says "I feel unwell". You take their temperature and pulse (numbers that summarise health), read their medical history (a record of what happened), and for a serious case follow a dye through the bloodstream to see exactly where the blockage is. Each tool answers a different question at a different cost.
Running a distributed system is similar. When a user says "checkout is slow", you need numbers that show how bad and since when, detailed records of what happened, and a way to follow one request across a dozen services. Observability is the practice of building systems so that you can answer questions about their behaviour from the outside, including questions you did not think of in advance.
The core idea
Monitoring watches for known problems: "alert me when CPU is above 90 percent". Observability goes further: the system emits enough rich data that you can investigate unknown problems: "why are requests from one region slow only for users with large carts?"
Two ideas organise the rest of this lesson:
Telemetry, the data your system emits: logs, metrics and traces.
Reliability targets, which turn that data into decisions: SLIs, SLOs and error budgets.
Telemetry without targets gives dashboards nobody acts on; targets without telemetry are guesses.
The three signals
Signal
What it is
Best at answering
Cost
Weakness
Metrics
Numbers sampled over time (counts, rates, gauges, histograms)
"Is something wrong, and how bad?" Trends, alerting, capacity
Cheap and compact, regardless of traffic volume
Aggregated: loses individual request detail
Logs
Timestamped records of discrete events
"What exactly happened in this case?"
Grows with traffic; expensive to store and search at scale
Hard to see the whole picture across services
Traces
The path of one request through services, as timed steps
"Where did the time go for this request?"
Needs instrumentation and usually sampling
Sampling can miss rare cases
Metrics
Metrics come in a few shapes. A counter only goes up (requests served, errors). A gauge goes up and down (queue length, memory used). A histogram records the distribution of values, such as request durations in buckets.
Prefer histograms for latency. Averages hide the pain: if 99 requests take 100 ms and one takes 10 seconds, the average looks acceptable while one user in a hundred waits ten seconds. Use percentiles: p50 (median), p95, p99. See requirements for percentile basics. Note that you cannot average percentiles across servers; aggregate the underlying histograms instead.
Cardinality matters: each unique combination of label values creates a separate time series. Labels like endpoint or status_code are fine; labels like user_id or request_id can explode memory and cost. High-detail identifiers belong in logs and traces.
Logs
Write structured logs, meaning one JSON object per event with consistent fields, not free-form sentences. Then you can filter by order_id or status instead of searching text with regular expressions.
Good log hygiene: a trace id on every line, consistent levels, no secrets or full personal data, and retention decided up front, because logs are the most expensive signal to keep.
Traces
A trace records one request's journey. It is made of spans: each span is one timed operation (an HTTP handler, a database query, a call to another service) with a parent, so spans form a tree. A trace id is passed along with each call (usually in a header) so every service adds its spans to the same trace.
sequenceDiagram
participant U as User
participant G as Gateway
participant O as Orders
participant I as Inventory
participant D as Database
U->>G: POST checkout
G->>O: create order
O->>I: reserve stock
I->>D: query stock
D-->>I: rows
I-->>O: reserved
O-->>G: order created
G-->>U: 201 Created
A trace view of that flow shows a waterfall of durations and makes the slow span obvious. The open standard for producing all three signals is OpenTelemetry, a vendor-neutral set of APIs, SDKs and a collector; many backends accept its data.
Because tracing every request is expensive at high volume, systems sample. Head sampling decides at the start of a request (for example keep 1 percent), which is simple but may discard interesting slow or failed requests. Tail sampling decides after the trace completes, keeping all errors and slow traces, at the cost of buffering spans in a collector.
How they work together
A typical investigation: an alert fires on a metric, a dashboard narrows it to one endpoint and region, an example trace shows the slow span is a database call, and the log lines for that span, found by trace id, show a query timing out. Link the signals by sharing ids and labels.
Staring at an empty dashboard, what do you put on it? Two compact checklists cover most needs.
RED: for services (request-driven)
Rate: requests per second.
Errors: how many fail, as a count or fraction.
Duration: how long requests take, as percentiles.
RED describes what users of a service experience. Put it on a dashboard for every service, per endpoint where it matters.
USE: for resources (things that get used up)
Utilisation: how busy the resource is (percent of time in use).
Saturation: how much extra work is waiting (queue depth, run queue).
Errors: resource-level error events.
USE applies to CPUs, memory, disks, network links, connection pools and thread pools. Saturation is the early warning: a connection pool at 100 percent utilisation with a growing wait queue is about to cause latency, even if CPU looks fine.
graph LR
U["User symptoms"] -->|measured by| RED["RED metrics per service"]
RED -->|explained by| USE["USE metrics per resource"]
USE --> A["Root cause"]
Use RED to learn that users are hurting and where, then USE to learn why. A related set, the "four golden signals" (latency, traffic, errors, saturation), covers the same ground.
SLIs, SLOs, SLAs and error budgets
Measuring is not the same as deciding what is good enough. Four terms get confused often, so define them carefully.
SLI (service level indicator): a measurement of one aspect of service quality from the user's point of view, expressed as a ratio. Example: the proportion of checkout requests that return a successful response in under 500 ms.
SLO (service level objective): a target for an SLI over a time window. Example: 99.9 percent of checkout requests are good, measured over 30 days.
SLA (service level agreement): a contract with customers that states consequences, often financial credits, if a target is missed. An SLA is usually looser than the internal SLO, so you have room to react before breaching it.
Error budget: the amount of unreliability the SLO permits. It equals 100 percent minus the SLO.
A worked example
Assume a checkout API receives 10 million requests in 30 days (illustrative). The SLO is 99.9 percent good requests. The error budget is 0.1 percent, which is 10,000 bad requests in the window.
Suppose a faulty release causes 4,000 failures. You have spent 40 percent of the budget in one incident. If the budget is nearly gone halfway through the window, the team slows risky changes and focuses on reliability work. If plenty remains, the team can release faster and run experiments. The budget turns the argument "reliability versus features" into a shared, quantified decision.
An error budget policy says in advance what the team does at each level of spend.
stateDiagram-v2
[*] --> Healthy
Healthy --> Cautious: more than half the budget spent
Cautious --> Frozen: budget exhausted
Healthy --> Frozen: one incident exhausts the budget
Frozen --> Cautious: burn stops and the window recovers
Cautious --> Healthy: budget recovers
How to read it: Healthy means normal release pace, Cautious means smaller releases and reliability work first in planning, and Frozen means only fixes and rollbacks ship until the budget recovers. Agree the thresholds with product before you need them.
Time-based thinking helps too: a 99.9 percent availability target over a 30-day month allows about 43 minutes of full outage (30 days x 24 x 60 = 43,200 minutes, times 0.001). See the table of nines in requirements.
Choosing good SLIs
Measure from the user's side. Prefer the load balancer or client view over a server's own "I am healthy" signal.
Pick user journeys, not components. "Can a user complete checkout?" beats "Is the pricing service up?".
Use ratios of good events to valid events, covering availability, latency and, where relevant, correctness, freshness and durability.
Do not aim for 100 percent. It is unreachable, extremely costly, and slows all change.
TIP
Set your first SLO from data: measure the current performance for a few weeks, then pick a target slightly below what you reliably achieve and that users are happy with. Tighten later if justified.
Burn rate alerts
The burn rate is how fast you consume the budget relative to a steady rate that would exactly use it up over the window. A burn rate of 1 uses the budget exactly at the end of the window; 10 would exhaust it in a tenth of the time. A well-known approach is multi-window, multi-burn-rate alerting: page if the burn rate is high over both a short and a longer window (a fast burn that threatens the SLO within hours), and open a ticket for slow burns that threaten it within days. The two windows prevent flapping on brief spikes while still catching real trends.
Alerting on symptoms, not causes
An alert should mean "a human must act now". Everything else is noise, and noise trains people to ignore pages.
Alert on
Example
Why
Symptoms (user impact)
Checkout error rate burning SLO budget at a high rate
Directly tied to user pain, so it is worth action
Causes (internal signals)
CPU at 85 percent; one pod restarted
Often harmless, and may be self-healing; many do not affect users
Cause-level signals are still valuable, but as dashboards and diagnostic data, or as low-urgency tickets, not pages. The exceptions are leading indicators of certain failure, such as a disk that will be full in four hours or a certificate expiring tomorrow. Those are worth a notification because the user impact is close to certain if no one acts.
Good alert hygiene: every page has a runbook link; alerts are actionable and urgent, otherwise they become tickets or dashboard items; alerts that never led to action are deleted in a monthly review; routing follows ownership; and alert fatigue is tracked as pages per shift and the fraction that needed action.
This flowchart sorts a signal into page, ticket or dashboard.
flowchart TD
Q1{"Is an SLO burning fast?"} -->|yes| PG["Page with a runbook"]
Q1 -->|no| Q2{"Will the budget run out within days?"}
Q2 -->|yes| TK["Open a ticket"]
Q2 -->|no| Q3{"Is failure certain soon if ignored?"}
Q3 -->|yes| TK2["Notify the owner, for example disk full in hours"]
Q3 -->|no| DB["Dashboard only"]
How to read it: the question is always user impact and time left, never how alarming a raw number looks.
Dashboards that help
A dashboard is a tool for a specific question. Three layouts work well: a service overview (RED panels at the top, then the SLO and error budget remaining, then dependencies), a resource view (USE panels for databases, caches, queues and pools), and a journey view (sign-ups, orders per minute, payment success), since technical health that hides business drops is a false comfort. Put the most important signal top-left, show deploy markers, keep panels few, and remove dashboards nobody has opened in months.
Putting it together in a design
Treat observability as part of the architecture: list the critical user journeys; define an SLI, SLO and error budget policy for each; instrument every service with RED metrics, structured logs and trace propagation (including gateway and mesh layers) and shared resources with USE metrics; create burn-rate alerts with runbooks; and plan cost through retention, sampling and cardinality limits.
Telemetry has its own failure modes. If your pipeline falls over during an incident you lose visibility exactly when you need it, so keep observability infrastructure at least as reliable as what it watches, and know how you would debug without it.
In practice: onboarding a booking platform to SLOs, error budgets and burn-rate alerts
NOTE
A realistic scenario, not a case history. This is a composite example built from common production patterns. It does not describe any specific company, and every number in it is an illustrative assumption.
The situation
A table and salon booking platform serves consumers on web and mobile and merchants through a dashboard. Fourteen engineers in three teams run about a dozen services on Kubernetes with PostgreSQL, Redis, RabbitMQ for confirmation messages, and an SMS provider. Telemetry is Prometheus and Grafana with Alertmanager, plus unstructured log files.
There are around 60 alerts, mostly on causes such as CPU, pod restarts and queue length, and the on-call engineer gets about 25 pages a week of which roughly 70 percent need no action (all numbers assumed). Yet a bad release that broke "confirm booking" for 40 minutes was discovered through a customer support ticket, not a page. The team wants fewer, better alerts and a shared way to decide between shipping and stabilising.
Starting architecture
How to read it: alerts watch internals, and logs cannot be joined to metrics or traces, so a page tells the engineer something is odd but not who is hurt.
Options considered
Option
What it fixes
What it costs
Risk
A. Smaller change: prune the 60 alerts to those that ever led to action and add runbook links
Noise, quickly
A few days of review
Still cause-based; the 40-minute outage pattern can recur
B. SLOs for the top journeys with multi-window burn-rate alerts, structured logs and trace ids
Detects user impact; ends the shipping-versus-stability argument
Instrumentation work; cultural change; some telemetry cost
Badly chosen SLIs give false comfort
C. Buy a full observability suite
Fast dashboards and tracing
Subscription, data egress, lock-in to a data model
Does not decide what to measure; cost rises with volume
The decision
Field
Entry
Context
Noisy cause-based paging, user-impact outages found late, no budget policy
Decision
Option B, preceded by the pruning from A. Two SLOs first: create-booking succeeds in under 800 ms, and confirmation message is sent within 60 seconds, both at 99.5 percent over 30 days
Good consequences
Pages map to user pain; a quantified budget settles release debates; traces and logs share ids
Bad consequences
Telemetry volume and cardinality need guarding; SLO owners must review monthly; early SLOs will be wrong and need adjusting
Revisit when
Two quarters of data show the SLO is never close to burning (tighten it) or burns every month (fix reliability or loosen it)
The arithmetic matters. Assume 2 million valid create-booking requests in 30 days. A 99.5 percent SLO gives an error budget of 0.5 percent, which is 10,000 bad requests. A burn rate of 1 uses exactly that budget in 30 days (720 hours).
Alert
Burn rate
Windows
Error ratio that trips it
Budget used if it persists
Page, fast burn
14.4
1 hour and 5 minutes
7.2 percent
2 percent per hour
Page, slower burn
6
6 hours and 30 minutes
3 percent
5 percent per 6 hours
Ticket
1
3 days and 6 hours
0.5 percent
10 percent per 3 days
At a burn rate of 14.4 the whole budget would be gone in about 50 hours (720 divided by 14.4), which is why it pages.
Rollout and verification
flowchart LR
W1["Weeks 1 and 2: measure SLIs, no alerts"] --> W2["Week 3: burn alerts to a chat channel"]
W2 --> W3["Week 4: burn alerts page, cause alerts retired"]
W3 --> W4["Month 2: first monthly SLO review"]
Measure for two weeks before choosing final targets, then run the burn-rate rules in a non-paging channel and check that they would have caught the 40-minute outage when replayed over historical data. Seed a fault in a game day (return errors from confirm-booking in staging, then a small canary in production) and time how long it takes the page to arrive. Track page precision (the share of pages that needed action), pages per shift, time to detect, and the budget remaining panel on the service overview dashboard.
The 2 a.m. failure
The page does not come from an SLO. It comes from a watchdog: "Heartbeat from Prometheus missing for 5 minutes". A release added a user_id label to the booking duration histogram. The number of time series exploded, Prometheus ran out of memory and restarted in a loop, and with no metrics there was nothing for the burn-rate rules to evaluate. Silence looked like health.
sequenceDiagram
participant S as Booking service
participant P as Prometheus
participant A as Alertmanager
participant W as External watchdog
S->>P: histogram with user id label
P->>P: series count explodes, out of memory
P--xA: no rules evaluated, no watchdog alert sent
A--xW: heartbeat stops arriving
W->>W: no heartbeat for 5 minutes, page the platform on-call
First three checks:
kubectl get pods -n monitoring and kubectl top pod -n monitoring to see restarts and memory.
Prometheus's TSDB status endpoint (/api/v1/status/tsdb) after it starts, to list metrics and labels with the most series.
kubectl rollout history deploy/booking-api to find the release that introduced the label.
Mitigation: roll back the release (or drop the label with metric_relabel_configs), and confirm the series count falls once Prometheus is up. If it cannot get through replaying its write-ahead log within its memory limit, raise the limit temporarily rather than deleting data. Follow-ups: set label_limit and sample_limit on scrape jobs, add a CI check that rejects unbounded labels, and alert on head series growth as a leading indicator. Treat the watchdog as a standing rule: an always-firing alert routed to an external dead-man's-switch service that pages when the heartbeat stops, so the check does not depend on the Prometheus that just failed.
Architect's notes
Measure first. Spend two weeks collecting the SLI before setting a target; invented SLOs are either ignored or constantly breached.
Ask in the review: what is the SLI for, who owns it and what happens at zero budget? An SLO without an owner and a policy is a dashboard.
Watch the telemetry bill. Logs and high-cardinality metrics dominate cost; set retention, sampling and label limits per signal before launch, not after the invoice.
Pages are a shared resource. Give each page a runbook and an owner, and delete pages that never lead to action; fewer pages protect the on-call rota.
Protect the pipeline. Monitor the monitor from outside, because a broken telemetry stack fails silently.
Defer fancy things. Skip per-customer SLOs and automated budget gating until the basic loop of measure, review and adjust has run for a quarter.
Common mistakes
Alerting on every cause. CPU, memory and restart alerts page people for harmless events. Alert on SLO burn and user symptoms; keep cause signals for diagnosis.
Using averages for latency. Averages hide the slow tail that users feel. Use histograms and p95 or p99, aggregated correctly.
High-cardinality metric labels. User or request ids make metric storage explode. Put them in logs and traces instead.
Unstructured logs without correlation ids. You cannot follow a request across services. Emit structured logs with trace ids on every line.
SLOs set at 100 percent or invented without data. They are unattainable or meaningless. Base the target on measured behaviour and user tolerance.
Measuring from the server's point of view only. A service can report itself healthy while the load balancer or client sees errors. Measure at the edge.
No ownership or runbooks for alerts. Pages arrive with no instructions, and response is slow. Attach a runbook and route to a clear owner.
Key takeaways
Observability answers unknown questions. Monitoring checks known failures; observability lets you investigate surprises.
Use all three signals together. Metrics detect, traces locate, logs explain, linked by shared ids.
Percentiles beat averages. Record latency in histograms and watch p95 and p99.
RED for services, USE for resources. Rate, errors and duration show user experience; utilisation, saturation and errors explain it.
An SLI is a measurement, an SLO is a target, an SLA is a contract. Keep the SLA looser than the SLO.
Error budgets turn reliability into a decision. Budget remaining guides whether to ship or stabilise.
Alert on symptoms. Page on user impact and budget burn; keep cause metrics for dashboards and tickets.
Watch cost and cardinality. Telemetry volume, labels and retention need design, just like any data system.
Practice exercises
Exercise 1 — Starter (30 min): For a photo-sharing app, list three critical user journeys. For each, write one SLI as a ratio of good events to valid events, with a latency threshold where relevant, and propose an SLO with a time window. Compute the error budget in minutes of downtime or in number of failed requests (assume a request volume).
Exercise 2 — Intermediate (2 hours): Sketch a RED dashboard for an orders service and a USE dashboard for its database and connection pool. For each panel name the metric type (counter, gauge or histogram), the labels you would allow, and one label you would deliberately forbid because of cardinality. Then write three alert rules: one fast-burn page, one slow-burn ticket, and one leading-indicator alert, each with its runbook outline.
Exercise 3 — Advanced (half day): An incident review: users report slow checkout, the average latency graph looks normal, CPU is fine, and error logs are sparse. Using the signals in this lesson, write the investigation plan step by step: which metrics, which trace queries (including a sampling concern), which logs and what each result would tell you. Then propose three instrumentation or alert changes that would have caught the problem earlier, and estimate the telemetry cost impact of each using stated assumptions.
Storage engines — how time-series data is stored and why cardinality matters.
Enroll to keep reading
Lesson 13 of 14 · System Design Building Blocks: Data, Messaging and Scale
Learn how logs, metrics and traces work together, apply the RED and USE methods, and define SLIs, SLOs and error budgets that drive useful alerts.
In the rest of this lesson
What to measure: RED and USE
SLIs, SLOs, SLAs and error budgets
Alerting on symptoms, not causes
Dashboards that help
Putting it together in a design
In practice: onboarding a booking platform to SLOs, error budgets and burn-rate alerts
Enrolling is free. It unlocks every lesson in System Design Building Blocks: Data, Messaging and Scale, tracks your progress across devices and keeps your quiz scores. The newsletter is optional.
Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.
Get new articles by email
Free. We email a confirmation link first, and you can unsubscribe from any email.