feat(core-shared): wire PII scrub processors FIRST in OTel pipeline

This commit is contained in:
2026-05-11 12:09:11 +02:00
parent 6ec5aeb31f
commit ad609f8f01
2 changed files with 13 additions and 9 deletions

View File

@@ -5,6 +5,7 @@ import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
import { PgInstrumentation } from "@opentelemetry/instrumentation-pg"; import { PgInstrumentation } from "@opentelemetry/instrumentation-pg";
import { buildResource } from "./resource"; import { buildResource } from "./resource";
import { createSentryOtelBridge } from "./sentry-bridge"; import { createSentryOtelBridge } from "./sentry-bridge";
import { PiiScrubSpanProcessor, PiiScrubLogRecordProcessor } from "./pii-scrub-processor";
const { BatchSpanProcessor } = tracing; const { BatchSpanProcessor } = tracing;
@@ -21,11 +22,11 @@ export type InitOtelServerNodeOpts = {
/** /**
* Initializes the OpenTelemetry NodeSDK for a server-side app. * Initializes the OpenTelemetry NodeSDK for a server-side app.
* - Configures Resource attributes per OTel semantic conventions. * - Configures Resource attributes per OTel semantic conventions.
* - Registers PII scrub processors FIRST so all downstream exporters see clean data.
* - Registers Sentry span processor (via createSentryOtelBridge) when DSN is set. * - Registers Sentry span processor (via createSentryOtelBridge) when DSN is set.
* - Registers Sentry log record processor when DSN is set. * - Registers Sentry log record processor when DSN is set.
* - Registers an in-process MeterProvider (no exporter — Sentry metrics not yet * - Registers an in-process MeterProvider (no exporter — Sentry metrics not yet
* wired; a future phase or vendor-specific exporter can add a MetricReader). * wired; a future phase or vendor-specific exporter can add a MetricReader).
* - PII scrub processors land in Phase 5.
* *
* Caller is responsible for `sdk.shutdown()` on process exit. * Caller is responsible for `sdk.shutdown()` on process exit.
*/ */
@@ -38,14 +39,17 @@ export function initOtelServerNode(opts: InitOtelServerNodeOpts): NodeSDK {
}); });
const bridge = createSentryOtelBridge({ dsn: opts.dsn }); const bridge = createSentryOtelBridge({ dsn: opts.dsn });
// PiiScrubSpanProcessor runs FIRST so the Sentry exporter never sees raw PII.
const spanProcessors = bridge.spanProcessor const spanProcessors = bridge.spanProcessor
? [new BatchSpanProcessor(bridge.spanProcessor as never)] ? [new PiiScrubSpanProcessor(), new BatchSpanProcessor(bridge.spanProcessor as never)]
: []; : [new PiiScrubSpanProcessor()];
// Register SentryLogRecordForwarder directly as a processor — it implements
// LogRecordProcessor (onEmit/forceFlush/shutdown) and forwards synchronously, // PiiScrubLogRecordProcessor runs FIRST for the same reason.
// so no batching wrapper is needed (unlike span processors that need batching // SentryLogRecordForwarder forwards synchronously (no batching wrapper needed).
// to avoid blocking the hot path). const logRecordProcessors = bridge.logRecordProcessor
const logRecordProcessors = bridge.logRecordProcessor ? [bridge.logRecordProcessor] : []; ? [new PiiScrubLogRecordProcessor(), bridge.logRecordProcessor]
: [new PiiScrubLogRecordProcessor()];
// In-process MeterProvider with no reader/exporter. Sentry metrics ingestion // In-process MeterProvider with no reader/exporter. Sentry metrics ingestion
// is experimental in @sentry/opentelemetry 10.x and not wired here; metrics // is experimental in @sentry/opentelemetry 10.x and not wired here; metrics

View File

@@ -9,7 +9,7 @@ import {
REDACTED_VALUE, REDACTED_VALUE,
keyContainsPii, keyContainsPii,
queryParamContainsPii, queryParamContainsPii,
} from "./pii-fields"; } from "../otel/pii-fields";
// Minimal structural types matching Sentry's event shape used in scrubbers. // Minimal structural types matching Sentry's event shape used in scrubbers.
// Using index signatures broad enough to satisfy both ErrorEvent and TransactionEvent. // Using index signatures broad enough to satisfy both ErrorEvent and TransactionEvent.