Wanneer je Node.js applicatie in productie draait en er gaat iets mis, wil je snel weten waar. Observability met OpenTelemetry in Node.js geeft je het inzicht dat je nodig hebt: welke request faalde, waar zat de traagheid en welke downstream service gaf de fout terug.
In dit artikel leer je hoe OpenTelemetry werkt, hoe je het opzet in een Node.js project en welke patronen je helpen om schaalbare observability te bouwen. Je krijgt concrete code en tips waarmee je direct aan de slag kunt.
Wat is observability?
Observability betekent dat je vanuit de buitenkant van een systeem kunt afleiden wat er van binnen gebeurt. Het gaat verder dan monitoring, waarbij je vooraf bepaalde metrics in de gaten houdt. Observability helpt je ook onbekende problemen te diagnosticeren.
De drie pijlers zijn:
- Traces, de levensloop van een request door je systeem, inclusief alle stappen.
- Metrics, numerieke metingen over tijd, zoals request rate, errors of latency.
- Logs, tekstuele gebeurtenissen met context op een specifiek moment.
In een microservices- of distributed architectuur is observability geen luxe. Zonder goede traces is het zoeken naar een naald in een hooiberg wanneer een call tien services passeert.
Wat is OpenTelemetry?
OpenTelemetry (afgekort OTel) is een open-source project onder de Cloud Native Computing Foundation dat een vendor-neutrale standaard biedt voor het verzamelen van telemetrie. Het levert SDK's voor veel talen, waaronder Node.js, plus een protocol (OTLP) om data naar een backend te sturen.
Het grote voordeel: je instrumenteert je code één keer met de OpenTelemetry API en kunt later zonder codewijzigingen wisselen van backend. Vandaag Jaeger, morgen Datadog, overmorgen Grafana Cloud. De telemetrie data blijft hetzelfde.
Voor Node.js is OpenTelemetry inmiddels volwassen. De tracing SDK is sinds 2021 stable, metrics sinds 2022 en logs zijn recent stable geworden.
Basis setup in Node.js
Begin met het installeren van de SDK en automatische instrumentaties. Die laatste detecteren populaire libraries zoals Express, Fastify en pg en voegen automatisch traces toe.
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http
Maak een tracing.js bestand aan dat je als eerste laadt:
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-api',
[SemanticResourceAttributes.SERVICE_VERSION]: '1.2.0',
}),
traceExporter: new OTLPTraceExporter({
url: 'http://localhost:4318/v1/traces',
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
Start je applicatie met dit bestand voorop:
node --require ./tracing.js src/server.js
Het belangrijk om tracing.js vóór je applicatie te laden. De auto-instrumentaties patchen modules bij require, dus ze moeten actief zijn voordat Express of andere frameworks worden geladen.
Traces en spans in de praktijk
Een trace bestaat uit één of meer spans. Een span representeert een stuk werk met een start, eind en attributen. Auto-instrumentaties maken spans voor HTTP-calls, database queries en meer.
Voor je eigen business logica kun je handmatig spans aanmaken:
const { trace } = require('@opentelemetry/api');
const tracer = trace.getTracer('orders-api');
async function processOrder(orderId) {
return tracer.startActiveSpan('processOrder', async (span) => {
try {
span.setAttribute('order.id', orderId);
const order = await fetchOrder(orderId);
await chargeCustomer(order);
span.setStatus({ code: 1 }); // OK
return order;
} catch (err) {
span.recordException(err);
span.setStatus({ code: 2, message: err.message }); // ERROR
throw err;
} finally {
span.end();
}
});
}
Let op twee dingen. Gebruik startActiveSpan zodat de context automatisch wordt doorgegeven aan child spans. En span.end() moet altijd worden aangeroepen, ook bij errors.
Metrics verzamelen
Naast traces kun je metrics exporteren. Dit is handig voor dashboards met request rate, latency percentielen en business metrics zoals het aantal geplaatste orders.
const { metrics } = require('@opentelemetry/api');
const meter = metrics.getMeter('orders-api');
const orderCounter = meter.createCounter('orders.placed', {
description: 'Aantal geplaatste orders',
});
const orderDuration = meter.createHistogram('orders.duration_ms', {
description: 'Verwerkingstijd per order',
unit: 'ms',
});
async function placeOrder(data) {
const start = Date.now();
const order = await createOrder(data);
orderCounter.add(1, { channel: data.channel });
orderDuration.record(Date.now() - start, { status: 'success' });
return order;
}
Counters gaan alleen omhoog, histograms verzamelen distributies (p50, p95, p99) en gauges meten een moment in de tijd. Kies het type dat past bij wat je wilt weten.
Logs correleren met traces
Logs worden nuttig als je ze aan een trace kunt koppelen. OpenTelemetry injecteert de huidige trace ID en span ID in logs, zodat je in je observability tool kunt springen van een log-regel naar de volledige trace.
Met een logger zoals Pino werkt dit via instrumentatie:
const pino = require('pino');
const logger = pino();
logger.info({ userId: 42 }, 'User logged in');
Met @opentelemetry/instrumentation-pino actief bevat elke log regel automatisch trace_id en span_id. Dat maakt debugging in productie enorm veel sneller, zoals we ook bespraken in error handling op schaal.
De OpenTelemetry Collector
In productie wil je de Collector gebruiken in plaats van rechtstreeks naar je backend te exporteren. De OpenTelemetry Collector is een losstaand proces dat telemetrie ontvangt, verwerkt en doorstuurt.
Voordelen:
- Ontkoppeling, je applicatie stuurt naar de collector, niet naar de backend. Wissel van backend zonder code changes.
- Processing, sampling, batch, filtering en enrichment in de collector.
- Resilience, queueing en retries als je backend tijdelijk onbereikbaar is.
Een minimale collector configuratie:
receivers:
otlp:
protocols:
http:
grpc:
processors:
batch:
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/jaeger]
Je Node.js app stuurt naar de collector, de collector stuurt door naar Jaeger, Tempo of een commerciële backend.
Sampling strategieën
Elke request tracen is in drukke systemen duur qua opslag en netwerk. Sampling bepaalt welk percentage van de traces je behoudt.
Drie veelgebruikte strategieën:
- Head-based sampling, beslis aan het begin van een trace (bijv. 10% van alle requests).
- Tail-based sampling, beslis aan het eind, zodat je alle errors en trage requests kunt behouden.
- Rate limiting, maximaal N traces per seconde per service.
Voor de meeste apps is tail-based sampling via de collector ideaal. Je ziet al je errors en latency uitschieters zonder de volledige volume op te slaan. Dit sluit goed aan op het production readiness denken.
Context propagation tussen services
In een microservices architectuur moet de trace context meereizen tussen services. OpenTelemetry gebruikt hiervoor W3C Trace Context headers (traceparent, tracestate).
De auto-instrumentaties voor HTTP, gRPC en veel message brokers zorgen hier automatisch voor. Een request naar een downstream service bevat de headers, de ontvanger continueert de trace.
Voor gRPC services en Kafka integraties werkt dit out of the box, zolang je de juiste instrumentaties laadt. Voor custom transports moet je de context zelf injecteren en extraheren met de propagation API.
Best practices
Een paar adviezen uit de praktijk:
- Zet
service.nameenservice.version, zonder duidelijke service identifiers worden traces onvindbaar. - Gebruik semantic conventions, OpenTelemetry definieert standaard attribuut-namen zoals
http.methodendb.system. Volg die zodat tools je data begrijpen. - Instrumenteer op de juiste laag, vertrouw op auto-instrumentaties voor frameworks en voeg handmatig spans toe rond business logica.
- Zet attribuut-waardes bewust, plaats geen PII zoals e-mailadressen of wachtwoorden in spans.
- Test je telemetrie, start een lokale Jaeger via Docker en controleer of je traces eruit zien zoals je verwacht.
Combineer dit met goede performance tuning en je hebt een sterke basis voor productie.
Veelvoorkomende valkuilen
Een paar dingen waar teams vaak tegenaan lopen:
- Tracing laden na de app, het bestand moet met
--requireof als eerste import worden geladen, anders werken auto-instrumentaties niet. - Synchrone exporters, gebruik de OTLP exporter, niet de console exporter, in productie. De console exporter blokkeert.
- Te veel spans, elke span kost CPU en geheugen. Maak geen span per loop-iteratie.
- High-cardinality attributes, plaats geen user IDs of request IDs als metric dimensies. Dat kan je backend opblazen.
- Vergeten van context, bij background jobs en workers moet je de context expliciet doorgeven of een nieuwe root-span starten.
Veelgestelde vragen
Wat is OpenTelemetry?
OpenTelemetry is een open-source framework voor het verzamelen van telemetrie data zoals traces, metrics en logs. Het biedt één gestandaardiseerde SDK en API waarmee je je applicatie kunt instrumenteren, ongeacht welke backend je uiteindelijk gebruikt.
Wat is het verschil tussen traces, metrics en logs?
Traces laten zien hoe een request door je systeem stroomt, metrics zijn numerieke metingen over tijd zoals CPU of request count, en logs zijn tekstuele gebeurtenissen. Samen vormen ze de drie pijlers van observability.
Is OpenTelemetry klaar voor productie?
Ja, de tracing en metrics SDK's voor Node.js zijn stable en worden gebruikt door grote bedrijven. De logs-specificatie is recent stable geworden. Voor productiegebruik is OpenTelemetry inmiddels een veilige keuze.
Welke backend moet ik gebruiken met OpenTelemetry?
OpenTelemetry is backend-agnostisch. Populaire keuzes zijn Jaeger of Tempo voor traces, Prometheus voor metrics en Loki voor logs. Commerciële opties zijn onder andere Datadog, New Relic, Honeycomb en Grafana Cloud.
Heeft OpenTelemetry veel performance impact?
De overhead is meestal minimaal, vaak enkele procenten. Met sampling kun je de impact verder beperken door niet elke request te traceren. Zorg wel dat je de OTLP exporter asynchroon gebruikt.
Afsluitend
OpenTelemetry maakt observability in Node.js toegankelijk en toekomstbestendig. Begin klein met auto-instrumentaties en een lokale Jaeger, breid uit met handmatige spans en metrics, en zet uiteindelijk een Collector in productie. Zo bouw je stap voor stap het inzicht op dat je nodig hebt om je applicatie met vertrouwen te laten draaien.