Skip to content

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, so provider.shutdown() at process exit guarantees pending spans are delivered. In a Lambda handler, call exporter.forceFlush() (or wrap the handler with flushAfterInvocation) at the end of each invocation. See the Pino page for the full Lambda pattern.

Terminal window
npm install @axonpush/sdk @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/sdk-trace-node
import { 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

TracerProvider supports multiple span processors — add AxonPushSpanExporter alongside 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();
});

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.

Terminal window
npm install @opentelemetry/auto-instrumentations-node
import { 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.

await exporter.forceFlush(); // drain pending spans
await exporter.shutdown(); // drain + stop the background worker

TracerProvider.shutdown() calls exporter.shutdown() automatically, so a graceful process exit that closes the provider will flush all pending spans.

FieldValue
identifierThe span name
eventType"app.span"
payload.nameSpan name
payload.kindOTel span kind (INTERNAL, SERVER, CLIENT, PRODUCER, CONSUMER)
payload.traceId / payload.spanIdPreserved from the OTel context
payload.parentSpanIdParent span ID, if any
payload.startTimeUnixNano / payload.endTimeUnixNanoSpan timing
payload.statusOTel { code, message }
payload.attributesAll span attributes
payload.events / payload.linksSpan events and links
payload.resourceMerged span resource + exporter-level overrides