OpenTelemetry Aralıklarıyla Dağıtık İzleme
Hizmet sınırları arasındaki gecikmeyi ortaya çıkaran aralıklar üretmek için hizmetleri otomatik ve el ile enstrümante edin.
OpenTelemetry Aralıklarıyla Dağıtık İzleme, CoddyKit'te ücretsiz bir Node.js Backend Development Bootcamp dersidir. Bu, 4 dersinin 2. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Node.js Backend Development Bootcamp öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Node.js Backend Development Bootcamp kursu toplamda 4 dersten oluşur.
Bu dersin bazı bölümleri henüz çevrilmemiş olup İngilizce olarak gösterilmektedir.
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.
Sıkça Sorulan Sorular
“OpenTelemetry Aralıklarıyla Dağıtık İzleme” dersi ücretsiz mi?
Evet — “OpenTelemetry Aralıklarıyla Dağıtık İzleme” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Node.js Backend Development Bootcamp kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Node.js Backend Development Bootcamp kursu toplamda 4 dersten oluşur.
“OpenTelemetry Aralıklarıyla Dağıtık İzleme” dersinde ne öğreneceğim?
Hizmet sınırları arasındaki gecikmeyi ortaya çıkaran aralıklar üretmek için hizmetleri otomatik ve el ile enstrümante edin. Node.js Backend Development Bootcamp ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
Node.js Backend Development Bootcamp öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te Node.js Backend Development Bootcamp, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 2. dersidir.
“OpenTelemetry Aralıklarıyla Dağıtık İzleme” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu Node.js Backend Development Bootcamp dersinde kod yazıp çalıştırabilir miyim?
Evet. Her Node.js Backend Development Bootcamp dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- İlişkilendirme Kimlikleriyle Yapılandırılmış Günlükleme
- OpenTelemetry Aralıklarıyla Dağıtık İzleme
- Uygulama Ölçümlerini Açığa Çıkarma ve RED Yöntemi
- AsyncLocalStorage ile Bağlam Aktarımı