← Backend← Backend
BackendBackend19 Th7, 2026Jul 19, 202619 phút đọc15 min read

Observability & MonitoringObservability & Monitoring

Thuộc bộ kiến thức Backend Roadmap.

Tổng quan

Khi một backend service rời khỏi máy của bạn và bắt đầu nhận traffic thật, câu hỏi quan trọng nhất không còn là “code có chạy không?” mà trở thành “nó đang làm gì ngay lúc này, và tại sao?”. Monitoring trả lời liệu hệ thống có khỏe mạnh hay không dựa trên những câu hỏi bạn đã quyết định đặt ra từ trước (CPU, error rate, request latency), và báo động (page) cho ai đó khi một ngưỡng bị vượt qua. Observability là một thuộc tính rộng hơn: mức độ mà telemetry do hệ thống phát ra cho phép bạn giải thích bất kỳ trạng thái nội bộ nào — kể cả những lỗi không ai lường trước — mà không cần deploy code mới chỉ để thêm một dòng print. Monitoring là một tập con của những gì observability tốt cho phép làm.

Sự phân biệt này ánh xạ rõ ràng với known-unknowns vs unknown-unknowns. Known-unknown là lỗi bạn đã lường trước (“database có thể chậm”) — bạn dựng một dashboard và một alert cho nó. Unknown-unknown là lỗi bạn chưa từng tưởng tượng (“checkout chậm chỉ với user Android trên app version 4.2 ở eu-west, và chỉ khi cache còn lạnh”). Monitoring không giúp được ở đây vì không ai vẽ biểu đồ đó từ trước. Observability thì có thể — nhưng chỉ khi telemetry của bạn mang đủ nhiều chiều (dimension) như user, region, version, endpoint, cache state để bạn có thể cắt lát (slice) theo chúng một cách ad hoc ngay trong lúc sự cố. Trong một backend microservice hay serverless hiện đại, nơi một request tỏa ra hàng chục service, unknown-unknowns chiếm ưu thế — đó là lý do observability đã trở thành một kỹ năng cốt lõi của backend chứ không còn là chuyện phụ của ops.

Note này tập trung vào góc nhìn của backend engineer: cách instrument service của bạn, cần emit những gì, và cách suy luận trong lúc sự cố. Về phía platform/hạ tầng — vận hành Prometheus ở quy mô lớn, monitoring Kubernetes, topology alerting cấp cluster — xem note đồng hành ../devops/vi/15-monitoring-and-observability.md. Observability cũng chính là vòng phản hồi (feedback loop) giúp các kỹ thuật reliability và scaling trong ./17-scalability-and-reliability.md trở nên đo lường được: bạn không thể quản lý thứ mình không thấy được. Nó bổ trợ cho các đảm bảo về tính đúng đắn mà bạn xây bằng ./13-testing.md (test bắt các bug đã biết trước khi deploy; observability bắt mọi thứ còn lại sau khi deploy).

Kiến thức nền tảng

Ba trụ cột: metrics, logs, traces

Mô hình tư duy kinh điển của observability là ba trụ cột (three pillars) — metrics, logs, và traces — thường được mở rộng thêm events (deploy, bật/tắt feature flag, thay đổi config) để trả lời “cái gì đã thay đổi?”. Mỗi trụ cột giỏi ở một câu hỏi khác nhau, và các team trưởng thành tương quan (correlate) chúng với nhau: một alert kích hoạt trên một metric, bạn nhảy sang một trace mẫu để tìm chỗ nào tốn thời gian, rồi chuyển sang logs của span bị lỗi để biết tại sao.

