Pelacakan Terdistribusi dengan Span OpenTelemetry
Instrumentasikan layanan secara otomatis dan manual untuk menghasilkan span yang menunjukkan latensi lintas batas layanan.
Pelacakan Terdistribusi dengan Span OpenTelemetry adalah pelajaran Node.js Backend Development Bootcamp gratis di CoddyKit. Ini adalah pelajaran 2 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Node.js Backend Development Bootcamp, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Node.js Backend Development Bootcamp mencakup 4 pelajaran total.
Bagian dari pelajaran ini belum diterjemahkan dan ditampilkan dalam bahasa Inggris.
Why Distributed Tracing?
In a microservices system one user request can hop across an API gateway, an orders service, a payments service, and a database. When that request is slow, a single service's logs cannot tell you where the time went.
Distributed tracing stitches the whole journey together. Each unit of work becomes a span, spans are linked into a trace, and the trace reveals latency across every service boundary.
- Trace: the entire end-to-end request, identified by a
traceId. - Span: one operation (an HTTP call, a DB query) with a start time, duration, and parent.
- Context propagation: passing
traceIdandspanIdacross service boundaries, usually via HTTP headers.
OpenTelemetry (OTel) is the vendor-neutral standard for producing these spans in Node.js.
Anatomy of a Span
A span is the atomic building block of a trace. Every span carries the same trace identity but its own identity and timing.
traceId: 16 bytes, shared by every span in the trace.spanId: 8 bytes, unique to this span.parentSpanId: links this span to the operation that caused it.name,startTime,endTime(duration = end - start).- Attributes: key/value tags like
http.methodordb.system. - Status:
OK,ERROR, orUNSET.
Parent/child links form a tree. The root span is the whole request; child spans are the calls it makes. Visualized, the tree becomes the familiar waterfall you see in Jaeger or Tempo.
Auto-Instrumentation with the Node SDK
The fastest way to get spans is auto-instrumentation. The OTel Node SDK monkey-patches popular libraries (http, Express, pg, ioredis, etc.) so they emit spans without you writing tracing code.
Create a tracing.js file that starts the SDK before anything else, then run your app with node -r ./tracing.js app.js so it loads first.
// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'orders-service',
}),
traceExporter: new OTLPTraceExporter({
url: 'http://localhost:4318/v1/traces',
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();What Auto-Instrumentation Gives You
With the SDK loaded, an incoming HTTP request automatically becomes a root span, and any outgoing http/fetch call or pg query becomes a child span underneath it.
- Inbound Express route → server span with
http.method,http.route,http.status_code. - Outbound HTTP call → client span, and the headers are injected automatically.
- Database query → client span with
db.systemand the statement.
This covers the boundaries for free. But auto-instrumentation does not understand your business logic — pricing rules, cache decisions, batch loops. For those you add spans manually.
Getting a Tracer
To create spans manually you first obtain a tracer from the global trace API. Name it after the module or library producing the spans; the version is optional but helps when debugging instrumentation.
The tracer is the factory for all your manual spans.
const { trace } = require('@opentelemetry/api');
// Name + version identify the instrumentation scope
const tracer = trace.getTracer('orders-service', '1.0.0');
// Later, anywhere in the code:
// const span = tracer.startSpan('chargeCustomer');startActiveSpan: The Idiomatic Pattern
Prefer tracer.startActiveSpan() over startSpan(). startActiveSpan makes the new span the active span for the duration of its callback, so any child spans created inside (including auto-instrumented ones) automatically attach as children.
The golden rules: always span.end() in a finally block, and record errors plus an ERROR status on failure.
const { trace, SpanStatusCode } = require('@opentelemetry/api');
const tracer = trace.getTracer('orders-service');
async function processOrder(order) {
return tracer.startActiveSpan('processOrder', async (span) => {
try {
span.setAttribute('order.id', order.id);
span.setAttribute('order.items', order.items.length);
const result = await chargeAndShip(order); // child spans nest here
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (err) {
span.recordException(err);
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
throw err;
} finally {
span.end();
}
});
}Attributes, Events, and Status
Spans become useful when you enrich them. Three tools:
- Attributes — searchable key/value tags. Use semantic conventions (
http.method,db.system,messaging.system) so backends understand them. - Events — timestamped log lines anchored inside the span, e.g.
span.addEvent('cache.miss'). - Status — set
ERRORonly on real failures; leave success asUNSETorOK.
Keep cardinality sane: never put a raw user ID or full SQL with literals into a high-traffic attribute if your backend indexes it — it can explode storage.
function readFromCache(span, key) {
const hit = cache.has(key);
if (hit) {
span.addEvent('cache.hit', { 'cache.key': key });
} else {
span.addEvent('cache.miss', { 'cache.key': key });
}
span.setAttribute('cache.hit', hit);
return hit ? cache.get(key) : null;
}Context Propagation Across Services
A trace only spans services if the trace context travels with the request. The W3C traceparent header carries the traceId, parent spanId, and sampling flag.
Auto-instrumentation injects and extracts this header for you on standard HTTP. When you do something non-standard (a custom transport, a message queue), you inject/extract manually with the propagation API.
const { context, propagation, trace } = require('@opentelemetry/api');
// SENDER: inject current context into outgoing carrier (e.g. message headers)
function publish(queue, payload) {
const headers = {};
propagation.inject(context.active(), headers);
queue.send({ payload, headers }); // traceparent now travels with the message
}
// RECEIVER: extract context and continue the trace
function onMessage(msg) {
const parentCtx = propagation.extract(context.active(), msg.headers);
const tracer = trace.getTracer('worker');
context.with(parentCtx, () => {
tracer.startActiveSpan('handleMessage', (span) => {
handle(msg.payload);
span.end();
});
});
}Reading the traceparent Header
The traceparent header has a fixed, parseable shape. Understanding it helps you debug broken traces (a missing child usually means a dropped header).
Format: version-traceId-parentId-flags, e.g.00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
00— version- 32 hex chars — the
traceId - 16 hex chars — the parent
spanId 01— flags (bit 0 = sampled)
Here is a tiny standalone parser to make the structure concrete.
function parseTraceparent(header) {
const parts = header.split('-');
if (parts.length !== 4) throw new Error('invalid traceparent');
const [version, traceId, parentId, flags] = parts;
return {
version,
traceId,
parentId,
sampled: (parseInt(flags, 16) & 1) === 1,
};
}
const h = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01';
console.log(parseTraceparent(h));
// { version: '00', traceId: '4bf9...4736', parentId: '00f0...02b7', sampled: true }Sampling to Control Cost
Tracing every request at high traffic is expensive. Samplers decide which traces to keep. The decision propagates via the traceparent sampled flag, so a trace is kept or dropped consistently across all services.
AlwaysOnSampler— keep everything (dev/low traffic).TraceIdRatioBasedSampler— keep a fixed fraction, e.g. 10%.ParentBasedSampler— respect the upstream decision; sample new roots by ratio. This is the production default.
Use head sampling (decide at the start) for simplicity, or tail sampling in a collector to always keep errors and slow traces.
const { ParentBasedSampler, TraceIdRatioBasedSampler } = require('@opentelemetry/sdk-trace-base');
// Keep 10% of new root traces; honor upstream decisions for the rest
const sampler = new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(0.1),
});
// Pass to the NodeSDK: new NodeSDK({ sampler, ... });Reading the Waterfall to Find Latency
Once spans reach a backend (Jaeger, Tempo, Honeycomb), you read the trace as a waterfall. Each bar is a span; its width is its duration; indentation shows parent/child.
How to find the bottleneck:
- Look for the widest child bar — that operation dominates the request.
- Watch for gaps between a parent and its first child — usually queueing, GC pauses, or un-instrumented work.
- Sequential bars that could run in parallel reveal a chance to use
Promise.all. - A red span with ERROR status points straight at the failing boundary.
The cross-service value: you can see that 80% of a 900ms request was spent inside the downstream payments service, not your own code.
Quick Check: Active Span Nesting
You manually wrap a function in tracer.startActiveSpan('outer', cb). Inside the callback, your auto-instrumented HTTP client makes an outbound call. Which statement is correct?
Recap & Takeaways
You can now produce spans that expose latency across service boundaries:
- Trace = many spans sharing a
traceId; each span has its ownspanIdand aparentSpanId. - Auto-instrumentation (NodeSDK + auto-instrumentations-node, loaded with
node -r) covers HTTP, DB, and queue boundaries for free. - Manual spans with
tracer.startActiveSpan()capture business logic; alwaysend()infinallyand set ERROR status on exceptions. - Enrich spans with attributes and events, watching cardinality.
- Context propagation via the W3C
traceparentheader makes traces cross services; inject/extract manually for non-HTTP transports. - Sampling (ParentBased + ratio) controls cost while keeping decisions consistent across services.
- Read the waterfall: widest bars, gaps, and serial calls reveal the real bottleneck.
Belajar JavaScript dengan tutor AI — gratis
Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.
- Kursus
- 22
- Pelajaran
- 92
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Pelacakan Terdistribusi dengan Span OpenTelemetry” gratis?
Ya — teks lengkap “Pelacakan Terdistribusi dengan Span OpenTelemetry” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Node.js Backend Development Bootcamp, upgrade ke CoddyKit PRO. Kursus Node.js Backend Development Bootcamp mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Pelacakan Terdistribusi dengan Span OpenTelemetry”?
Instrumentasikan layanan secara otomatis dan manual untuk menghasilkan span yang menunjukkan latensi lintas batas layanan. Kamu berlatih Node.js Backend Development Bootcamp dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Node.js Backend Development Bootcamp?
Tidak diperlukan pengalaman sebelumnya. Node.js Backend Development Bootcamp di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 2 dari 4.
Berapa lama pelajaran “Pelacakan Terdistribusi dengan Span OpenTelemetry” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Node.js Backend Development Bootcamp ini?
Ya. Setiap pelajaran Node.js Backend Development Bootcamp menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Logging Terstruktur dengan ID Korelasi
- Pelacakan Terdistribusi dengan Span OpenTelemetry
- Mengekspos Metrik Aplikasi dan Metode RED
- Propagasi Konteks dengan AsyncLocalStorage