Skip to content
intermediatePhase 51 · High-Level Design Framework

Monitoring Design

Plan logging, metrics, alerting, and observability.

30m
0 problems
Topic Progress0%

Logging

The Three Pillars of Observability

Observability is the ability to understand the internal state of a system by examining its outputs. The three pillars are:

  • Logs: Discrete, timestamped events that record what happened
  • Metrics: Numerical measurements aggregated over time
  • Traces: End-to-end request flows across service boundaries

Together these give you full visibility. Logs tell you what went wrong, metrics tell you when and how much, and traces tell you where in the chain.

Structured Logging

Unstructured logs like User 123 logged in at 3pm are hard to query. Structured logs use a consistent format (usually JSON):

{
  "timestamp": "2026-08-16T14:23:01.123Z",
  "level": "INFO",
  "service": "auth-service",
  "traceId": "abc-123-def-456",
  "spanId": "span-789",
  "message": "User authenticated successfully",
  "userId": "user_42",
  "method": "POST",
  "path": "/api/login",
  "durationMs": 145,
  "statusCode": 200
}

Why structured logging matters:

  • Enables query by field: service:auth-service AND level:ERROR
  • Facilitates aggregation: average durationMs per endpoint
  • Supports correlation across services via traceId
  • Machine-parseable for automated alerting

Correlation IDs

A correlation ID (or trace ID) is a unique identifier assigned to a request at the edge of your system. Every log entry across all services includes this ID, allowing you to reconstruct the full journey of a single request.

API Gateway generates traceId: abc-123
  -> Auth Service logs: { traceId: abc-123, message: "Token validated" }
  -> Order Service logs: { traceId: abc-123, message: "Order created" }
  -> Payment Service logs: { traceId: abc-123, message: "Charge successful" }

Without correlation IDs, debugging a failing request across 5 microservices is nearly impossible.

Log Levels

Level When to Use Example
ERROR Something broke, needs immediate attention Database connection failed
WARN Something unexpected but not broken Retry attempt 2 of 3
INFO Normal business events Order placed
DEBUG Detailed diagnostic info Query result: 42 rows

Best practice: Production should run at INFO level. Enable DEBUG only when troubleshooting specific issues.

Log Retention and Storage

  • Hot storage (0-7 days): Fast query, expensive (e.g., Elasticsearch)
  • Warm storage (7-30 days): Slower query, cheaper (e.g., S3 with Athena)
  • Cold storage (30+ days): Archive, cheapest (e.g., S3 Glacier)

Cost grows linearly with log volume. A service processing 10K requests/sec generates ~1TB of logs per day. Retention policies are critical for cost control.

Metrics & Dashboards

The RED Method

The RED method is a framework for monitoring microservices, focused on the request-driven nature of services:

  • Rate: Number of requests per second (traffic volume)
  • Errors: Number of failed requests per second (error rate)
  • Duration: Distribution of request latencies (latency)

These three metrics capture the most important signals. If a service is healthy, these metrics look normal. If any of them deviates, you have a problem.

The USE Method

For infrastructure (servers, databases, queues), use the USE method:

  • Utilization: Percentage of resource in use (CPU at 85%)
  • Saturation: Queue depth or backlog (1000 pending requests)
  • Errors: Count of error events (disk read errors)

Key Infrastructure Metrics

Resource Utilization Saturation Errors
CPU % usage Run queue length Hardware errors
Memory % used Swap usage OOM kills
Disk IOPS usage I/O wait queue Read/write errors
Network Bandwidth usage TCP retransmits Packet drops

Prometheus + Grafana

Prometheus scrapes metrics from instrumented services at regular intervals (e.g., every 15s). Metrics are stored in a time-series database.

Service instrumentation (Go example):

import "github.com/prometheus/client_golang/prometheus"

var requestDuration = prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "Duration of HTTP requests",
        Buckets: []float64{.01, .05, .1, .25, .5, 1, 2.5, 5, 10},
    },
    []string{"method", "path", "status"},
)

// In handler:
requestDuration.WithLabelValues("POST", "/api/orders", "200").Observe(duration.Seconds())

PromQL queries:

# 99th percentile latency over 5 minutes
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

# Error rate: 5xx responses / total responses
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

# Requests per second by service
sum(rate(http_requests_total[5m])) by (service)