Trụ cộtLà gìTốt nhất choChi phí / lưu ýTrả lời
MetricsChuỗi thời gian (time series) dạng số (counter, gauge, histogram) kèm labelAlerting, xu hướng, dashboard, SLORẻ & nhanh, nhưng đã pre-aggregated — không có chi tiết từng request; giới hạn cardinality của label gì đó sai không?
LogsBản ghi sự kiện có timestamp, lý tưởng là structured (JSON)Điều tra pháp y (forensics), chi tiết đầy đủ, debug một request cụ thểĐắt khi volume lớn; nhiễu; khó aggregate nếu không indexTại sao sai?
TracesHành trình của một request qua các service dưới dạng cây các span có tính thời gianBóc tách latency, tìm service chậm/lỗi qua ranh giớiCần instrumentation; thường bị sampling; storage phình nhanhSai ở đâu?
EventsCác thay đổi rời rạc: deploy, config, flag, scalingTương quan tìm nguyên nhân gốc (“cái gì thay đổi ngay trước đó?”)Chỉ hữu ích nếu được ghi lại và canh thời gian với các signal khácCái gì đã thay đổi?

Một cách nhìn hiện đại hữu ích (từ cộng đồng “observability 2.0”) là coi wide, structured events (sự kiện rộng, có cấu trúc) như primitive hợp nhất: emit một event được chú thích phong phú cho mỗi đơn vị công việc, mang hàng chục field, rồi dẫn xuất metrics, traces, logs từ nó. Dù bạn theo mô hình đó hay ba trụ cột kinh điển, nguyên tắc vẫn giống nhau — nắm bắt context, giữ cardinality có thể query được, tương quan giữa các signal.

OpenTelemetry: chuẩn cho instrumentation

OpenTelemetry (OTel) là chuẩn trung lập với vendor của CNCF để tạo và xuất telemetry. Nó quan trọng vì tách rời instrumentation (nằm trong code của bạn) khỏi backend lưu trữ và query telemetry (Prometheus, Jaeger, Tempo, Loki, Datadog, v.v.). Instrument một lần bằng OTel API, và bạn có thể đổi vendor observability chỉ bằng cách thay đổi config của exporter — không cần sửa code. Nó định nghĩa:

Auto-instrumentation móc vào các library phổ biến (HTTP server, DB driver, gRPC client) để phát span và metric mà không cần sửa code — cách nhanh nhất để có độ phủ cơ bản. Manual instrumentation thêm span, attribute, và metric mang tính nghiệp vụ mà auto-instrumentation không thể biết (“đây có phải khách quay lại không?”, “giỏ hàng có bao nhiêu món?”). Hệ thống thực tế dùng cả hai: auto cho độ rộng, manual cho các chi tiết quan trọng về nghiệp vụ.

SLIs, SLOs, và error budgets

Reliability cần một định nghĩa, nếu không “đáng tin cậy” chỉ là ý kiến chủ quan. Framework SRE của Google đưa ra ba khái niệm liên kết:

SLA (Service Level Agreement) là cam kết hợp đồng, đối mặt với khách hàng, kèm phạt tài chính — luôn lỏng hơn SLO nội bộ của bạn để bạn có khoảng đệm phản ứng trước khi vi phạm hợp đồng.

Four golden signals (bốn tín hiệu vàng)

Từ cuốn Google SRE, nếu chỉ đo được bốn thứ cho một hệ thống hướng người dùng, hãy đo những thứ này:

SignalÝ nghĩaMetric điển hình
LatencyMột request mất bao lâu — tách latency thành công vs lỗi (một lỗi 500 nhanh không nên trông “tốt”)p50 / p95 / p99 request duration
TrafficNhu cầu bao nhiêurequests/sec, connections, messages/sec
ErrorsTỉ lệ request thất bại (5xx tường minh, nội dung sai ngầm định, hoặc theo policy — quá chậm)error ratio
SaturationService “đầy” tới mức nào — tài nguyên nào gần chạm giới hạn nhấtCPU %, memory, queue depth, connection pool usage

Phương pháp RED (Rate, Errors, Duration) là tập con hướng request, lý tưởng cho service và API. Phương pháp USE (Utilization, Saturation, Errors) là phần bổ trợ hướng tài nguyên, lý tưởng cho hạ tầng (CPU, disk, network). Dùng RED cho các endpoint của bạn và USE cho máy móc, queue bên dưới.

Khái niệm chính

Metrics và Prometheus

Prometheus là hệ thống metrics mã nguồn mở de-facto (CNCF graduated). Các ý chính:

Các loại metric:

