Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
Traces, metrics and logs are usually introduced as 'the three pillars', which suggests they are three views of the same thing. They are not — they are three different trade-offs between cost and resolution, and each one destroys information the others keep. A metric aggregates, so it is cheap enough to keep for a year but can never tell you about one request. A trace keeps a single request in full causal order, so it can tell you exactly what happened once, but you cannot afford to keep all of them. A log keeps arbitrary detail with no structure guarantees, which is why it is the most flexible and the least queryable. Knowing which one destroys the information you need is the whole skill.
Here is the same incident answered three ways so you can see what each signal keeps and what it throws away. The point is the last column: three questions, three different signals, and no amount of the wrong signal substitutes.
# The same 30 seconds of an outage, in three signals.
# ── METRIC: aggregated, cheap, keeps no individual request ──────────
# http.server.request.duration{route="/checkout",status=500} p99 = 8.2s
# answers: "how bad, since when, is it getting worse?"
# cannot answer: "which request?", "what were its inputs?"
# cost: ~1 series per (route x status). Keep for 13 months.
# ── TRACE: one request, full causal order, sampled ──────────────────
# trace 4bf92f3577b34da6a3ce929d0e0e4736
# POST /checkout 8210ms SERVER
# ├─ SELECT cart_items ~12ms CLIENT db.system=postgresql
# ├─ POST api.stripe.com/v1/charges 8010ms CLIENT <-- there it is
# │ http.response.status_code=200
# └─ INSERT orders ~9ms CLIENT
# answers: "which hop is slow, and is it ours?"
# cannot answer: "how often?" (you only kept 1 in 100)
# cost: kilobytes per request. Keep days, not months.
# ── LOG: arbitrary detail, no shape guarantees ──────────────────────
# {"ts":"...","level":"warn","msg":"stripe retry 3/3",
# "idempotency_key":"ck_8813","trace_id":"4bf92f...4736"}
# answers: "what exactly did the code decide to do?"
# cannot answer: anything aggregate, cheaply
# cost: highest per byte of insight. The trace_id is what makes it useful.
# The rule that follows:
# "how bad / since when" -> metric
# "why this one" -> trace
# "what did the code do" -> log, joined by trace_idpython3 main.py