Observability — metrics, logs, traces and alerts
How you find out something is broken, and how you find out why, at three in the morning.
What problem does observability solve?
A design that cannot be debugged is not finished. The interviewer asks "how do you know it is working?" and the answer cannot be "we would look at the logs".
Three signals answer three different questions. Metrics tell you something is wrong. Traces tell you where. Logs tell you why. People collect all three and can still answer none of them, because they collected the wrong things.
What observability costs you
Every concept in here states a cost. A technique with no price is a technique you have not understood yet — and naming the cost out loud is most of what separates a senior answer from a junior one.
When to reach for observability, and when not to
Use it when
- Every production system. It is part of the design.
- Whenever you are asked how you would detect or debug a failure. This is a standard follow-up.
- Before you promise an availability number, because you cannot promise what you cannot measure.
Do not, when
- Do not log every request body at full volume. That is a cost problem and a privacy problem at the same time.
- Do not alert on every metric. An alert nobody acts on trains people to ignore alerts, which is worse than having none.
How observability actually works
Metrics are numbers over time, cheap to store and always on. The four worth naming for any request-serving system are request rate, error rate, latency percentiles and saturation (how full the thing is). Quote p50 and p99, never the average, for the reason the latency page gives: nobody experiences the average. For a queue, the equivalents are consumer lag, oldest message age and dead letter arrival rate.
The rule that keeps metrics affordable is no unbounded labels. A metric tagged with user id or request id creates a separate time series per value, and that is how a monitoring bill ends up larger than the hosting bill. Tag by endpoint, status code and region: things with a small fixed set of values.
Traces follow one request across every service it touches, with a timing for each hop. This is the signal that answers "was it one slow call or two hundred fast ones", which is usually the question when something is slow. It needs a trace id created at the edge and passed through every call, including into queue messages, or the trace stops at the first async boundary, where many of the hard failures are. Because tracing every request is too expensive, you sample, and the right approach is to always keep traces for errors and slow requests, and sample the boring ones.
Logs are the detail, and they are the most expensive per byte. Make them structured (key-value, not prose) so they can be searched and aggregated instead of grepped. Every line should carry the trace id, so a log and a trace can be joined. And logs are where personal data leaks by accident, so decide explicitly what may not be written.
SLIs and SLOs are how you turn all of this into a decision. An SLI is the thing you measure: say, the share of requests that succeed in under 300 ms. An SLO is the target you commit to, say 99.9% over 30 days. The useful part is what falls out of it: an error budget. If the target allows 0.1% failure and you have used a quarter of it this month, you can keep shipping. If you have burned it, you stop shipping features and fix reliability. That converts an argument about "is this stable enough" into arithmetic, and mentioning it is a strong senior signal.
Alerting has one rule that matters: alert on symptoms, not causes. Page someone when users are affected: error rate up, latency past the SLO, the queue's oldest message getting older. Do not page them because CPU hit 90%, because CPU at 90% with everything working fine is not a problem, and it teaches people to ignore the pager. Every alert should be actionable, should say what to do, and should be reviewed when it fires and turns out not to matter.
Two more that come up. Health checks should distinguish liveness (is this process alive; cheap and dependency-free) from readiness, which may check dependencies. Confusing them takes down whole fleets, as the load balancing page describes. And dashboards are not monitoring: a dashboard nobody is looking at at 3am detects nothing. The alert is the detection; the dashboard is for the investigation afterwards.
- Saying "we would monitor it" with no named signal, threshold or action.
- Metrics labelled with user id or request id, which explodes cardinality and cost.
- No trace id passed through queue messages, so the trace stops at the async boundary.
- Alerting on CPU and disk instead of on what users experience.
- Promising an availability number with nothing measuring it.
The observability follow-up an interviewer will ask
"Something is slow. Users are complaining. Walk me through the first five minutes."
I start with the metric, not the logs, because the logs cannot tell me the shape of the problem. First: is it everyone or a subset? A dashboard of error rate and p50 and p99 by endpoint answers that in seconds. If p99 is bad and p50 is fine, it is data-dependent — some users have far more rows, or one tenant is hammering one path. If both are bad, something shared is saturating. Second: what changed when it started? Many incidents have a change behind it, so I look at what deployed or what config changed in the window when the graph bent. That single question resolves a large share of incidents on its own. Third: I take a trace of a slow request and see where the time went — one slow dependency, or many small calls. That tells me which service to look at, which is the thing I would otherwise have guessed. Fourth, and only now, logs for that service, filtered by the trace id, to find out why. Fifth, in parallel with all of it: mitigate before diagnosing. If a recent deploy is the suspect, roll it back before I finish understanding it, because the goal in the first five minutes is to stop the bleeding, not to be right. What I would want to have set up beforehand for this to work: trace ids propagated through queues as well as HTTP, structured logs carrying that id, and an alert that fired from the SLO rather than from a user complaint. In this question the users told us first, which means my monitoring already failed.
Your turn
Why alert on error rate rather than on CPU usage? Give the failure mode of each choice.
Learn it from the source
This page gets you interview-ready. These take you to where the idea is actually specified or measured — read them when you want the real depth.
- OpenTelemetry — Observability primer ↗Traces, metrics and logs defined by the project that standardised them.
- Google SRE Book — Practical Alerting ↗How to alert on symptoms rather than causes, and why paging on CPU is wrong.