LoạiHành viDùng choVí dụ
CounterChỉ tăng đơn điệu (reset về 0 khi restart)Đếm số lượng — request, error, bytehttp_requests_total — luôn vẽ bằng rate()
GaugeLên và xuốngMột giá trị hiện tại — nhiệt độ, queue depth, request đang xử lýqueue_depth, memory_bytes
HistogramCác bucket quan sát + sum + countPhân bố latency/kích thước; tính được quantilehttp_request_duration_seconds_bucket
SummaryQuantile tính ở phía clientQuantile khi không thể aggregate qua nhiều instancerpc_duration_seconds{quantile="0.99"}

Ưu tiên histogram hơn summary cho latency: bucket của histogram aggregate được qua nhiều instance (bạn tính được p99 toàn fleet), trong khi quantile của summary là theo từng instance và không thể lấy trung bình một cách có ý nghĩa.

Một metric được expose trên /metrics:

# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="POST",route="/checkout",status="200"} 24571
http_requests_total{method="POST",route="/checkout",status="500"} 37

Một query PromQL tính error ratio trong 5 phút gần nhất:

sum(rate(http_requests_total{status=~"5..", route="/checkout"}[5m]))
/
sum(rate(http_requests_total{route="/checkout"}[5m]))

Cardinality — cạm bẫy số một của metrics. Mỗi tổ hợp giá trị label duy nhất là một time series riêng biệt mà Prometheus phải lưu và index. Đưa một giá trị không bị chặn (user ID, email, request ID, URL đầy đủ kèm query param) vào label sẽ gây cardinality explosion có thể làm OOM Prometheus. Nguyên tắc: label phải là chiều có số lượng bị chặn, cardinality thấp (method, route template, status code, region). Các định danh cardinality cao thuộc về traces và logs, không phải label của metric.

# XẤU — user_id và path không bị chặn → hàng triệu series
http_requests_total{user_id="8f3a...", path="/users/8f3a/orders/91?ref=x"}

# TỐT — label bị chặn; route là template, không phải path cụ thể
http_requests_total{route="/users/:id/orders/:oid", status="200"}

Logging

Một log là bản ghi có timestamp của một sự kiện rời rạc. Nâng cấp có tác động lớn nhất cho một backend là structured logging — emit JSON (hoặc định dạng máy đọc được khác) với tên field nhất quán thay vì chuỗi văn bản tự do. Structured log query được và aggregate được; grep trên văn bản thô thì không.

{
  "timestamp": "2026-07-19T10:32:11.482Z",
  "level": "ERROR",
  "service": "checkout-api",
  "message": "payment authorization failed",
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "span_id": "00f067aa0ba902b7",
  "user_id": "u_8f3a91",
  "order_id": "o_44821",
  "payment_provider": "stripe",
  "error_code": "card_declined",
  "duration_ms": 812
}

Log levels — dùng chúng một cách nhất quán:

LevelKhi nàoCó page không?
ERRORMột thao tác thất bại và cần chú ýThường có
WARNĐiều gì đó bất thường nhưng đã xử lý được (retry, fallback)Đôi khi
INFOSự kiện quan trọng bình thường (khởi động, request hoàn tất)Không
DEBUGThông tin chẩn đoán chi tiết, thường tắt ở prodKhông

Correlation ID. Để tái dựng hành trình của một request qua các service, hãy truyền (propagate) một định danh dùng chung — lý tưởng là trace_id từ hệ thống tracing — qua mọi dòng log và mọi cuộc gọi downstream (qua một HTTP header). Trong lúc sự cố, lọc toàn bộ log theo một trace_id sẽ ráp lại toàn bộ câu chuyện qua các ranh giới service.

Centralized logging. Chuyển log ra khỏi host vào một kho có thể tìm kiếm: ELK/Elastic Stack (Elasticsearch + Logstash/Beats + Kibana) là lựa chọn kinh điển, nặng ký; Grafana Loki là phương án nhẹ hơn, rẻ hơn, chỉ index label và lưu phần thân log ở dạng nén (kết hợp tự nhiên với Prometheus + Grafana). Sampling kiểm soát chi phí: giữ 100% log ERROR nhưng sample các log INFO/DEBUG volume cao (ví dụ 1%), và cân nhắc tail-based sampling để luôn giữ log cho các request bị lỗi hoặc chậm.

