Distribuerad spårning med OpenTelemetry-spans
Instrumentera tjänster automatiskt och manuellt för att skapa spans som visar fördröjning över tjänstegränser.
Distribuerad spårning med OpenTelemetry-spans är en gratis lektion i Bootcamp i backendutveckling med Node.js på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Bootcamp i backendutveckling med Node.js, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Bootcamp i backendutveckling med Node.js innehåller totalt 4 lektioner.
Varför distribuerad spårning?
I ett mikrotjänstsystem kan en användarbegäran gå via en API-gateway, en ordertjänst, en betaltjänst och en databas. När begäran är långsam kan loggarna från en enskild tjänst inte tala om för dig var tiden försvann.
Distribuerad spårning fogar samman hela förloppet. Varje arbetsenhet blir en span, spans länkas till en trace, och tracen visar fördröjningen över varje tjänstegräns.
- Trace: hela begäran från början till slut, identifierad av ett
traceId. - Span: en operation (ett HTTP-anrop eller en databassökning) med starttid, varaktighet och överordnad span.
- Kontextpropagering: att skicka
traceIdochspanIdöver tjänstegränser, vanligtvis via HTTP-headers.
OpenTelemetry (OTel) är den leverantörsoberoende standarden för att skapa dessa spans i Node.js.
En spans uppbyggnad
En span är det grundläggande byggblocket i en trace. Varje span har samma trace-identitet, men en egen identitet och tidsinformation.
traceId: 16 byte, delas av alla spans i tracen.spanId: 8 byte, unikt för denna span.parentSpanId: länkar denna span till operationen som orsakade den.name,startTime,endTime(varaktighet = slut - start).- Attribut: nyckel/värde-taggar som
http.methodellerdb.system. - Status:
OK,ERRORellerUNSET.
Länkar mellan överordnade och underordnade spans bildar ett träd. Root-spannen är hela begäran; child-spans är anropen den gör. Visualiserat blir trädet den välbekanta waterfall-vyn som du ser i Jaeger eller Tempo.
Automatisk instrumentering med Node SDK
Det snabbaste sättet att få spans är automatisk instrumentering. OTel Node SDK monkey-patchar populära bibliotek (http, Express, pg, ioredis med flera) så att de genererar spans utan att du behöver skriva tracing-kod.
Skapa en fil med namnet tracing.js som startar SDK:t innan något annat, och kör sedan appen med node -r ./tracing.js app.js så att den laddas först.
// 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();Det här får du med automatisk instrumentering
När SDK:t är laddat blir en inkommande HTTP-begäran automatiskt en root-span, och alla utgående anrop med http/fetch eller frågor med pg blir child-spans under den.
- Inkommande Express-route → server-span med
http.method,http.routeochhttp.status_code. - Utgående HTTP-anrop → client-span, och headers injiceras automatiskt.
- Databasfråga → client-span med
db.systemoch frågeuttrycket.
Detta täcker gränserna utan extra arbete. Men automatisk instrumentering förstår inte din affärslogik – prism regler, cachebeslut och batchloopar. För sådant lägger du till spans manuellt.
Hämta en tracer
För att skapa spans manuellt hämtar du först en tracer från det globala trace-API:t. Namnge den efter modulen eller biblioteket som producerar spans; versionen är valfri men underlättar vid felsökning av instrumenteringen.
Tracern är fabriken för alla dina manuella 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: det idiomatiska mönstret
Föredra tracer.startActiveSpan() framför startSpan(). startActiveSpan gör den nya spannen till den aktiva spannen under callbackens hela körning, så att eventuella child-spans som skapas inuti (inklusive automatiskt instrumenterade spans) automatiskt kopplas som underordnade.
De viktigaste reglerna: anropa alltid span.end() i ett finally-block och registrera fel samt statusen ERROR vid misslyckanden.
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();
}
});
}Attribut, händelser och status
Spans blir användbara när du berikar dem. Tre verktyg:
- Attribut – sökbara nyckel/värde-taggar. Använd semantiska konventioner (
http.method,db.system,messaging.system) så att backends förstår dem. - Händelser – tidsstämplade loggrader som är förankrade i spannen, till exempel
span.addEvent('cache.miss'). - Status – ange
ERRORendast vid verkliga fel; låt lyckade operationer haUNSETellerOK.
Håll kardinaliteten på en rimlig nivå: lägg aldrig in ett rått användar-ID eller fullständig SQL med literaler i ett attribut med hög trafik om backend-systemet indexerar det – lagringsmängden kan explodera.
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;
}Kontextpropagering mellan tjänster
En trace omfattar flera tjänster endast om trace-kontexten följer med begäran. W3C-headern traceparent innehåller traceId, överordnat spanId och en flagga för sampling.
Automatisk instrumentering injicerar och extraherar denna header åt dig för vanlig HTTP. När du använder något icke-standardiserat (en anpassad transport eller en meddelandekö) injicerar och extraherar du den manuellt med propagation-API:t.
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();
});
});
}Läsa traceparent-headern
Headern traceparent har en fast form som går att parsa. Om du förstår den blir det lättare att felsöka trasiga traces (en saknad child-span betyder vanligtvis att en header har tappats bort).
Format: version-traceId-parentId-flags, till exempel00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
00– version- 32 hextecken –
traceId - 16 hextecken – överordnat
spanId 01– flaggor (bit 0 = samplad)
Här är en liten fristående parser som gör strukturen konkret.
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 för kostnadskontroll
Att spåra varje begäran vid hög trafik är kostsamt. Samplers avgör vilka traces som ska sparas. Beslutet propageras via den samplade flaggan i traceparent, så att en trace sparas eller tas bort konsekvent i alla tjänster.
AlwaysOnSampler– behåll allt (utveckling/låg trafik).TraceIdRatioBasedSampler– behåll en fast andel, till exempel 10 %.ParentBasedSampler– följ beslutet uppströms; sampla nya roots enligt en kvot. Detta är standardinställningen i produktion.
Använd head sampling (fatta beslutet i början) för enkelhetens skull, eller tail sampling i en collector för att alltid behålla fel och långsamma 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, ... });Läsa waterfall-vyn för att hitta fördröjningar
När spans når en backend (Jaeger, Tempo, Honeycomb) läser du tracen som en waterfall. Varje stapel är en span; dess bredd visar varaktigheten och indraget visar relationen mellan överordnad och underordnad.
Så hittar du flaskhalsen:
- Leta efter den bredaste child-stapeln – den operationen dominerar begäran.
- Var uppmärksam på luckor mellan en parent och dess första child – det beror vanligtvis på köbildning, GC-pauser eller arbete utan instrumentering.
- Sekventiella staplar som skulle kunna köras parallellt visar en möjlighet att använda
Promise.all. - En röd span med statusen ERROR pekar direkt på den gräns där felet uppstod.
Värdet över flera tjänster: du kan se att 80 % av en begäran på 900 ms tillbringades i den nedströms betaltjänsten, inte i din egen kod.
Snabb kontroll: nästlade aktiva span
Ni omsluter manuellt en funktion med tracer.startActiveSpan('outer', cb). Inuti callback-funktionen gör den automatiskt instrumenterade HTTP-klienten ett utgående anrop. Vilket påstående är korrekt?
Sammanfattning och viktigaste punkter
Ni kan nu skapa span som synliggör fördröjning över tjänstegränser:
- Trace = många span som delar ett
traceId; varje span har ett egetspanIdoch ettparentSpanId. - Automatisk instrumentering (NodeSDK + auto-instrumentations-node, laddas med
node -r) täcker HTTP-, databas- och kögränser utan extra arbete. - Manuella span med
tracer.startActiveSpan()fångar affärslogik; anropa alltidend()ifinallyoch ange statusen ERROR vid undantag. - Berika span med attribut och händelser, men håll koll på kardinaliteten.
- Kontextpropagering via W3C-huvudet
traceparentgör att trace kan gå mellan tjänster; injicera och extrahera manuellt för andra transportprotokoll än HTTP. - Sampling (ParentBased + ratio) styr kostnaden samtidigt som besluten förblir konsekventa mellan tjänster.
- Läs vattenfallsdiagrammet: de bredaste staplarna, luckorna och seriella anropen avslöjar den verkliga flaskhalsen.
Lär dig JavaScript med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 22
- Lektioner
- 92
Vanliga frågor
Är lektionen ”Distribuerad spårning med OpenTelemetry-spans” gratis?
Ja – hela texten till ”Distribuerad spårning med OpenTelemetry-spans” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Bootcamp i backendutveckling med Node.js, kan Ni uppgradera till CoddyKit PRO. Kursen i Bootcamp i backendutveckling med Node.js innehåller totalt 4 lektioner.
Vad lär jag mig i ”Distribuerad spårning med OpenTelemetry-spans”?
Instrumentera tjänster automatiskt och manuellt för att skapa spans som visar fördröjning över tjänstegränser. Ni övar på Bootcamp i backendutveckling med Node.js med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Bootcamp i backendutveckling med Node.js?
Du behöver inga förkunskaper. Utbildningen i Bootcamp i backendutveckling med Node.js på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.
Hur lång tid tar lektionen ”Distribuerad spårning med OpenTelemetry-spans”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Bootcamp i backendutveckling med Node.js-lektionen?
Ja. Varje Bootcamp i backendutveckling med Node.js-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Strukturerad loggning med korrelations-ID:n
- Distribuerad spårning med OpenTelemetry-spans
- Exponera applikationsmätvärden och RED-metoden
- Kontextpropagering med AsyncLocalStorage