Grafana provides dashboards. Key panels:

  • Request rate over time
  • Error rate percentage
  • Latency distribution (p50, p95, p99)
  • Saturation indicators (queue depth, connection pool usage)

Alerting

Alert rules in Prometheus:

groups:
  - name: service-alerts
    rules:
      - alert: HighErrorRate
        expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Error rate above 5% for 5 minutes"

Escalation pipeline:

  1. Alert fires in Prometheus/AlertManager
  2. PagerDuty receives alert, pages on-call engineer
  3. If unacknowledged in 15 minutes, escalates to secondary
  4. If unacknowledged in 30 minutes, escalates to engineering manager

On-call rotation: Engineers rotate weekly. Each week has a primary and secondary on-call. Handoff includes a written summary of ongoing issues.

Health Checks

Liveness probe: "Is the process alive?" If this fails, the orchestrator (Kubernetes) restarts the container. Use for detecting deadlocks or infinite loops.

livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
  failureThreshold: 3

Readiness probe: "Can the process accept traffic?" If this fails, the orchestrator removes the pod from the load balancer but does NOT restart it. Use for detecting dependency failures (database down, cache cold).

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 3
  failureThreshold: 2

Critical difference: If your readiness probe checks database connectivity and the DB is temporarily down, you don't want to restart every pod (that would make things worse). Readiness fails, traffic stops, pods stay alive and recover when DB returns.

Distributed Tracing

Why Distributed Tracing?

In a monolith, a stack trace tells you what went wrong. In microservices, a single user request may touch 10-20 services. You need to track the request across all of them.

A distributed trace is a tree of spans. Each span represents one unit of work (an HTTP call, a DB query, a cache lookup).

[API Gateway] traceId=abc-123
  |-- [Auth Service] spanId=span-1, duration=15ms
  |-- [User Service] spanId=span-2, duration=45ms
       |-- [PostgreSQL] spanId=span-3, duration=30ms
  |-- [Order Service] spanId=span-4, duration=120ms
       |-- [Redis] spanId=span-5, duration=2ms
       |-- [Payment Service] spanId=span-6, duration=80ms
       |-- [Inventory Service] spanId=span-7, duration=25ms

From this trace, you can see the order service took 120ms total, with 80ms spent waiting for payment and 2ms for cache.

OpenTelemetry

OpenTelemetry (OTel) is the CNCF standard for instrumentation. It provides:

  • APIs for generating traces, metrics, and logs
  • SDKs for auto-instrumentation in most languages
  • Exporters to send data to backends (Jaeger, Zipkin, Datadog, AWS X-Ray)

Auto-instrumentation (Python example):

from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

# Setup
provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="otel-collector:4317"))
provider.add_span_processor(processor)

# Auto-instrument Flask
FlaskInstrumentor().instrument_app(app)

Manual span creation:

from opentelemetry import trace

tracer = trace.get_tracer("order-service")

with tracer.start_as_current_span("process-payment") as span:
    span.set_attribute("payment.method", "credit_card")
    span.set_attribute("payment.amount", 99.99)
    # ... payment logic

Trace Backends

Backend Type Best For
Jaeger OSS, self-hosted Full control, CNCF native
AWS X-Ray Managed AWS-centric workloads
Datadog APM SaaS All-in-one observability
Honeycomb SaaS High-cardinality analysis

Sampling Strategies

Tracing generates enormous volume. Sampling controls what you keep:

  • Head-based: Decide at the start of a request. Simple, but may miss interesting traces.
  • Tail-based: Collect all traces, decide later which to keep. Higher fidelity, but requires more storage.
  • Adaptive: Sample more aggressively during normal traffic, less during anomalies.

Production tip: Sample 1% of normal requests, 100% of error requests.

Real-World Monitoring Architecture

For a microservices architecture serving 10K req/sec:

  1. Instrumentation layer: OpenTelemetry SDK in each service
  2. Collection layer: OTel Collector (agent per node) + OTel Collector (gateway)
  3. Storage layer: Prometheus (metrics), Elasticsearch (logs), Jaeger (traces)
  4. Visualization: Grafana (metrics), Kibana (logs), Jaeger UI (traces)
  5. Alerting: Prometheus AlertManager -> PagerDuty
  6. Correlation: All three pillars share traceId, enabling cross-signal investigation

Cost estimation (rough): At 10K req/sec, expect ~$2-5K/month for observability infrastructure (excluding SaaS tools).