Distributed tracing

Một trace biểu diễn hành trình đầu-cuối của một request qua hệ thống phân tán, mô hình hóa thành cây các span. Mỗi span là một đơn vị công việc có tên (một HTTP handler, một DB query, một cache lookup) với thời điểm bắt đầu, duration, một trace_id (dùng chung cả trace), một span_id, một parent_span_id, một status, và các attribute (tag key/value như http.status_code, db.statement).

Context propagation là cơ chế ghép các span qua ranh giới tiến trình: bên gọi inject trace context vào request đi ra (header HTTP W3C traceparent là chuẩn), và bên nhận extract nó để biến span của mình thành con của span bên gọi. Không có propagation thì bạn chỉ có các mảnh rời rạc thay vì một trace mạch lạc.

traceparent
00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
  • 00version
  • 4bf92f3577b34da6a3ce929d0e0e4736trace-id (16 bytes)
  • 00f067aa0ba902b7parent-span-id
  • 01flags (sampled)

Tạo span thủ công với OpenTelemetry (Python):

from opentelemetry import trace

tracer = trace.get_tracer("checkout-api")

def process_checkout(order):
    with tracer.start_as_current_span("process_checkout") as span:
        span.set_attribute("order.id", order.id)
        span.set_attribute("order.item_count", len(order.items))
        try:
            charge = charge_payment(order)          # span con, auto-instrumented
            span.set_attribute("payment.provider", charge.provider)
        except PaymentError as e:
            span.record_exception(e)
            span.set_status(trace.Status(trace.StatusCode.ERROR))
            raise

Backend: JaegerGrafana Tempo là các kho trace mã nguồn mở phổ biến; Zipkin là bậc tiền bối cũ hơn. Tất cả đều nhận OTLP từ OpenTelemetry. Vì lưu mọi span thì đắt, trace được samplinghead-based (quyết định ngay từ đầu, ví dụ giữ 5%, rẻ nhưng có thể bỏ sót lỗi hiếm) hoặc tail-based (buffer cả trace rồi giữ lại nếu nó lỗi hoặc chậm — giá trị hơn, tốn hạ tầng hơn). Exemplars liên kết một đỉnh (spike) trên histogram metric trực tiếp tới một trace ví dụ, cho bạn cú nhảy một-cú-click từ “p99 tăng” tới “đây là một request chậm”.

Dashboard, alerting, và on-call

APM và công cụ thương mại

Các nền tảng APM (Application Performance Monitoring) gói metrics, logs, traces, dashboard, và alerting vào một sản phẩm tích hợp với agent auto-instrumentation:

Công cụGhi chú
DatadogSaaS all-in-one rộng, bóng bẩy; tích hợp mạnh; giá theo usage có thể leo thang
New RelicAPM lâu đời; giá theo mức tiêu thụ
DynatraceHướng enterprise; mapping phụ thuộc tự động mạnh và root cause hỗ trợ AI
Grafana Cloud / ElasticBản managed hosting của các stack mã nguồn mở
HoneycombTiên phong “observability 2.0” dựa trên event cardinality cao và khám phá ad-hoc nhanh

Đánh đổi: APM thương mại tiết kiệm khổng lồ công sức thiết lập và tích hợp nhưng tạo ra vendor lock-in và chi phí tăng theo volume dữ liệu. Vì OpenTelemetry trung lập với vendor, instrument bằng OTel giữ cho lối thoát của bạn luôn mở dù bạn bắt đầu với backend nào.

Continuous profiling

Metrics/logs/traces cho biết một function hay service chậm; profiling cho biết dòng code nào đốt CPU hay cấp phát memory. Continuous profiling (Grafana Pyroscope, Parca, Datadog Profiler, Google Cloud Profiler) lấy mẫu stack production với overhead thấp (thường vài phần trăm) và lưu chúng dưới dạng flame graph có timestamp, cho phép bạn so sánh profile CPU/heap/lock qua các version và theo thời gian — ví dụ xác nhận một bản deploy làm tăng CPU và thấy chính xác function nào chịu trách nhiệm, tất cả từ production, không cần tái hiện ở local.

