From 9b8cdd4861e94d0bfd9bfb11d4af51e628b839db Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 21 Jul 2026 16:28:03 +0200 Subject: [PATCH 1/3] refactor(server-utils): Extract shared safeChannelCallback helper Co-Authored-By: Claude Opus 4.8 (1M context) --- .../tracing-channel/aws-sdk/index.ts | 12 +----------- .../firebase/instrumentation.ts | 18 +----------------- .../tracing-channel/graphql/index.ts | 19 ++----------------- packages/server-utils/src/tracing-channel.ts | 10 ++++++++++ 4 files changed, 14 insertions(+), 45 deletions(-) diff --git a/packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts b/packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts index 4b3d85700653..a26142a982f1 100644 --- a/packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts +++ b/packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts @@ -17,7 +17,7 @@ import { import { DEBUG_BUILD } from '../../../debug-build'; import { CHANNELS } from '../../../orchestrion/channels'; import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel'; -import { bindTracingChannelToSpan } from '../../../tracing-channel'; +import { bindTracingChannelToSpan, safeChannelCallback as safe } from '../../../tracing-channel'; import { AWS_SDK_ORIGIN } from './constants'; import { ServicesExtensions } from './services'; import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types'; @@ -49,16 +49,6 @@ interface AwsV3Command { constructor?: { name?: string }; } -/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */ -function safe(fn: () => T): T | undefined { - try { - return fn(); - } catch (error) { - DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error); - return undefined; - } -} - // `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the // same reason as `CommandInput`, see types.ts). function setMetadataAttributes(span: Span, metadata: Record | undefined): void { diff --git a/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts b/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts index 2d59a03ae8e2..94726985c335 100644 --- a/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts +++ b/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts @@ -1,8 +1,6 @@ import * as diagnosticsChannel from 'node:diagnostics_channel'; -import { debug } from '@sentry/core'; -import { DEBUG_BUILD } from '../../../debug-build'; import { CHANNELS } from '../../../orchestrion/channels'; -import { bindTracingChannelToSpan } from '../../../tracing-channel'; +import { bindTracingChannelToSpan, safeChannelCallback as safe } from '../../../tracing-channel'; import type { FirestoreReference } from './firestore-types'; import { startFirestoreSpan } from './firestore'; import { wrapFunctionsRegistration } from './functions'; @@ -43,20 +41,6 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [ const NOOP = (): void => {}; -/** - * Runs a span-building callback so a throw inside it can never break the user's firebase call: these run - * inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would - * propagate into the traced call. - */ -function safe(fn: () => T): T | undefined { - try { - return fn(); - } catch (error) { - DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error); - return undefined; - } -} - export function instrumentFirebase() { for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) { bindTracingChannelToSpan(diagnosticsChannel.tracingChannel(channel), data => diff --git a/packages/server-utils/src/integrations/tracing-channel/graphql/index.ts b/packages/server-utils/src/integrations/tracing-channel/graphql/index.ts index 2a3baa669e16..7591c487fcde 100644 --- a/packages/server-utils/src/integrations/tracing-channel/graphql/index.ts +++ b/packages/server-utils/src/integrations/tracing-channel/graphql/index.ts @@ -1,11 +1,10 @@ import * as diagnosticsChannel from 'node:diagnostics_channel'; import type { IntegrationFn } from '@sentry/core'; -import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core'; -import { DEBUG_BUILD } from '../../../debug-build'; +import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core'; import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql'; import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber'; import { CHANNELS } from '../../../orchestrion/channels'; -import { bindTracingChannelToSpan } from '../../../tracing-channel'; +import { bindTracingChannelToSpan, safeChannelCallback as safe } from '../../../tracing-channel'; import { finalizeExecuteSpan, finalizeValidateSpan, @@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap }; } -/** - * Runs a span-building callback so a throw inside it can never break the user's graphql call: these - * run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan` - * producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call. - */ -function safe(fn: () => T): T | undefined { - try { - return fn(); - } catch (error) { - DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error); - return undefined; - } -} - const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => { const config = getOptionsWithDefaults(options); const getConfig = (): GraphqlResolvedConfig => config; diff --git a/packages/server-utils/src/tracing-channel.ts b/packages/server-utils/src/tracing-channel.ts index 097a3da6018a..c44faf7ccb8c 100644 --- a/packages/server-utils/src/tracing-channel.ts +++ b/packages/server-utils/src/tracing-channel.ts @@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle { const NOOP = (): void => {}; +/** Runs a span-building callback so a throw inside it can never break the user's traced call. */ +export function safeChannelCallback(fn: () => T): T | undefined { + try { + return fn(); + } catch (error) { + DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error); + return undefined; + } +} + /** * Bind a span and its lifecycle to a tracing channel so the span becomes the active async context * for the traced operation and is ended when the operation completes. From 34f60c3e814298946716376696b1b8aedb999a15 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 21 Jul 2026 16:34:41 +0200 Subject: [PATCH 2/3] refactor(server-utils): Use safeChannelCallback directly without alias Co-Authored-By: Claude Opus 4.8 (1M context) --- .../integrations/tracing-channel/aws-sdk/index.ts | 10 +++++----- .../tracing-channel/firebase/instrumentation.ts | 6 +++--- .../integrations/tracing-channel/graphql/index.ts | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts b/packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts index a26142a982f1..4ed048a9de1f 100644 --- a/packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts +++ b/packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts @@ -17,7 +17,7 @@ import { import { DEBUG_BUILD } from '../../../debug-build'; import { CHANNELS } from '../../../orchestrion/channels'; import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel'; -import { bindTracingChannelToSpan, safeChannelCallback as safe } from '../../../tracing-channel'; +import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel'; import { AWS_SDK_ORIGIN } from './constants'; import { ServicesExtensions } from './services'; import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types'; @@ -81,7 +81,7 @@ const _awsChannelIntegration = (() => { } const getSpan = (data: AwsSendChannelContext): Span | undefined => - safe(() => { + safeChannelCallback(() => { const command = data.arguments[0] as AwsV3Command | undefined; const commandName = command?.constructor?.name; if (!command || !commandName) { @@ -130,7 +130,7 @@ const _awsChannelIntegration = (() => { // so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure). // // The provider call is guarded separately: the span is already started, so a synchronous - // throw bubbling into the enclosing `safe` would discard it without ending it (a leaked + // throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked // open span). let regionResult: string | Promise | undefined; try { @@ -159,7 +159,7 @@ const _awsChannelIntegration = (() => { // Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before // `send` proceeds, so the mutated `commandInput` is used to build the request. - safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span)); + safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span)); return span; }); @@ -176,7 +176,7 @@ const _awsChannelIntegration = (() => { // The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's // `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`). - safe(() => { + safeChannelCallback(() => { if (failed) { const err = data.error as | { $metadata?: Record; RequestId?: string; extendedRequestId?: string } diff --git a/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts b/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts index 94726985c335..c3f91a1e4f31 100644 --- a/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts +++ b/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts @@ -1,6 +1,6 @@ import * as diagnosticsChannel from 'node:diagnostics_channel'; import { CHANNELS } from '../../../orchestrion/channels'; -import { bindTracingChannelToSpan, safeChannelCallback as safe } from '../../../tracing-channel'; +import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel'; import type { FirestoreReference } from './firestore-types'; import { startFirestoreSpan } from './firestore'; import { wrapFunctionsRegistration } from './functions'; @@ -44,7 +44,7 @@ const NOOP = (): void => {}; export function instrumentFirebase() { for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) { bindTracingChannelToSpan(diagnosticsChannel.tracingChannel(channel), data => - safe(() => { + safeChannelCallback(() => { const reference = data.arguments[0] as FirestoreReference | undefined; if (!reference) { return undefined; @@ -60,7 +60,7 @@ export function instrumentFirebase() { // registration call, so we only rewrap the handler argument here (in `start`) and open the // span inside that wrapper. The other lifecycle events are irrelevant, so no-op them. diagnosticsChannel.tracingChannel(channel).subscribe({ - start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)), + start: data => void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)), end: NOOP, asyncStart: NOOP, asyncEnd: NOOP, diff --git a/packages/server-utils/src/integrations/tracing-channel/graphql/index.ts b/packages/server-utils/src/integrations/tracing-channel/graphql/index.ts index 7591c487fcde..ecfd91c2785b 100644 --- a/packages/server-utils/src/integrations/tracing-channel/graphql/index.ts +++ b/packages/server-utils/src/integrations/tracing-channel/graphql/index.ts @@ -4,7 +4,7 @@ import { defineIntegration, extendIntegration, waitForTracingChannelBinding } fr import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql'; import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber'; import { CHANNELS } from '../../../orchestrion/channels'; -import { bindTracingChannelToSpan, safeChannelCallback as safe } from '../../../tracing-channel'; +import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel'; import { finalizeExecuteSpan, finalizeValidateSpan, @@ -47,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = waitForTracingChannelBinding(() => { bindTracingChannelToSpan(diagnosticsChannel.tracingChannel(CHANNELS.GRAPHQL_PARSE), () => - safe(() => startParseSpan()), + safeChannelCallback(() => startParseSpan()), ); bindTracingChannelToSpan( diagnosticsChannel.tracingChannel(CHANNELS.GRAPHQL_VALIDATE), - data => safe(() => startValidateSpan(data.arguments[1])), - { beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) }, + data => safeChannelCallback(() => startValidateSpan(data.arguments[1])), + { beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) }, ); bindTracingChannelToSpan( diagnosticsChannel.tracingChannel(CHANNELS.GRAPHQL_EXECUTE), - data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)), - { beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) }, + data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)), + { beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) }, ); }); }, From a0da4c09858de78877c70919cffc74235a8fca27 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 21 Jul 2026 16:50:56 +0200 Subject: [PATCH 3/3] style(server-utils): Format firebase instrumentation Co-Authored-By: Claude Opus 4.8 (1M context) --- .../integrations/tracing-channel/firebase/instrumentation.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts b/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts index c3f91a1e4f31..6a122bca4ff2 100644 --- a/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts +++ b/packages/server-utils/src/integrations/tracing-channel/firebase/instrumentation.ts @@ -60,7 +60,8 @@ export function instrumentFirebase() { // registration call, so we only rewrap the handler argument here (in `start`) and open the // span inside that wrapper. The other lifecycle events are irrelevant, so no-op them. diagnosticsChannel.tracingChannel(channel).subscribe({ - start: data => void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)), + start: data => + void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)), end: NOOP, asyncStart: NOOP, asyncEnd: NOOP,