Observability
Observability is the ability to understand what a system is doing β and why β from the outside, using the signals it emits. It never sits on the request path: these components are side channels that watch the services. But without them you are flying blind: you cannot see which box is saturated, you cannot tell a slow dependency from a slow database, and you find out about outages from angry users. The rule of the course is simple: you can't fix what you don't measure.
The palette has three signals and two actions. Signals: metrics (numbers over time β is something wrong?), logs (detailed events β what exactly happened?) and traces (one request across services β where did the time go?). Actions: alerting (wake a human when users are affected) and health checks (let load balancers and orchestrators route around broken instances automatically). In BoxOffice, a RED dashboard per service, traces from the gateway to the payment provider, and an SLO burn-rate alert on checkout are what tell the on-call engineer, at 10:01 on sale day, that the payment provider is slow β not the database.
| Component | One-liner | Playground id |
|---|---|---|
| Metrics | Numeric time series (rates, errors, latency percentiles, utilization) | metrics |
| Logs | Timestamped records of discrete events, ideally structured (JSON) | logs |
| Tracing | The path and timing of one request across services (spans) | tracing |
| Alerting | Rules on signals that notify a human when users are affected | alerting |
| Health Check | Endpoint probes that tell LBs/orchestrators if an instance can serve | healthcheck |
flowchart LR
S1[gateway] -.->|OTLP| COL[OpenTelemetry Collector]
S2[order] -.->|OTLP| COL
S3[payment] -.->|OTLP| COL
COL -.-> M[Metrics]
COL -.-> L[Logs]
COL -.-> T[Tracing]
M -.->|SLO burn rate| AL[Alerting]
AL -.->|page| ONCALL((on-call))
LB[Load Balancer] -.->|GET /health/ready| S2 | Question | Signal | Example |
|---|---|---|
| Is something wrong? How much? | Metrics | Checkout error rate jumped from 0.1% to 4% |
| Where is the time going in this slow request? | Tracing | 3.8 s of 4 s inside payment β provider |
| What exactly happened to order 81234? | Logs | provider timeout after 2000 ms, retry 2/3 |
| Should someone act now? | Alerting | SLO burning 14Γ faster than sustainable |
| Should traffic go to this instance? | Health check | /health/ready returns 503 during warm-up |
Metrics
What it is. Metrics are numeric measurements aggregated over time: counters (requests served, errors), gauges (queue depth, memory in use) and histograms (latency distribution, from which you read p50/p95/p99). Each time series is identified by a name and a set of labels (http_requests_total{service="order", route="/orders", status="500"}). They are cheap to store and query because they are pre-aggregated, which makes them the right signal for dashboards, SLOs and alerts. The standard recipes: RED for services (rate, errors, duration), USE for resources (utilization, saturation, errors) and Google's four golden signals (latency, traffic, errors, saturation).
When to use.
- Always, for every service and resource: the RED dashboard of BoxOffice's
order,inventoryandpaymentservices. - SLIs and SLOs: "share of checkout requests under 500 ms" is computed from a latency histogram.
- Capacity planning and autoscaling: CPU utilization, queue depth and consumer lag drive the autoscaler.
- Business metrics alongside technical ones: tickets sold per minute, waiting-room size, conversion.
When not to (or watch out).
- To investigate a single request: metrics are aggregates β use traces and logs.
- Cardinality: every unique combination of label values is a separate series. Labels like
user_id,order_idor full URLs explode the number of series and the cost β keep labels bounded (route templates, status class). - Averages hide the tail: record histograms and look at percentiles; never average percentiles across instances.
- Sampling interval: a 1-minute scrape smooths a 10-second spike away.
Key numbers.
| Metric | Typical value | Note |
|---|---|---|
| Scrape / export interval | 15β60 s | Prometheus' global default is 1 min; 15 s is common |
| Series count | β metrics Γ label combinations Γ instances | 50 metrics Γ 200 label combos Γ 20 pods = 200 k series (β illustrative) |
| Cost per sample | β 1β2 bytes compressed in modern TSDBs | β illustrative; series count matters more than sample count |
| Retention | β 15 days raw locally; months to years downsampled | Long-term storage in Mimir/Thanos or a managed service |
| Percentiles to watch | p50, p95, p99 (p99.9 for high-volume paths) | From histograms, per route |
Failure modes.
- Cardinality explosion β someone adds
user_idas a label; memory and cost blow up, the metrics backend falls over β label allow-lists, limits per tenant, review metric changes. - Missing metric where it mattered β the slow dependency had no client-side latency metric β instrument outbound calls (OpenTelemetry auto-instrumentation does).
- Misleading averages β histograms and percentiles; per-route breakdowns.
- Monitoring that dies with the system β the metrics stack runs in the same cluster that is down β monitor from outside too (synthetic checks, a separate region or a managed service).
- Dashboards nobody reads β few dashboards built around SLOs and RED/USE, linked from alerts.
In the playground. metrics is a side channel, not part of the request path. Connect services to it and their utilization becomes visible on the canvas; a service with no metrics node connected shows its utilization as "?" β you can't fix what you don't measure (a "god mode" toggle reveals everything for teaching). It adds a monthly cost. Scenarios: observability-stack, scale-out.
Real products.
- Amazon CloudWatch (metrics, dashboards) β native metrics for AWS services and custom metrics.
- Amazon Managed Service for Prometheus and Amazon Managed Grafana.
- Cloud Monitoring β metrics, dashboards and SLO monitoring.
- Google Cloud Managed Service for Prometheus.
- Azure Monitor (Metrics) and Azure Monitor managed service for Prometheus.
- Azure Managed Grafana.
- Prometheus β pull-based metrics collection, PromQL, the de facto standard for Kubernetes.
- Grafana β dashboards over many data sources.
- Grafana Mimir, Thanos, VictoriaMetrics β scalable long-term metric storage.
- OpenTelemetry (SDKs and Collector) β vendor-neutral instrumentation and export (used in Hands-on 8 with
grafana/otel-lgtm).
In the interview
"Every service exports RED metrics β rate, errors and a latency histogram per route β and every resource USE metrics. My SLIs and alerts are computed from those histograms, I watch p99, not the average, and I keep labels bounded so cardinality doesn't explode."
Learn more: Observability βΊ Signals Β· Observability βΊ OpenTelemetry Β· Estimation βΊ Percentiles Β· Performance βΊ Methodology Β· Hands-on 8
Logs
What it is. Logs are timestamped records of discrete events: "order 81234 created", "payment provider timeout after 2000 ms", "user 77 failed login". Modern practice is structured logging β one JSON object per event with fields (level, service, trace_id, order_id, duration_ms) β so logs can be filtered and aggregated, not just grepped. Logs are shipped by an agent or the OpenTelemetry Collector to a central store where they are indexed and searched. Including the trace id in every log line links logs to traces.
When to use.
- Investigating a specific request or user: "what happened to order 81234?" β search by
order_idortrace_id. - Errors with context: stack traces, the input that caused them, which retry attempt failed.
- Audit trails for security and compliance: who refunded what, who changed permissions (kept separately, with longer retention and restricted access).
- Rare events that are not worth a metric.
When not to (or watch out).
- As your main way to detect problems: counting log lines to compute error rates is slow and expensive β use metrics, link to logs.
- Logging every request body at high volume: costs grow linearly with traffic β sample success logs, keep all errors.
- Sensitive data: passwords, tokens, card numbers and personal data must never be logged (LGPD/GDPR, PCI DSS) β redact at the source.
- Unstructured text with inconsistent formats makes search nearly impossible.
Key numbers.
| Metric | Typical value | Note |
|---|---|---|
| Log volume estimate | requests/s Γ lines per request Γ bytes per line | 2,000 req/s Γ 5 lines Γ 500 B = 5 MB/s β 430 GB/day (β illustrative) |
| Typical structured log line | β 200 B β 2 KB | Grows fast with stack traces and payloads |
| Ingestion-to-searchable delay | β seconds | Depends on the pipeline |
| Hot retention | β 7β30 days | Older logs to cheap object storage; audit logs longer |
| Cost driver | GB ingested and indexed | Index less, sample success logs |
Failure modes.
- Log flood β a failing dependency makes every request log a stack trace; the pipeline backs up and the bill explodes β rate-limit repeated logs, sample, drop debug in production.
- Missing correlation β logs from 6 services cannot be joined β propagate and log the
trace_id. - Leaking secrets or personal data β structured fields with redaction, reviews, scanning.
- Logging blocking the app β synchronous writes to a slow sink add latency β async appenders, local buffering, stdout + agent.
- Lost logs when a pod dies β write to stdout and let the node agent ship them; buffers with backpressure.
In the playground. logs is a side channel: connecting services to it enables the event log details (what happened to the sampled requests, which errors and retries) in the Events tab. It costs per GB, so it shows up in the cost tile. Scenarios: observability-stack, cascading-failure.
Real products.
- Amazon CloudWatch Logs β log collection, retention and Logs Insights queries.
- Amazon OpenSearch Service β search and analytics over logs.
- Cloud Logging β collection, Log Explorer, log-based metrics, sinks to BigQuery or Cloud Storage.
- Azure Monitor Logs (Log Analytics workspaces) β log storage queried with KQL.
- Grafana Loki β log aggregation indexed by labels only (cheap); queried with LogQL.
- OpenSearch / Elasticsearch β full-text indexed log search (the "ELK" stack with Logstash and Kibana).
- Fluent Bit, Vector, OpenTelemetry Collector β log shippers and processors.
In the interview
"Services write structured JSON logs to stdout with the trace id in every line; an agent ships them to a central store. I use metrics to detect, traces to locate and logs to explain. Errors are always kept, successful requests are sampled, and nothing sensitive is ever logged."
Learn more: Observability βΊ Signals Β· Observability βΊ OpenTelemetry Β· Observability βΊ Incident response Β· Network & security βΊ Zero trust Β· Hands-on 8
Tracing
What it is. Distributed tracing follows one request across all the services it touches. Each unit of work is a span (name, start, duration, attributes, status) and spans form a tree under one trace id, which is propagated between services in request headers (the W3C traceparent header) and in message metadata. The result is a waterfall: you see that a 4 s checkout spent 3.8 s waiting for the payment provider, or that one page makes 40 sequential database calls (N+1). Because recording every request is expensive, traces are sampled: head sampling (decide at the start, e.g. 5%) or tail sampling (decide at the end, keeping all errors and slow requests).
When to use.
- Any system with more than a couple of services: finding where latency or errors come from.
- Diagnosing the critical path: what is sequential that could be parallel, which dependency dominates p99.
- Understanding fan-out and retries: a trace shows the three retries that tripled the load.
- LLM applications: a trace per agent run shows each model call, tool call, tokens and latency.
When not to (or watch out).
- Without context propagation through every hop (HTTP, gRPC, Kafka headers, async jobs), traces break into fragments β use OpenTelemetry instrumentation everywhere.
- 100% sampling at high volume is expensive; too little sampling misses the rare slow requests β tail-based sampling for errors and slow traces.
- Traces show one request; for trends use metrics (or metrics derived from spans).
- Sensitive data in span attributes is as dangerous as in logs.
Key numbers.
| Metric | Typical value | Note |
|---|---|---|
| Spans per trace | β tens to hundreds | One per service call, query, cache lookup |
| Head sampling rate | β 1β10% in high-volume production | β illustrative; 100% in development and low-volume services |
| Instrumentation overhead | small (β microseconds per span) | Export is batched and asynchronous |
| Propagation header | traceparent (W3C Trace Context) | Carries trace id, parent span id, sampled flag |
| Retention | β days to a few weeks | Traces are for recent investigations |
Failure modes.
- Broken traces β one service or the message broker drops the context β instrument every hop, propagate through Kafka headers and job payloads.
- Sampling hides the problem β the rare 10-second request was not sampled β tail sampling (keep errors and slow traces), exemplars linking metrics to traces.
- Collector overload at peak β batching, horizontal Collector scaling, sampling at the edge of the pipeline.
- Clock skew makes child spans look like they start before the parent β rely on durations within a host, NTP everywhere.
In the playground. tracing is a side channel: when present, the Trace tab shows the waterfall of one sampled slow request, with the time spent queueing and serving in each node β the fastest way to find a slow dependency or a retry storm. It adds cost. Scenarios: observability-stack, cascading-failure, retry-storm, ai-assistant.
Real products.
- AWS X-Ray β distributed tracing for AWS workloads (accepts OpenTelemetry data).
- Amazon CloudWatch Application Signals β application performance monitoring built on OpenTelemetry.
- Cloud Trace β distributed tracing, OpenTelemetry-compatible.
- Azure Monitor Application Insights β application performance monitoring with distributed tracing (OpenTelemetry distro).
- OpenTelemetry β the standard APIs, SDKs, auto-instrumentation agents and Collector.
- Jaeger β tracing back end and UI (CNCF).
- Grafana Tempo β trace storage on object storage, integrated with Grafana.
- Zipkin β the original open-source tracing system.
In the interview
"With OpenTelemetry the trace id flows from the gateway through every service and through Kafka headers, so one slow checkout shows up as a waterfall: 3.8 s of 4 s waiting on the payment provider. I keep all error and slow traces with tail sampling and a few percent of the rest."
Learn more: Observability βΊ OpenTelemetry Β· Observability βΊ Signals Β· Performance βΊ Bottlenecks Β· AI systems βΊ Safety & evals Β· Hands-on 8
Alerting
What it is. Alerting turns signals into notifications for humans: rules evaluated continuously over metrics (or logs) that, when true for long enough, route a message to the right person β a page for urgent problems, a ticket or chat message for the rest. Good alerts are about symptoms users feel (errors, latency, SLO violations), are actionable, and come with a runbook link. The modern standard is SLO-based alerting with burn rates: alert when the error budget is being consumed much faster than sustainable, using two windows (e.g. 1 h and 5 min) so the alert is both fast and not noisy.
When to use.
- Every user-facing SLO: "checkout availability 99.9% over 28 days" with fast-burn (page) and slow-burn (ticket) alerts.
- Leading indicators of imminent outages: disk filling up within hours, certificate expiring in 7 days, consumer lag approaching retention.
- On-call rotations with escalation policies and acknowledgement.
When not to (or watch out).
- Alerting on causes instead of symptoms ("CPU > 80%") pages people for things that don't affect users β alert fatigue, and real pages get ignored.
- Alerts without an owner or a runbook.
- Static thresholds on noisy metrics flap β use windows, burn rates, hysteresis.
- Alerting depends on the monitoring stack being alive β a "dead man's switch" (an always-firing heartbeat alert that pages when it stops).
Key numbers.
| Metric | Typical value | Note |
|---|---|---|
| Error budget of a 99.9% SLO over a rolling 28 days | β 40 minutes of full downtime | 0.1% Γ 28 days Γ 24 h Γ 60 min |
| Fast-burn page (SRE Workbook) | burn rate 14.4 over 1 h (with a 5-min check) | Consumes 2% of a 28-day budget in one hour |
| Slow-burn ticket | burn rate β 1β6 over 6 h β 3 days | β illustrative; catches gradual degradation |
| Pages per on-call shift that is sustainable | a few at most | Google's SRE book suggests β€ 2 incidents per 12-hour shift |
| Time to acknowledge target | β 5β15 min for pages | β illustrative; set by the team's policy |
Failure modes.
- Alert fatigue β too many noisy alerts, the real one is missed β delete or demote non-actionable alerts, SLO-based paging only.
- Silent failure β the metric stopped being reported, so the threshold never fires β alert on absent data, dead man's switch.
- Alerting stack down with the system β monitor the monitor from outside (synthetic checks, a separate provider).
- No runbook / wrong owner β each alert links a runbook and routes to the owning team.
- Flapping β longer evaluation windows,
fordurations, hysteresis.
In the playground. alerting is a side channel connected to metrics: when present, random failures are detected and fixed faster β the model reduces MTTR by 50%, which shows up as higher availability in the run report. It costs money, like the other observability nodes. Scenarios: observability-stack, multi-region.
Real products.
- Amazon CloudWatch alarms (with composite alarms) routed through Amazon SNS.
- AWS Incident Manager (Systems Manager) β on-call, escalation and response plans.
- Cloud Monitoring alerting policies β including SLO burn-rate alerts, with notification channels.
- Azure Monitor alerts with action groups (e-mail, SMS, webhooks, ITSM).
- Prometheus Alertmanager β grouping, silencing and routing of Prometheus alerts.
- Grafana Alerting β alert rules over any Grafana data source.
- Sloth / Pyrra β generate multi-window burn-rate alerts from SLO definitions.
- Commercial paging services such as PagerDuty are common for on-call schedules.
In the interview
"I page on symptoms, not causes: multi-window burn-rate alerts on the checkout SLO β 14Γ burn over an hour pages the on-call, slow burns open a ticket. Every alert has an owner and a runbook, and a heartbeat alert tells us if monitoring itself is down."
Learn more: Observability βΊ Alerting Β· Observability βΊ SLOs Β· Observability βΊ Incident response Β· Estimation βΊ Availability math Β· Hands-on 8
Health Check
What it is. A health check is a probe that something outside the instance β a load balancer, Kubernetes, a DNS failover service β sends periodically to decide whether that instance should receive traffic or be restarted. Kubernetes distinguishes three: liveness ("is the process stuck? if it fails, restart it"), readiness ("can it serve traffic right now? if it fails, take it out of the load balancer, don't restart it") and startup ("is it still booting? hold the other probes until it's done"). Load balancers use similar active checks (an HTTP request to /health every few seconds) and sometimes passive checks (outlier detection on real traffic errors).
When to use.
- Every replica behind a load balancer or a Kubernetes Service: without it, the LB keeps sending traffic to dead or warming-up instances.
- Readiness during startup and warm-up (JIT, caches, connection pools) and during graceful shutdown (fail readiness first, then drain).
- Regional failover: DNS health checks decide when to send users to the other region.
- Load shedding: an overloaded instance can fail readiness temporarily.
When not to (or watch out).
- Don't check dependencies in liveness: if the database is slow and every pod's liveness checks it, Kubernetes restarts all pods at once β a restart storm that makes the outage worse.
- Readiness that checks a shared dependency can take every replica out at once β check only what the instance itself needs, and degrade gracefully.
- Health endpoints must be cheap, fast and unauthenticated-but-not-public (no sensitive details).
- Too-aggressive thresholds flap; too-lax ones leave dead instances in rotation.
Key numbers.
| Metric | Typical value | Note |
|---|---|---|
| Kubernetes probe defaults | periodSeconds 10, timeoutSeconds 1, failureThreshold 3 | Documented defaults |
| Detection time | β interval Γ failure threshold | K8s defaults: β 30 s; 2 s Γ 2 β 4 s if tuned |
| AWS ALB target health check defaults | interval 30 s, unhealthy threshold 2, healthy threshold 5 | Documented defaults; β 60 s to mark unhealthy |
| Probe endpoint latency | β milliseconds | Must not do heavy work |
| Effect on availability | faster detection β fewer failed requests per incident | Failed requests β traffic share Γ detection time |
Failure modes.
- Restart storm β liveness depends on the database; DB slows; all pods restart β liveness only checks the process itself.
- Fleet-wide removal β readiness fails everywhere because of a shared dependency; the LB has no healthy targets β degrade instead of failing readiness; LBs that "fail open" when all targets are unhealthy.
- Zombie instance β health endpoint returns 200 but real requests fail (e.g. a broken thread pool) β passive health checks / outlier detection on real traffic, deeper readiness checks.
- Slow detection β shorter intervals for critical paths, plus passive checks.
- Flapping β healthy/unhealthy thresholds with hysteresis.
In the playground. healthcheck is a side channel: connecting it to a service behind a load balancer reduces the LB's failure-detection time from 30 s to 3 s, so after a replica crash far fewer requests are sent to the dead instance. Compare the error spike after a crash with and without it. Scenarios: scale-out, multi-region, observability-stack.
Real products.
- Elastic Load Balancing target health checks (ALB/NLB).
- Amazon Route 53 health checks β for DNS failover.
- Cloud Load Balancing health checks.
- Cloud Monitoring uptime checks β external synthetic probes.
- Azure Load Balancer health probes and Application Gateway health probes.
- Application Insights availability tests β external synthetic probes.
- Kubernetes liveness, readiness and startup probes (used in Hands-on 10).
- Spring Boot Actuator health groups (
/actuator/health/liveness,/actuator/health/readiness). - Prometheus Blackbox Exporter β HTTP/TCP/ICMP probes from outside.
- Envoy outlier detection β passive health checking in proxies and service meshes.
In the interview
"Each replica exposes liveness and readiness separately: liveness only says the process isn't stuck, readiness says it can serve now β it fails during warm-up and shutdown. The load balancer stops routing to a failed replica within seconds, and I never put a database check in liveness, or a slow database restarts the whole fleet."
Learn more: Observability βΊ Signals Β· Cloud-native βΊ Kubernetes Β· Reliability βΊ Patterns Β· Edge βΊ Load balancing Β· Load Balancer Β· Hands-on 3 Β· Hands-on 10