Best Practices

Tài liệu tham khảo

Part of the Backend Roadmap knowledge base.

Overview

Once a backend service leaves your laptop and starts taking real traffic, the most important question stops being “does the code work?” and becomes “what is it doing right now, and why?” Monitoring answers whether the system is healthy against questions you decided to ask in advance (CPU, error rate, request latency), and pages someone when a threshold is crossed. Observability is a broader property: how well the telemetry a system emits lets you explain any internal state — including failures nobody predicted — without shipping new code to add a print statement. Monitoring is a subset of what good observability enables.

The distinction maps cleanly onto known-unknowns vs unknown-unknowns. A known-unknown is a failure you anticipated (“the database might get slow”) — you build a dashboard and an alert for it. An unknown-unknown is the failure you never imagined (“checkout is slow only for Android users on app version 4.2 in eu-west, but only when the cache is cold”). Monitoring cannot help you here because nobody drew that graph in advance. Observability can, if — and only if — your telemetry carries enough dimensions (user, region, version, endpoint, cache state) that you can slice across them ad hoc during an incident. In a modern microservice or serverless backend where one request fans out across dozens of services, unknown-unknowns dominate, which is why observability has become a core backend skill rather than an ops afterthought.

This note focuses on the backend engineer’s view: how to instrument your services, what to emit, and how to reason during an incident. For the platform/infrastructure side — running Prometheus at scale, Kubernetes monitoring, cluster-level alerting topology — see the companion note ../devops/en/15-monitoring-and-observability.md. Observability is also the feedback loop that makes the reliability and scaling techniques in ./17-scalability-and-reliability.md measurable: you cannot manage what you cannot see, and it complements the correctness guarantees you build with ./13-testing.md (tests catch known bugs before deploy; observability catches everything else after).

Fundamentals

The three pillars: metrics, logs, traces

The classic mental model of observability is the three pillars — metrics, logs, and traces — often extended with events (deploys, feature-flag flips, config changes) that answer “what changed?”. Each pillar is best at a different question, and mature teams correlate them: an alert fires on a metric, you jump to an exemplar trace to find where the time went, then pivot to the logs of the failing span to learn why.

PillarWhat it isBest forCost / caveatsAnswers
MetricsNumeric time series (counter, gauge, histogram) with labelsAlerting, trends, dashboards, SLOsCheap & fast, but pre-aggregated — no per-request detail; label cardinality limitsIs something wrong?
LogsTimestamped event records, ideally structured (JSON)Forensics, full detail, debugging a specific requestExpensive at volume; noisy; hard to aggregate without indexingWhy is it wrong?
TracesOne request’s path across services as a tree of timed spansLatency breakdown, finding the slow/failing service across boundariesNeeds instrumentation; usually sampled; storage grows fastWhere is it wrong?
EventsDiscrete changes: deploy, config, flag, scalingRoot-cause correlation (“what changed right before?”)Only useful if recorded and time-aligned with the other signalsWhat changed?

A useful modern reframing (from the “observability 2.0” community) is to treat wide, structured events as the unifying primitive: emit one richly-annotated event per unit of work carrying dozens of fields, then derive metrics, traces, and logs from it. Whether you adopt that or the classic three pillars, the principle is identical — capture context, keep cardinality queryable, correlate across signals.

OpenTelemetry: the standard for instrumentation

OpenTelemetry (OTel) is the CNCF vendor-neutral standard for generating and exporting telemetry. It matters because it decouples instrumentation (which lives in your code) from the backend that stores and queries it (Prometheus, Jaeger, Tempo, Loki, Datadog, etc.). Instrument once with OTel APIs, and you can switch observability vendors by changing an exporter config — no code changes. It defines:

