Distributed Tracing
When one request flows through many services, a single log file can't tell you where the time went. Distributed tracing stitches the whole journey together so you can follow one request — and find the slow span — across every service it touches.
Learn Distributed Tracing in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn the core ideas of observability — traces, spans, and context propagation — and set up the @opentelemetry/sdk-node SDK with auto-instrumentation, exporting to a backend like Jaeger or Zipkin.
What You'll Learn in This Lesson
1️⃣ Set Up the OpenTelemetry SDK
Tracing starts with the SDK . You configure NodeSDK with a service name, an exporter (where spans go), and a set of instrumentations . The catch: this file must load before the rest of your app, because auto-instrumentation patches libraries at the moment they're required.
getNodeAutoInstrumentations() is the magic: it patches common libraries — http , express , pg , and more — so every request and query produces a span without you writing any tracing code. Load it with node --require ./tracing.js server.js .
2️⃣ Traces and Spans
A span is one timed unit of work; a trace is the tree of spans that share a trace ID. Auto-instrumentation gives you spans for free, but you can also create your own to time a specific piece of business logic. A span started inside another becomes its child , building the tree you see in the UI.
Two rules matter: set attributes so spans are searchable (by user, cart size, status), and always end every span you start. An unended span never records its duration and can leak.
3️⃣ Context Propagation Across Services
What makes tracing distributed is context propagation . When one service calls another, OpenTelemetry injects the current trace and span IDs into a traceparent header. The receiving service reads it and continues the same trace, so a request that crosses four services shows up as one connected trace, not four disconnected ones.
Because http is auto-instrumented, the traceparent header rides along automatically — you don't set it by hand. This correlation is what lets you open one slow request and see, span by span, exactly which service ate the time.
Your turn. Fill in the ___ blank so the span is properly closed.
Add a custom span around a deliberately slow function, for example one that await s a 500ms timer, and give it an attribute like slow.reason . Trigger the request, then open the trace in Jaeger or Zipkin and find your span in the waterfall. Confirm its duration stands out against the fast spans around it. That's the everyday workflow: a request feels slow, you open its trace, and the longest span tells you exactly where to look.
📋 Quick Reference — OpenTelemetry in Node
Practice quiz
What problem does distributed tracing primarily solve?
- Following a single request as it travels across many services
- Compressing log files
- Replacing the database
- Encrypting network traffic
Answer: Following a single request as it travels across many services. Tracing reconstructs the full path of one request across all the services it touches.
In tracing terminology, what is a span?
- A log line
- A type of database index
- A single named, timed unit of work within a trace
- The whole request from start to finish
Answer: A single named, timed unit of work within a trace. A span represents one operation, with a start time, duration, and attributes; a trace is a tree of spans.
What is a trace?
- A metrics counter
- A collection of spans sharing one trace ID that together describe one request's journey
- A single HTTP header
- A single log message
Answer: A collection of spans sharing one trace ID that together describe one request's journey. A trace is all the spans that share a trace ID, forming the end-to-end view of one request.
How does a trace stay connected as a request crosses a service boundary?
- By guessing based on timestamps
- By writing to a shared file
- It cannot cross service boundaries
- Through context propagation, passing trace and span IDs (commonly via the traceparent header)
Answer: Through context propagation, passing trace and span IDs (commonly via the traceparent header). Context propagation carries the trace/span IDs across calls, usually in the W3C traceparent header.
Which OpenTelemetry package bootstraps tracing for a Node service?
- @opentelemetry/sdk-node
- express
- @grpc/grpc-js
- amqplib
Answer: @opentelemetry/sdk-node. @opentelemetry/sdk-node wires up the SDK; you start it before the rest of your app loads.
What does getNodeAutoInstrumentations() do?
- Deletes old traces
- Automatically instruments common libraries like http, express, and pg without manual code
- Sends emails on errors
- Compiles TypeScript
Answer: Automatically instruments common libraries like http, express, and pg without manual code. Auto-instrumentations patch popular libraries so spans are created without hand-written code.
Where do you typically send (export) the collected spans?
- To the npm registry
- Only to the console, never anywhere else
- Back into the source code
- To a backend like Jaeger or Zipkin via an exporter
Answer: To a backend like Jaeger or Zipkin via an exporter. An exporter ships spans to a tracing backend such as Jaeger or Zipkin for storage and viewing.
Why must the OpenTelemetry SDK be started before your app's other modules load?
- It does not matter when you start it
- To save memory
- So auto-instrumentation can patch libraries like http and pg before they are required
- Because Node requires it by law
Answer: So auto-instrumentation can patch libraries like http and pg before they are required. Instrumentation works by patching modules at require time, so the SDK must initialize first.
What is a parent-child relationship between spans?
- A retry of the same span
- A span created within another span records the outer span as its parent, forming the trace tree
- Two unrelated traces
- A database foreign key
Answer: A span created within another span records the outer span as its parent, forming the trace tree. Nested operations create child spans whose parent is the enclosing span, building the trace tree.
How does tracing help when one request is slow across several services?
- It shows each span's duration so you can pinpoint exactly which service or call is the bottleneck
- It automatically rewrites the code
- It only reports total time with no breakdown
- It hides the slow service
Answer: It shows each span's duration so you can pinpoint exactly which service or call is the bottleneck. Per-span durations reveal where time is actually spent, isolating the bottleneck across services.