Distributed tracing met OpenTelemetry-spans
Instrumenteer services automatisch en handmatig om spans te produceren die latency over servicegrenzen zichtbaar maken.
Distributed tracing met OpenTelemetry-spans is een gratis Bootcamp backendontwikkeling met Node.js-les op CoddyKit. Dit is les 2 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Bootcamp backendontwikkeling met Node.js. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Bootcamp backendontwikkeling met Node.js bevat in totaal 4 lessen.
Waarom gedistribueerde tracing?
In een microservicesysteem kan één gebruikersrequest via een API-gateway, een orderservice, een betaalservice en een database lopen. Wanneer die request traag is, kunnen de logs van één service je niet vertellen waar de tijd is gebleven.
Gedistribueerde tracing brengt de hele reis samen. Elke werkeenheid wordt een span, spans worden gekoppeld tot een trace en de trace laat de latency over elke servicegrens heen zien.
- Trace: de volledige request van begin tot eind, geïdentificeerd door een
traceId. - Span: één bewerking (een HTTP-aanroep, een databasequery) met een begintijd, duur en parent.
- Contextdoorgifte:
traceIdenspanIdover servicegrenzen heen doorgeven, meestal via HTTP-headers.
OpenTelemetry (OTel) is de leveranciersonafhankelijke standaard voor het produceren van deze spans in Node.js.
Anatomie van een span
Een span is de atomaire bouwsteen van een trace. Elke span bevat dezelfde trace-identiteit, maar heeft een eigen identiteit en tijdsgegevens.
traceId: 16 bytes, gedeeld door elke span in de trace.spanId: 8 bytes, uniek voor deze span.parentSpanId: koppelt deze span aan de bewerking die hem heeft veroorzaakt.name,startTime,endTime(duur = einde - begin).- Attributen: sleutel-waardetags zoals
http.methodofdb.system. - Status:
OK,ERRORofUNSET.
Parent-childkoppelingen vormen een boom. De root-span is de volledige request; child-spans zijn de aanroepen die deze uitvoert. Gevisualiseerd wordt de boom de bekende waterval die je in Jaeger of Tempo ziet.
Automatische instrumentatie met de Node-SDK
De snelste manier om spans te verkrijgen is automatische instrumentatie. De OTel Node-SDK past populaire bibliotheken (http, Express, pg, ioredis enzovoort) dynamisch aan, zodat ze spans produceren zonder dat je tracingcode hoeft te schrijven.
Maak een tracing.js-bestand dat de SDK vóór al het andere start en voer je app daarna uit met node -r ./tracing.js app.js, zodat het bestand als eerste wordt geladen.
// 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();Wat automatische instrumentatie je oplevert
Als de SDK is geladen, wordt een binnenkomende HTTP-request automatisch een root-span en wordt elke uitgaande http/fetch-aanroep of pg-query een child-span daaronder.
- Inkomende Express-route → server-span met
http.method,http.route,http.status_code. - Uitgaande HTTP-aanroep → client-span; de headers worden automatisch geïnjecteerd.
- Databasequery → client-span met
db.systemen de instructie.
Hiermee worden de grenzen automatisch afgedekt. Automatische instrumentatie begrijpt echter je bedrijfslogica niet — prijsregels, cachebeslissingen en batchlussen. Daarvoor voeg je handmatig spans toe.
Een tracer ophalen
Om handmatig spans te maken, haal je eerst een tracer op via de globale trace-API. Geef hem de naam van de module of bibliotheek die de spans produceert; de versie is optioneel, maar helpt bij het debuggen van instrumentatie.
De tracer is de fabriek voor al je handmatige 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: het idiomatische patroon
Gebruik bij voorkeur tracer.startActiveSpan() in plaats van startSpan(). startActiveSpan maakt de nieuwe span actief gedurende de callback, zodat child-spans die daarin worden gemaakt (ook automatisch geïnstrumenteerde spans) automatisch als children worden gekoppeld.
De gouden regels: roep altijd span.end() aan in een finally-blok en registreer bij een fout de fout plus een ERROR-status.
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();
}
});
}Attributen, gebeurtenissen en status
Spans worden nuttig wanneer je ze verrijkt. Daarvoor zijn er drie hulpmiddelen:
- Attributen — doorzoekbare sleutel-waardetags. Gebruik semantische conventies (
http.method,db.system,messaging.system), zodat backends ze begrijpen. - Gebeurtenissen — gelogde regels met een tijdstempel die binnen de span zijn verankerd, bijvoorbeeld
span.addEvent('cache.miss'). - Status — stel
ERRORalleen in bij echte fouten; laat geslaagde bewerkingen opUNSETofOKstaan.
Houd de cardinaliteit beheersbaar: zet nooit een onbewerkte gebruikers-ID of volledige SQL met letterlijke waarden in een attribuut met veel verkeer als je backend dit indexeert — de opslag kan dan exploderen.
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;
}Contextdoorgifte over services heen
Een trace strekt zich alleen over services uit als de tracecontext met de request meereist. De W3C-header traceparent bevat de traceId, de parent-spanId en de samplingvlag.
Automatische instrumentatie injecteert deze header bij standaard-HTTP voor je en leest hem ook uit. Wanneer je iets niet-standaards doet (een aangepast transport of een message queue), injecteer en lees je hem handmatig uit met de 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();
});
});
}De traceparent-header lezen
De traceparent-header heeft een vaste, parseerbare structuur. Als je die begrijpt, kun je kapotte traces beter debuggen (een ontbrekende child betekent meestal dat een header is weggevallen).
Indeling: version-traceId-parentId-flags, bijvoorbeeld00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
00— versie- 32 hextekens — de
traceId - 16 hextekens — de parent-
spanId 01— vlaggen (bit 0 = gesampled)
Hier is een kleine zelfstandige parser om de structuur concreet te maken.
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 }Samplen om kosten te beheersen
Elke request tracen bij veel verkeer is duur. Samplers bepalen welke traces je bewaart. De beslissing wordt doorgegeven via de gesamplede vlag in traceparent, zodat een trace consistent wordt bewaard of verwijderd in alle services.
AlwaysOnSampler— alles bewaren (ontwikkeling/weinig verkeer).TraceIdRatioBasedSampler— een vast deel bewaren, bijvoorbeeld 10%.ParentBasedSampler— de beslissing van upstream respecteren en nieuwe roots samplen volgens een verhouding. Dit is de standaard voor productie.
Gebruik voor eenvoud head sampling (beslissen aan het begin), of gebruik tail sampling in een collector om fouten en trage traces altijd te bewaren.
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, ... });De waterval lezen om latency te vinden
Zodra spans een backend (Jaeger, Tempo, Honeycomb) bereiken, lees je de trace als een waterval. Elke balk is een span; de breedte geeft de duur aan en de inspringing toont parent-childrelaties.
Zo vind je de bottleneck:
- Zoek naar de breedste child-balk — die bewerking bepaalt het grootste deel van de request.
- Let op hiaten tussen een parent en de eerste child — meestal wachtrijen, GC-pauzes of niet-geïnstrumenteerd werk.
- Sequentiële balken die parallel zouden kunnen lopen, laten zien dat je
Promise.allkunt gebruiken. - Een rode span met ERROR-status wijst rechtstreeks naar de grens waar de fout optreedt.
De waarde over services heen: je ziet dat 80% van een request van 900 ms binnen de downstream-betaalservice is besteed, en niet in je eigen code.
Snelle controle: nesten van actieve spans
Je wikkelt handmatig een functie in met tracer.startActiveSpan('outer', cb). Binnen de callback doet je automatisch geïnstrumenteerde HTTP-client een uitgaande aanroep. Welke uitspraak is juist?
Samenvatting en belangrijkste punten
Je kunt nu spans maken die de latentie over servicegrenzen heen zichtbaar maken:
- Trace = veel spans die een
traceIddelen; elke span heeft een eigenspanIden eenparentSpanId. - Automatische instrumentatie (NodeSDK + auto-instrumentations-node, geladen met
node -r) dekt HTTP-, database- en wachtrijgrenzen zonder extra werk. - Handmatige spans met
tracer.startActiveSpan()leggen bedrijfslogica vast; roep altijdend()aan infinallyen stel bij uitzonderingen de status in op ERROR. - Verrijk spans met attributen en gebeurtenissen en let daarbij op de kardinaliteit.
- Contextpropagatie via de W3C-header
traceparentlaat traces services overschrijden; injecteer en extraheer handmatig voor andere transporten dan HTTP. - Sampling (ParentBased + ratio) beheert de kosten en houdt beslissingen consistent tussen services.
- Lees de watervalweergave: de breedste balken, hiaten en seriële aanroepen laten het echte knelpunt zien.
Leer JavaScript met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 22
- Lessen
- 92
Veelgestelde vragen
Is de les “Distributed tracing met OpenTelemetry-spans” gratis?
Ja — de volledige tekst van “Distributed tracing met OpenTelemetry-spans” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Bootcamp backendontwikkeling met Node.js wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Bootcamp backendontwikkeling met Node.js bevat in totaal 4 lessen.
Wat leer ik in “Distributed tracing met OpenTelemetry-spans”?
Instrumenteer services automatisch en handmatig om spans te produceren die latency over servicegrenzen zichtbaar maken. Je oefent met Bootcamp backendontwikkeling met Node.js door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met Bootcamp backendontwikkeling met Node.js te beginnen?
Ervaring vooraf is niet nodig. Bootcamp backendontwikkeling met Node.js op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.
Hoe lang duurt de les “Distributed tracing met OpenTelemetry-spans”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over Bootcamp backendontwikkeling met Node.js?
Ja. Elke les over Bootcamp backendontwikkeling met Node.js bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Gestructureerd loggen met correlatie-ID's
- Distributed tracing met OpenTelemetry-spans
- Applicatiemetrics beschikbaar maken en de RED-methode
- Contextpropagatie met AsyncLocalStorage