OpenTelemetry
If your Node.js service is already instrumented with the OpenTelemetry SDK, add AxonPushSpanExporter to your tracer provider and every span you create ships to AxonPush as an app.span event alongside whatever other OTel-compatible backends you already export to.
[!TIP] Non-blocking by default (v0.0.2+)
Span exports are pushed onto a bounded in-memory queue and drained by a single background task. The OTel
forceFlush()hook drains the queue synchronously — it’s called by the tracer provider before shutdown, soprovider.shutdown()at process exit guarantees pending spans are delivered. In a Lambda handler, callexporter.forceFlush()(or wrap the handler withflushAfterInvocation) at the end of each invocation. See the Pino page for the full Lambda pattern.
Installation
Section titled “Installation”npm install @axonpush/sdk @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/sdk-trace-nodeimport { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";import { AxonPush } from "@axonpush/sdk";import { AxonPushSpanExporter } from "@axonpush/sdk/integrations/otel";
const client = new AxonPush({ apiKey: process.env.AXONPUSH_API_KEY!, tenantId: process.env.AXONPUSH_TENANT_ID!,});
const exporter = new AxonPushSpanExporter({ client, channelId: 1, serviceName: "my-api", environment: "production",});
const provider = new NodeTracerProvider();provider.addSpanProcessor(new SimpleSpanProcessor(exporter));provider.register();[!TIP] Multiple exporters
TracerProvidersupports multiple span processors — addAxonPushSpanExporteralongside your existing OTLP exporter and spans flow to both backends:provider.addSpanProcessor(new SimpleSpanProcessor(otlpExporter));provider.addSpanProcessor(new SimpleSpanProcessor(axonpushExporter));
Use tracer.startActiveSpan(...) as normal — spans are exported when they close:
import { trace } from "@opentelemetry/api";
const tracer = trace.getTracer("my-api");
await tracer.startActiveSpan("POST /chat", async (span) => { span.setAttribute("http.method", "POST"); span.setAttribute("http.route", "/chat");
await tracer.startActiveSpan("llm.call", async (llmSpan) => { llmSpan.setAttribute("model", "gpt-4o-mini"); llmSpan.setAttribute("prompt_tokens", 128); // ... await LLM call ... llmSpan.end(); });
span.end();});Auto-instrumentation
Section titled “Auto-instrumentation”OTel ships auto-instrumentation packages for popular Node.js libraries (Express, Fastify, pg, redis, AWS SDK, …). Install them alongside the SDK and they hook into the tracer provider you configured — no code changes required.
npm install @opentelemetry/auto-instrumentations-nodeimport { registerInstrumentations } from "@opentelemetry/instrumentation";import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
registerInstrumentations({ tracerProvider: provider, instrumentations: [getNodeAutoInstrumentations()],});Every HTTP request, DB query, and AWS SDK call now ships to AxonPush.
Flushing and shutdown
Section titled “Flushing and shutdown”await exporter.forceFlush(); // drain pending spansawait exporter.shutdown(); // drain + stop the background workerTracerProvider.shutdown() calls exporter.shutdown() automatically, so a graceful process exit that closes the provider will flush all pending spans.
Events
Section titled “Events”| Field | Value |
|---|---|
identifier | The span name |
eventType | "app.span" |
payload.name | Span name |
payload.kind | OTel span kind (INTERNAL, SERVER, CLIENT, PRODUCER, CONSUMER) |
payload.traceId / payload.spanId | Preserved from the OTel context |
payload.parentSpanId | Parent span ID, if any |
payload.startTimeUnixNano / payload.endTimeUnixNano | Span timing |
payload.status | OTel { code, message } |
payload.attributes | All span attributes |
payload.events / payload.links | Span events and links |
payload.resource | Merged span resource + exporter-level overrides |