Auto-instrumentation hooks common libraries (HTTP servers, DB drivers, gRPC clients) to emit spans and metrics with zero code changes — the fastest way to get baseline coverage. Manual instrumentation adds domain-specific spans, attributes, and metrics that auto-instrumentation can’t know about (“was this a returning customer?”, “how many items in the cart?”). Real systems use both: auto for breadth, manual for the business-critical detail.

SLIs, SLOs, and error budgets

Reliability needs a definition, or “reliable” is just an opinion. The Google SRE framework gives three linked concepts:

An SLA (Service Level Agreement) is the contractual, customer-facing promise with financial penalties — always looser than your internal SLO so you have room to react before breaching the contract.

The four golden signals

From the Google SRE book, if you can only measure four things for a user-facing system, measure these:

SignalMeaningTypical metric
LatencyHow long a request takes — split success vs error latency (a fast 500 shouldn’t look “good”)p50 / p95 / p99 request duration
TrafficHow much demandrequests/sec, connections, messages/sec
ErrorsRate of failed requests (explicit 5xx, implicit wrong content, or policy — too slow)error ratio
SaturationHow “full” the service is — the resource nearest its limitCPU %, memory, queue depth, connection pool usage

The RED method (Rate, Errors, Duration) is the request-centric subset ideal for services and APIs. The USE method (Utilization, Saturation, Errors) is the resource-centric complement ideal for infrastructure (CPU, disk, network). Use RED for your endpoints and USE for the machines and queues underneath them.

Key Concepts

Metrics and Prometheus

Prometheus is the de-facto open-source metrics system (CNCF graduated). Key ideas:

Metric types:

TypeBehaviorUse forExample
CounterMonotonically increasing (resets to 0 on restart)Counts of things — requests, errors, byteshttp_requests_total — always graph with rate()
GaugeGoes up and downA current value — temperature, queue depth, in-flight requestsqueue_depth, memory_bytes
HistogramBuckets of observations + sum + countLatency/size distributions; enables quantileshttp_request_duration_seconds_bucket
SummaryClient-side computed quantilesQuantiles when you can’t aggregate across instancesrpc_duration_seconds{quantile="0.99"}

Prefer histograms over summaries for latency: histogram buckets are aggregatable across instances (you can compute a fleet-wide p99), whereas summary quantiles are per-instance and cannot be meaningfully averaged.

A metric exposed on /metrics:

# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="POST",route="/checkout",status="200"} 24571
http_requests_total{method="POST",route="/checkout",status="500"} 37

A PromQL query for the error ratio over the last 5 minutes:

sum(rate(http_requests_total{status=~"5..", route="/checkout"}[5m]))
/
sum(rate(http_requests_total{route="/checkout"}[5m]))

Cardinality — the number-one metrics pitfall. Each unique combination of label values is a separate time series that Prometheus must store and index. Putting an unbounded value (user ID, email, request ID, full URL with query params) into a label causes a cardinality explosion that can OOM your Prometheus. Rule: labels must be bounded, low-cardinality dimensions (method, route template, status code, region). High-cardinality identifiers belong in traces and logs, not metric labels.

# BAD — user_id and path are unbounded → millions of series
http_requests_total{user_id="8f3a...", path="/users/8f3a/orders/91?ref=x"}

# GOOD — bounded labels; the route is a template, not the concrete path
http_requests_total{route="/users/:id/orders/:oid", status="200"}

Logging

A log is a timestamped record of a discrete event. The single most impactful upgrade for a backend is structured logging — emit JSON (or another machine-parseable format) with consistent field names instead of free-text strings. Structured logs are queryable and aggregatable; grep on prose is not.

{
  "timestamp": "2026-07-19T10:32:11.482Z",
  "level": "ERROR",
  "service": "checkout-api",
  "message": "payment authorization failed",
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "span_id": "00f067aa0ba902b7",
  "user_id": "u_8f3a91",
  "order_id": "o_44821",
  "payment_provider": "stripe",
  "error_code": "card_declined",
  "duration_ms": 812
}

Log levels — use them consistently:

LevelWhenPaged on?
ERRORAn operation failed and needs attentionOften
WARNSomething unexpected but handled (retry, fallback)Sometimes
INFONormal significant events (startup, request completed)No
DEBUGDetailed diagnostic info, usually off in prodNo

