Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { context, propagation, trace } from '@opentelemetry/api';
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from '@opentelemetry/core';
import type { Resource } from '@opentelemetry/resources';
import type { SpanExporter } from '@opentelemetry/sdk-trace';
import { BatchSpanProcessor, TracerProvider } from '@opentelemetry/sdk-trace';

// OpenTelemetry SDK v3 has no `NodeTracerProvider.register()`, so the app registers the tracer
// provider, context manager and propagator itself. The OTel SDK v2 variant replaces this file with
// `otel-v2/otel-tracer-provider.ts`.
export function registerTracerProvider(exporter: SpanExporter, resource: Resource): void {
trace.setGlobalTracerProvider(
new TracerProvider({
resource,
spanProcessors: [new BatchSpanProcessor({ exporter, scheduledDelayMillis: 100 })],
}),
);
context.setGlobalContextManager(new AsyncLocalStorageContextManager().enable());
propagation.setGlobalPropagator(
new CompositePropagator({ propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()] }),
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Resource } from '@opentelemetry/resources';
import type { SpanExporter } from '@opentelemetry/sdk-trace-base';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';

export function registerTracerProvider(exporter: SpanExporter, resource: Resource): void {
new NodeTracerProvider({
resource,
spanProcessors: [new BatchSpanProcessor(exporter, { scheduledDelayMillis: 100 })],
}).register();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { getOtlpTracesEndpoint } from '@sentry/nextjs';
import { OTLP_RECEIVER_PORT, startOtlpReceiver } from './otel-receiver';
import { registerTracerProvider } from './otel-tracer-provider';

// Next.js can run `register()` more than once in dev, which would leave a second receiver fighting
// for the port and a second set of providers losing the race to register globally.
Expand All @@ -27,13 +26,8 @@ if (!globalWithOtelFlag.__otelRegistered) {
// The app owns tracing: this registers the global tracer provider, context manager and
// propagator. Sentry is initialized afterwards with `enableOpenTelemetrySetup: false` so it does
// not contend for any of them.
new NodeTracerProvider({
registerTracerProvider(
new OTLPTraceExporter({ url: `${otlpBaseUrl}/v1/traces`, headers: otlpTracesEndpoint.headers }),
resource,
spanProcessors: [
new BatchSpanProcessor(
new OTLPTraceExporter({ url: `${otlpBaseUrl}/v1/traces`, headers: otlpTracesEndpoint.headers }),
{ scheduledDelayMillis: 100 },
),
],
}).register();
);
}
17 changes: 12 additions & 5 deletions dev-packages/e2e-tests/test-applications/nextjs-otlp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@
"test:dev": "TEST_ENV=development playwright test",
"test:dev-webpack": "TEST_ENV=development-webpack playwright test",
"test:build": "pnpm install && pnpm build",
"test:build:otel-v2": "pnpm remove @opentelemetry/context-async-hooks @opentelemetry/core @opentelemetry/sdk-trace && pnpm add @opentelemetry/api@1.9.1 @opentelemetry/exporter-trace-otlp-http@^0.220.0 @opentelemetry/resources@^2.9.0 @opentelemetry/sdk-trace-base@^2.9.0 @opentelemetry/sdk-trace-node@^2.9.0 && pnpm dedupe && cp otel-v2/otel-tracer-provider.ts otel-tracer-provider.ts && pnpm build",
"test:build-webpack": "pnpm install && pnpm build-webpack",
"test:assert": "pnpm test:prod && pnpm test:dev",
"test:assert-webpack": "pnpm test:prod && pnpm test:dev-webpack"
},
"dependencies": {
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/exporter-trace-otlp-http": "^0.220.0",
"@opentelemetry/resources": "^2.9.0",
"@opentelemetry/sdk-trace-base": "^2.9.0",
"@opentelemetry/sdk-trace-node": "^2.9.0",
"@opentelemetry/api": "^1.10.0",
"@opentelemetry/context-async-hooks": "^3.0.0",
"@opentelemetry/core": "^3.0.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.300.0",
"@opentelemetry/resources": "^3.0.0",
"@opentelemetry/sdk-trace": "^3.0.0",
"@sentry/core": "file:../../packed/sentry-core-packed.tgz",
"@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz",
"import-in-the-middle": "^2",
Expand Down Expand Up @@ -51,6 +53,11 @@
"build-command": "pnpm test:build-webpack",
"label": "nextjs-otlp (webpack)",
"assert-command": "pnpm test:assert-webpack"
},
{
"build-command": "pnpm test:build:otel-v2",
"assert-command": "pnpm test:assert",
"label": "nextjs-otlp (OTel SDK v2)"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts", "**/*.mts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "otel-v2"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { SpanExporter } from '@opentelemetry/sdk-trace-base';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';

export function registerTracerProvider(exporter: SpanExporter): void {
new NodeTracerProvider({
spanProcessors: [new BatchSpanProcessor(exporter, { scheduledDelayMillis: 100 })],
}).register();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
"test": "playwright test",
"clean": "npx rimraf node_modules pnpm-lock.yaml",
"test:build": "pnpm install && pnpm build",
"test:build:otel-v2": "pnpm remove @opentelemetry/context-async-hooks @opentelemetry/core @opentelemetry/sdk-trace && pnpm add @opentelemetry/api@1.9.1 @opentelemetry/exporter-trace-otlp-http@^0.220.0 @opentelemetry/sdk-trace-base@^2.9.0 @opentelemetry/sdk-trace-node@^2.9.0 && pnpm dedupe && cp otel-v2/tracer-provider.ts src/tracer-provider.ts && pnpm build",
"test:assert": "pnpm test"
},
"dependencies": {
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/exporter-trace-otlp-http": "^0.220.0",
"@opentelemetry/sdk-trace-base": "^2.9.0",
"@opentelemetry/sdk-trace-node": "^2.9.0",
"@opentelemetry/api": "^1.10.0",
"@opentelemetry/context-async-hooks": "^3.0.0",
"@opentelemetry/core": "^3.0.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.300.0",
"@opentelemetry/sdk-trace": "^3.0.0",
"@sentry/node": "file:../../packed/sentry-node-packed.tgz",
"@types/express": "^4.17.21",
"@types/node": "^18.19.1",
Expand All @@ -27,5 +29,14 @@
},
"volta": {
"extends": "../../package.json"
},
"sentryTest": {
"variants": [
{
"build-command": "pnpm test:build:otel-v2",
"assert-command": "pnpm test:assert",
"label": "node-express-otlp (OTel SDK v2)"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { trace } from '@opentelemetry/api';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import * as Sentry from '@sentry/node';
import express from 'express';
import { registerTracerProvider } from './tracer-provider';

const dsn = process.env.E2E_TEST_DSN as string;
const appPort = 3030;
Expand All @@ -17,19 +16,12 @@ if (!otlpTracesEndpoint) {
// The user brings their own OpenTelemetry setup. In production `url` would be
// `otlpTracesEndpoint.url`; here it points at the local receiver below so the test can assert what
// was actually exported. The auth headers are the real DSN-derived ones either way.
const provider = new NodeTracerProvider({
spanProcessors: [
new BatchSpanProcessor(
new OTLPTraceExporter({
url: `http://localhost:${otlpReceiverPort}/v1/traces`,
headers: otlpTracesEndpoint.headers,
}),
{ scheduledDelayMillis: 100 },
),
],
});

provider.register();
registerTracerProvider(
new OTLPTraceExporter({
url: `http://localhost:${otlpReceiverPort}/v1/traces`,
headers: otlpTracesEndpoint.headers,
}),
);

Sentry.init({
dsn,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { context, propagation, trace } from '@opentelemetry/api';
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from '@opentelemetry/core';
import type { SpanExporter } from '@opentelemetry/sdk-trace';
import { BatchSpanProcessor, TracerProvider } from '@opentelemetry/sdk-trace';

// OpenTelemetry SDK v3 has no `NodeTracerProvider.register()`, so the app registers the tracer
// provider, context manager and propagator itself. The OTel SDK v2 variant replaces this file with
// `otel-v2/tracer-provider.ts`.
export function registerTracerProvider(exporter: SpanExporter): void {
trace.setGlobalTracerProvider(
new TracerProvider({
spanProcessors: [new BatchSpanProcessor({ exporter, scheduledDelayMillis: 100 })],
}),
);
context.setGlobalContextManager(new AsyncLocalStorageContextManager().enable());
propagation.setGlobalPropagator(
new CompositePropagator({ propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()] }),
);
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading