From e85b8b12cfda8d41682621e9c3195590c1b97342 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Mon, 11 May 2026 11:24:55 +0200 Subject: [PATCH] feat(core-shared): OTel NodeSDK init helper with Sentry exporter wiring --- .../otel/init-server-node.test.ts | 23 ++++++++++ .../instrumentation/otel/init-server-node.ts | 46 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 packages/core-shared/src/instrumentation/otel/init-server-node.test.ts create mode 100644 packages/core-shared/src/instrumentation/otel/init-server-node.ts diff --git a/packages/core-shared/src/instrumentation/otel/init-server-node.test.ts b/packages/core-shared/src/instrumentation/otel/init-server-node.test.ts new file mode 100644 index 0000000..054d281 --- /dev/null +++ b/packages/core-shared/src/instrumentation/otel/init-server-node.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect } from "vitest"; +import { initOtelServerNode } from "./init-server-node"; + +describe("initOtelServerNode", () => { + it("returns an SDK handle with shutdown()", () => { + const sdk = initOtelServerNode({ + dsn: "", + serviceName: "test-service", + environment: "test", + }); + expect(sdk).toBeDefined(); + expect(typeof sdk.shutdown).toBe("function"); + }); + + it("accepts a DSN and wires the Sentry bridge", () => { + const sdk = initOtelServerNode({ + dsn: "https://test@sentry.io/1", + serviceName: "test-service", + environment: "test", + }); + expect(sdk).toBeDefined(); + }); +}); diff --git a/packages/core-shared/src/instrumentation/otel/init-server-node.ts b/packages/core-shared/src/instrumentation/otel/init-server-node.ts new file mode 100644 index 0000000..bb4e35d --- /dev/null +++ b/packages/core-shared/src/instrumentation/otel/init-server-node.ts @@ -0,0 +1,46 @@ +import { NodeSDK } from "@opentelemetry/sdk-node"; +import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base"; +import { buildResource } from "./resource"; +import { createSentryOtelBridge } from "./sentry-bridge"; + +export type InitOtelServerNodeOpts = { + /** Sentry DSN. When empty, OTel SDK boots without the Sentry exporter. */ + dsn: string; + serviceName: string; + serviceVersion?: string; + environment: string; + namespace?: string; +}; + +/** + * Initializes the OpenTelemetry NodeSDK for a server-side app. + * - Configures Resource attributes per OTel semantic conventions. + * - Registers Sentry span processor (via createSentryOtelBridge) when DSN is set. + * - logRecordProcessors filled in Phase 3; metricReader filled in Phase 4. + * - PII scrub processors land in Phase 5. + * + * Caller is responsible for `sdk.shutdown()` on process exit. + */ +export function initOtelServerNode(opts: InitOtelServerNodeOpts): NodeSDK { + const resource = buildResource({ + serviceName: opts.serviceName, + serviceVersion: opts.serviceVersion, + environment: opts.environment, + namespace: opts.namespace, + }); + + const bridge = createSentryOtelBridge({ dsn: opts.dsn }); + const spanProcessors = bridge.spanProcessor + ? [new BatchSpanProcessor(bridge.spanProcessor as never)] + : []; + + const sdk = new NodeSDK({ + resource, + spanProcessors, + // logRecordProcessors filled in Phase 3 + // metricReader filled in Phase 4 + }); + + sdk.start(); + return sdk; +}