Correlation IDs. To reconstruct one request’s journey across services, propagate a shared identifier — ideally the trace_id from your tracing system — through every log line and every downstream call (via an HTTP header). During an incident, filtering all logs by a single trace_id reassembles the full story across service boundaries.

Centralized logging. Ship logs off the host into a searchable store: the ELK/Elastic Stack (Elasticsearch + Logstash/Beats + Kibana) is the classic heavyweight; Grafana Loki is a lighter, cheaper alternative that indexes only labels and stores the log body compressed (pairs naturally with Prometheus + Grafana). Sampling controls cost: keep 100% of ERROR logs but sample high-volume INFO/DEBUG (e.g., 1%), and consider tail-based sampling to always keep logs for requests that errored or were slow.

Distributed tracing

A trace represents one request’s end-to-end journey through a distributed system, modeled as a tree of spans. Each span is a single named unit of work (an HTTP handler, a DB query, a cache lookup) with a start time, duration, a trace_id (shared across the whole trace), a span_id, a parent_span_id, a status, and attributes (key/value tags like http.status_code, db.statement).

Context propagation is the mechanism that stitches spans across process boundaries: the caller injects the trace context into outbound requests (the W3C traceparent HTTP header is the standard), and the callee extracts it to make its spans children of the caller’s. Without propagation you get disconnected fragments instead of one coherent trace.

traceparent
00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
  • 00version
  • 4bf92f3577b34da6a3ce929d0e0e4736trace-id (16 bytes)
  • 00f067aa0ba902b7parent-span-id
  • 01flags (sampled)

Manual span creation with OpenTelemetry (Python):

from opentelemetry import trace

tracer = trace.get_tracer("checkout-api")

def process_checkout(order):
    with tracer.start_as_current_span("process_checkout") as span:
        span.set_attribute("order.id", order.id)
        span.set_attribute("order.item_count", len(order.items))
        try:
            charge = charge_payment(order)          # child span, auto-instrumented
            span.set_attribute("payment.provider", charge.provider)
        except PaymentError as e:
            span.record_exception(e)
            span.set_status(trace.Status(trace.StatusCode.ERROR))
            raise

Backends: Jaeger and Grafana Tempo are the common open-source trace stores; Zipkin is the older pioneer. All ingest OTLP from OpenTelemetry. Because storing every span is expensive, traces are sampledhead-based (decide at the start, e.g. keep 5%, cheap but may miss the rare error) or tail-based (buffer the whole trace, then keep it if it errored or was slow — more valuable, more infra). Exemplars link a spike on a metrics histogram directly to an example trace, giving you a one-click jump from “p99 is up” to “here is a slow request.”

Dashboards, alerting, and on-call

APM and commercial tools

APM (Application Performance Monitoring) platforms bundle metrics, logs, traces, dashboards, and alerting into one integrated product with auto-instrumentation agents:

ToolNote
DatadogBroad, polished all-in-one SaaS; strong integrations; usage-based pricing can escalate
New RelicLong-standing APM; consumption-based pricing
DynatraceEnterprise focus; strong automatic dependency mapping and AI-assisted root cause
Grafana Cloud / ElasticManaged hosting of the open-source stacks
HoneycombPioneered high-cardinality, event-based “observability 2.0” and fast ad-hoc exploration

Trade-off: commercial APMs save enormous setup and integration effort but create vendor lock-in and cost that scales with data volume. Because OpenTelemetry is vendor-neutral, instrumenting with OTel keeps your exit options open regardless of which backend you start on.

Continuous profiling

Metrics/logs/traces tell you a function or service is slow; profiling tells you which lines of code burn the CPU or allocate the memory. Continuous profiling (Grafana Pyroscope, Parca, Datadog Profiler, Google Cloud Profiler) samples production stacks at low overhead (typically a few percent) and stores them as time-stamped flame graphs, letting you compare CPU/heap/lock profiles across versions and time — e.g. confirm a deploy raised CPU and see exactly which function is responsible, all from production, without reproducing locally.

Best Practices

References