Skip to content

Instantly share code, notes, and snippets.

@hnykda
Last active August 3, 2025 13:46
Show Gist options
  • Save hnykda/180ac0e87558ed9b0dcf548b1d07b870 to your computer and use it in GitHub Desktop.
Save hnykda/180ac0e87558ed9b0dcf548b1d07b870 to your computer and use it in GitHub Desktop.
sentry and open telemetry with standard OTEL/W3CT propagation nodejs/nextjs
// this file is loaded instrumentation.js via something like this:
// export async function register() {if (process.env.NEXT_RUNTIME === "nodejs"){await import("../sentry.server.config");}}
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { Resource, resourceFromAttributes } from "@opentelemetry/resources";
import {
W3CTraceContextPropagator,
CompositePropagator,
} from "@opentelemetry/core";
import {
NodeTracerProvider,
SimpleSpanProcessor,
ConsoleSpanExporter,
BatchSpanProcessor,
} from "@opentelemetry/sdk-trace-node";
import * as Sentry from "@sentry/nextjs";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { env } from "next-runtime-env";
import {
SentryPropagator,
SentrySampler,
SentrySpanProcessor,
} from "@sentry/opentelemetry";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
import { Context, propagation, TextMapSetter } from "@opentelemetry/api";
// import { trace } from "@opentelemetry/api";
// uncomment this to see some extra debug info
// import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
// diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
// Initialize Sentry with custom OpenTelemetry setup
const sentryClient = Sentry.init({
dsn: env("NEXT_PUBLIC_SENTRY_DSN"),
environment: env("ENVIRONMENT"),
skipOpenTelemetrySetup: true,
tracesSampleRate: 1.0,
// Setting this option to true will print useful information to the console while you're setting up Sentry.
// true will only work if disableLogger is false in next.config.ts
debug: false,
});
const otlpExporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT
? `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces`
: "http://localhost:4318/v1/traces",
});
// https://docs.sentry.io/platforms/javascript/guides/node/opentelemetry/custom-setup/
const provider = new NodeTracerProvider({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: "your-service-name",
}),
// @ts-expect-error - SentrySampler is not typed correctly
sampler: sentryClient ? new SentrySampler(sentryClient) : undefined,
spanProcessors: [
// new SimpleSpanProcessor(new ConsoleSpanExporter()), // useful for debugging
// @ts-expect-error - SentrySpanProcessor is not typed correctly
new SentrySpanProcessor(),
new BatchSpanProcessor(otlpExporter),
],
});
provider.register({
propagator: new CompositePropagator({
// W3CTraceContextPropagator is OTEL stadnard, makes sure that e.g. `traceparent` is included in requests
propagators: [new W3CTraceContextPropagator(), new SentryPropagator()],
}),
contextManager: new Sentry.SentryContextManager(),
});
Sentry.validateOpenTelemetrySetup();
registerInstrumentations({
// actually adds traceparent into headers when calling fetch
instrumentations: [new UndiciInstrumentation()],
});
// ways to instrument your specific calls/code/... would be somewhere on the BE of your Node app (or e.g. NextJS backend)
// trace.getTracer("cohort-portal").startActiveSpan("test", (span) => {
// console.log("test", span);
// fetch("http://example.com") // example.com should receive a request with header that has sentry-trace and traceparent
// span.end();
// });
// also works, sentry recommends this
// Sentry.getClient()?.tracer.startActiveSpan("test", (span) => {
// console.log("test 2", span);
// span.end();
// });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment