@virtu3d_org/otel-node
v0.2.0
Published
OpenTelemetry + OTLP observability for Node (Express, Fastify, Nest)
Downloads
92
Readme
@virtu3d_org/otel-node
OpenTelemetry + OTLP observability for Node (Express, Fastify, Nest). Traces are sent to an OTLP endpoint (e.g. SigNoz); only explicitly traced routes and operations are exported.
v0.2+ upgrades OpenTelemetry to the 0.200 line and bundles @posthog/ai for PostHogTraceExporter (static import). Use module / moduleResolution Node16 (or compatible) in consuming packages if TypeScript cannot resolve @posthog/ai/otel.
Install
npm i @virtu3d_org/otel-nodeUsage
Init (once at app startup)
import { initNodeOtel, FRAMEWORK_EXPRESS } from '@virtu3d_org/otel-node';
await initNodeOtel({
serviceName: 'my-service',
serviceVersion: process.env.APP_VERSION ?? '0.0.1',
environment: process.env.NODE_ENV ?? 'development',
otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? 'http://localhost:4318',
authKey: process.env.SIGNOZ_INGESTION_KEY, // optional
framework: FRAMEWORK_EXPRESS, // or FRAMEWORK_FASTIFY
flowIdHeader: 'x-otel-flow-id', // optional; default is x-otel-flow-id
});Express (or Fastify)
Wrap route handlers with traceRoute so they are traced; other HTTP requests are dropped at export.
import { traceRoute } from '@virtu3d_org/otel-node';
app.post('/api/login', traceRoute({ name: 'auth.login', controller: loginController }));Nest
Use the /nest subpath so Express-only apps don't pull in @nestjs/*:
import { TraceRoute, TraceRouteInterceptor } from '@virtu3d_org/otel-node/nest';
// Global interceptor
app.useGlobalInterceptors(new TraceRouteInterceptor(new Reflector()));
// On controllers/methods you want traced
@TraceRoute()
@Get('documents/:id')
getDocument() { ... }
@TraceRoute('documents.getDocument')
@Get('documents/:id')
getDocument() { ... }Non-HTTP operations
import { runTracedOperation } from '@virtu3d_org/otel-node';
await runTracedOperation('background.autosave', () => doAutosave());PostHog LLM analytics (optional, separate pipeline)
Regular OTLP export uses a route filter so only opt-in HTTP routes and runTracedOperation spans are sent. AI SDK / gen_ai spans can be dropped if they are roots or lack otel.traced.
To send the same spans to PostHog LLM analytics on a second BatchSpanProcessor—without that filter—enable ai. @posthog/ai is a dependency of this package (exporter is imported statically).
Pass
aiintoinitNodeOtel:await initNodeOtel({ serviceName: 'ai-microservice', environment: process.env.NODE_ENV ?? 'development', otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? 'http://localhost:4318', ai: { provider: 'posthog', apiKey: process.env.POSTHOG_API_KEY ?? '', host: process.env.POSTHOG_HOST ?? 'https://us.i.posthog.com', }, });Enable Vercel AI SDK telemetry on your
streamText/generateTextcalls (experimental_telemetry) and pass custom metadata (e.g.user_email,organisation_id,conversation_id) — these are propagated to all child spans and appear as PostHog event properties.
otlpEnabled: set to false only if you want PostHog-only export (no SigNoz/generic OTLP). If neither is configured the SDK starts without tracing and logs a warning — your app continues normally.
Env / config
- OTEL_EXPORTER_OTLP_ENDPOINT – OTLP endpoint (e.g.
https://ingest.signoz.io:443). - OTEL_LOGS_ENABLED – Set to
trueto enable OpenTelemetry diagnostic logs and[OTEL]console output. - flowIdHeader – Config option; default
x-otel-flow-id. Set to e.g.x-request-idif you already use that header.