Practice Problems

0/3solved
Design Monitoring & Observability System

Design a scalable Monitoring & Observability system. Cover high-level architecture, data model, and API design.

Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliability
Monitoring & Observability Scaling

How would you scale Monitoring & Observability to handle 10x the current load? Identify bottlenecks and solutions.

Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decomposition
Monitoring & Observability Failure Modes

Analyze potential failure modes for Monitoring & Observability and design mitigation strategies.

Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradation

Quiz

1. What is the primary difference between a liveness probe and a readiness probe?

Question 1 options

2. In the RED method, what does 'E' stand for?

Question 2 options

3. Why are correlation IDs critical in a microservices architecture?

Question 3 options

4. What is the main advantage of structured logging over unstructured logging?

Question 4 options

5. In OpenTelemetry, what is the role of the OTel Collector?

Question 5 options

Flashcards

Question

What are the three pillars of observability?

Answer

Logs (discrete events), Metrics (numerical measurements over time), and Traces (end-to-end request flows across services).

Question

RED method acronym?

Answer

Rate (requests/sec), Errors (failed requests/sec), Duration (latency distribution). Used for monitoring microservices.

Question

USE method acronym?

Answer

Utilization (% resource in use), Saturation (queue depth/backlog), Errors (error events). Used for infrastructure monitoring.

Question

Liveness vs Readiness probe?

Answer

Liveness: 'Is the process alive?' — failure triggers restart. Readiness: 'Can it accept traffic?' — failure removes from load balancer but does NOT restart.

Question

What is a distributed trace composed of?

Answer

A tree of spans. Each span represents one unit of work (HTTP call, DB query, cache lookup). The root span is the entire request; child spans represent downstream calls.

Question

What is OpenTelemetry?

Answer

A CNCF standard providing APIs, SDKs, and exporters for generating and exporting traces, metrics, and logs. Supports auto-instrumentation for most languages and exports to multiple backends.

Question

What is head-based vs tail-based sampling?

Answer

Head-based: Decide to sample at the start of a request (simple, may miss anomalies). Tail-based: Collect all traces, decide later which to keep (higher fidelity, more storage).

Revision Notes

Key Takeaways

  • 1.Observability has three pillars: logs, metrics, and traces — you need all three
  • 2.Structured logs (JSON) with correlation IDs are essential for debugging across microservices
  • 3.RED method for services, USE method for infrastructure — know when to use each
  • 4.Liveness probes restart containers; readiness probes stop traffic — different failure modes
  • 5.OpenTelemetry is the standard — learn it, don't vendor-lock to one tool
  • 6.Sample aggressively in production — 1% of normal traffic, 100% of errors

Interview Tips

  • When designing a system, always mention monitoring: 'I'd instrument this with Prometheus for metrics, structured logs with correlation IDs, and OpenTelemetry for tracing'
  • Explain the difference between liveness and readiness probes — interviewers love this detail
  • Mention alerting with escalation: 'Alert fires → PagerDuty → on-call → escalation if unacknowledged'
  • For cost-aware designs, mention log retention tiers: hot (7d) → warm (30d) → cold (archive)
  • If asked about debugging a production issue, walk through: check dashboards → query logs by traceId → examine the distributed trace

Cheat Sheet

Monitoring & Observability Cheat Sheet

Three Pillars: Logs + Metrics + Traces

Structured Logging:

  • JSON format with fields: timestamp, level, service, traceId, message
  • Correlation ID (traceId) propagated across all services
  • Log levels: ERROR > WARN > INFO > DEBUG

RED Method (services):

  • Rate: requests/sec
  • Errors: failed requests/sec
  • Duration: latency distribution (p50, p95, p99)

USE Method (infra):

  • Utilization: % resource in use
  • Saturation: queue depth
  • Errors: error events

Prometheus + Grafana:

  • Prometheus scrapes metrics every 15s
  • PromQL for queries: histogram_quantile, rate, sum
  • Grafana for dashboards

Health Checks:

  • Liveness: dead? → restart container
  • Readiness: healthy enough for traffic? → remove from LB

OpenTelemetry:

  • CNCF standard for traces, metrics, logs
  • Auto-instrumentation + manual spans
  • Exports to Jaeger, X-Ray, Datadog, etc.

Alerting Pipeline:
Prometheus AlertManager → PagerDuty → On-call rotation → Escalation