@beignet/provider-tracing-opentelemetry
v0.0.56
Published
OpenTelemetry tracing and metrics provider for Beignet
Maintainers
Readme
@beignet/provider-tracing-opentelemetry
Runtime: Beignet requires Node.js 22.12 or newer. Bun is optional.
[!CAUTION] Beignet is experimental alpha software. The
0.0.xpackage line is for early evaluation, and APIs may change between releases while the framework settles.
OpenTelemetry tracing and metrics for Beignet requests, use cases, listeners, jobs, schedules, tasks, and provider instrumentation.
The package is an adapter, not an SDK bootstrap. Your app owns the global OpenTelemetry SDK, exporter, sampler, resource attributes, and shutdown or serverless flush behavior.
Install
bun add @beignet/provider-tracing-opentelemetry @opentelemetry/apiInstall the SDK or host integration that exports your telemetry separately.
Next.js on Vercel
Install Vercel's OpenTelemetry bootstrap:
bun add @vercel/otelCreate one idempotent app-owned registration function:
// lib/telemetry.ts
import { registerOTel } from "@vercel/otel";
const state = globalThis as typeof globalThis & {
__appTelemetryRegistered?: boolean;
};
export function registerTelemetry() {
if (state.__appTelemetryRegistered) return;
registerOTel({ serviceName: "my-app" });
state.__appTelemetryRegistered = true;
}Call it from the root Next.js instrumentation hook:
// instrumentation.ts
import { registerTelemetry } from "@/lib/telemetry";
export function register() {
registerTelemetry();
}Next.js only invokes that hook for the Next.js runtime. Standalone job workers, task and schedule commands, outbox drains, and scripts must call the same registration function before initializing their Beignet server. Otherwise the provider safely uses OpenTelemetry's no-op globals and exports nothing.
Then install the Beignet provider after devtools and before providers whose operations should feed OpenTelemetry:
import { createDevtoolsProvider } from "@beignet/devtools";
import { createOpenTelemetryTracingProvider } from "@beignet/provider-tracing-opentelemetry";
import { registerTelemetry } from "@/lib/telemetry";
registerTelemetry();
export const providers = [
createDevtoolsProvider(),
createOpenTelemetryTracingProvider(),
// Database, mail, jobs, and other instrumented providers follow.
] as const;Provider ordering matters for instrumentation composition. The OpenTelemetry provider forwards events to an earlier devtools or instrumentation sink, while later providers resolve the composed sink and contribute their operation metrics and span events.
If Sentry is also installed and the app uses another OpenTelemetry SDK, disable Sentry's SDK setup so only one tracing pipeline owns process instrumentation:
createSentryErrorReportingProvider({
init: { skipOpenTelemetrySetup: true },
});Spans
Beignet creates active spans with stable names and low-cardinality attributes:
| Boundary | Span name |
| --- | --- |
| HTTP request | beignet.request <contract> |
| Use case | beignet.use_case <name> |
| Listener | beignet.listener <name> |
| Job handler | beignet.job <name> |
| Outbox delivery | beignet.outbox deliver <name> |
| Schedule handler | beignet.schedule <name> |
| Task handler | beignet.task <name> |
Incoming traceparent and tracestate headers continue the request trace.
Nested in-process work uses the active OpenTelemetry context automatically.
Beignet's versioned TraceCarrier continues context through outbox rows, Redis
event messages, BullMQ jobs, and Inngest functions. Old messages without a
carrier start a new trace. Malformed or unknown carrier metadata is ignored so
telemetry cannot replace message delivery behavior. OpenTelemetry baggage is
not propagated.
Metrics
The adapter records these instruments through the registered global meter, or
through an injected meter:
beignet.request.durationbeignet.use_case.durationbeignet.listener.durationbeignet.job.durationbeignet.outbox.delivery.durationbeignet.schedule.durationbeignet.task.durationbeignet.operation.errorsbeignet.provider.operation.count
Duration units are milliseconds. Metric attributes contain operation names, types, outcomes, attempts, and provider names where available; payloads, request bodies, tenant IDs, user IDs, and error messages are excluded.
TraceOperation.attributes are span-only. Custom tracing integrations must put
only bounded operation dimensions in TraceOperation.metricAttributes; never
copy request, actor, tenant, or payload values into metric labels.
Not every tracing bootstrap installs a metric exporter. In that case the OpenTelemetry API's no-op meter receives these calls until the app registers a real meter provider.
Set meter: false to disable Beignet metrics while retaining spans, or inject
a configured meter directly:
createOpenTelemetryTracingProvider({
meter: myMeter,
});Error privacy
Failed spans set OpenTelemetry error status and the low-cardinality
error.type attribute. They do not record exception messages or stacks by
default. Apps that have reviewed their exporter redaction policy can opt in:
createOpenTelemetryTracingProvider({
recordExceptions: true,
});The provider does not export OpenTelemetry logs. Continue using Beignet's logger and error-reporting ports for structured logs and captured exceptions.
Direct setup
Use createOpenTelemetryTracing(...) in tests or custom composition:
import { createOpenTelemetryTracing } from "@beignet/provider-tracing-opentelemetry";
const { tracing, instrumentation } = createOpenTelemetryTracing({
tracer,
meter,
instrumentation: existingSink,
});This package starts no workers, network clients, timers, or background loops. Its runtime is safe to install in serverless processes; export and flush semantics remain the responsibility of the app-owned OpenTelemetry SDK. Failures in tracer span mutation or metric recording are isolated from the wrapped application operation.
