diff --git a/dev-packages/node-integration-tests/suites/breadcrumbs/process-thread/app.mjs b/dev-packages/node-integration-tests/suites/breadcrumbs/process-thread/app.mjs index b832bae748d5..987fffeb6d92 100644 --- a/dev-packages/node-integration-tests/suites/breadcrumbs/process-thread/app.mjs +++ b/dev-packages/node-integration-tests/suites/breadcrumbs/process-thread/app.mjs @@ -10,7 +10,7 @@ Sentry.init({ traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', - integrations: [Sentry.childProcessIntegration({ captureWorkerErrors: false })], + integrations: [Sentry.childProcessIntegration(), Sentry.workerIntegration({ captureWorkerErrors: false })], transport: loggingTransport, }); diff --git a/dev-packages/node-integration-tests/suites/child-process/test.ts b/dev-packages/node-integration-tests/suites/child-process/test.ts index 1d04772c351e..efeb4b297ab9 100644 --- a/dev-packages/node-integration-tests/suites/child-process/test.ts +++ b/dev-packages/node-integration-tests/suites/child-process/test.ts @@ -10,7 +10,7 @@ const WORKER_EVENT: Event = { type: 'Error', value: 'Test error', mechanism: { - type: 'auto.child_process.worker_thread', + type: 'auto.worker_thread', handled: false, data: { threadId: expect.any(String), diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 30f597757b40..fc43622d9e23 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -120,6 +120,7 @@ export { processSessionIntegration, prismaIntegration, childProcessIntegration, + workerIntegration, createSentryWinstonTransport, hapiIntegration, setupHapiErrorHandler, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index d07490563212..ef7771521856 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -142,6 +142,7 @@ export { anthropicAIIntegration, googleGenAIIntegration, childProcessIntegration, + workerIntegration, createSentryWinstonTransport, vercelAIIntegration, logger, diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index bf8dd5d7533e..067b714e080a 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -178,6 +178,7 @@ export { metrics, withStreamedSpan } from '@sentry/core'; export * as logger from './logs/exports'; export { childProcessIntegration } from './integrations/childProcess'; +export { workerIntegration } from './integrations/workerIntegration'; export { consoleIntegration } from './integrations/console'; export { nodeContextIntegration } from './integrations/context'; export { contextLinesIntegration } from './integrations/contextlines'; diff --git a/packages/node/src/integrations/childProcess.ts b/packages/node/src/integrations/childProcess.ts index 1c4cf4a38966..1dae2eeab560 100644 --- a/packages/node/src/integrations/childProcess.ts +++ b/packages/node/src/integrations/childProcess.ts @@ -1,7 +1,6 @@ import type { ChildProcess } from 'node:child_process'; import * as diagnosticsChannel from 'node:diagnostics_channel'; -import type { Worker } from 'node:worker_threads'; -import { addBreadcrumb, captureException, defineIntegration, isObjectLike } from '@sentry/core'; +import { addBreadcrumb, defineIntegration, isObjectLike } from '@sentry/core'; interface Options { /** @@ -12,9 +11,8 @@ interface Options { includeChildProcessArgs?: boolean; /** - * Whether to capture errors from worker threads. - * - * @default true + * @deprecated Use `workerIntegration({ captureWorkerErrors })` instead. + * This option is no longer used by childProcessIntegration. */ captureWorkerErrors?: boolean; } @@ -22,7 +20,8 @@ interface Options { const INTEGRATION_NAME = 'ChildProcess' as const; /** - * Capture breadcrumbs and events for child processes and worker threads. + * Capture breadcrumbs and events for child processes. + * For worker thread events, use `workerIntegration()` instead. */ export const childProcessIntegration = defineIntegration((options: Options = {}) => { return { @@ -33,12 +32,6 @@ export const childProcessIntegration = defineIntegration((options: Options = {}) captureChildProcessEvents(event.process as ChildProcess, options); } }); - - diagnosticsChannel.channel('worker_threads').subscribe((event: unknown) => { - if (isObjectLike(event) && 'worker' in event) { - captureWorkerThreadEvents(event.worker as Worker, options); - } - }); }, }; }); @@ -89,25 +82,3 @@ function captureChildProcessEvents(child: ChildProcess, options: Options): void }); } -function captureWorkerThreadEvents(worker: Worker, options: Options): void { - let threadId: number | undefined; - - worker - .on('online', () => { - threadId = worker.threadId; - }) - .on('error', error => { - if (options.captureWorkerErrors !== false) { - captureException(error, { - mechanism: { type: 'auto.child_process.worker_thread', handled: false, data: { threadId: String(threadId) } }, - }); - } else { - addBreadcrumb({ - category: 'worker_thread', - message: `Worker thread errored with '${error.message}'`, - level: 'error', - data: { threadId }, - }); - } - }); -} diff --git a/packages/node/src/integrations/workerIntegration.ts b/packages/node/src/integrations/workerIntegration.ts new file mode 100644 index 000000000000..adfdc4f092eb --- /dev/null +++ b/packages/node/src/integrations/workerIntegration.ts @@ -0,0 +1,53 @@ +import type { Worker } from 'node:worker_threads'; +import * as diagnosticsChannel from 'node:diagnostics_channel'; +import { addBreadcrumb, captureException, defineIntegration, isObjectLike } from '@sentry/core'; + +interface Options { + /** + * Whether to capture errors from worker threads. + * + * @default true + */ + captureWorkerErrors?: boolean; +} + +const INTEGRATION_NAME = 'Worker' as const; + +/** + * Capture breadcrumbs and events for worker threads. + */ +export const workerIntegration = defineIntegration((options: Options = {}) => { + return { + name: INTEGRATION_NAME, + setup() { + diagnosticsChannel.channel('worker_threads').subscribe((event: unknown) => { + if (isObjectLike(event) && 'worker' in event) { + captureWorkerThreadEvents(event.worker as Worker, options); + } + }); + }, + }; +}); + +function captureWorkerThreadEvents(worker: Worker, options: Options): void { + let threadId: number | undefined; + + worker + .on('online', () => { + threadId = worker.threadId; + }) + .on('error', error => { + if (options.captureWorkerErrors !== false) { + captureException(error, { + mechanism: { type: 'auto.worker_thread', handled: false, data: { threadId: threadId !== undefined ? String(threadId) : undefined } }, + }); + } else { + addBreadcrumb({ + category: 'worker_thread', + message: `Worker thread errored with '${error.message}'`, + level: 'error', + data: { threadId }, + }); + } + }); +} diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts index bcf8f48bd44b..26d6dcf87889 100644 --- a/packages/node/src/sdk/index.ts +++ b/packages/node/src/sdk/index.ts @@ -21,6 +21,7 @@ import { detectOrchestrionSetup } from '@sentry/server-utils/orchestrion'; import { registerDiagnosticsChannelInjection } from '@sentry/server-utils/orchestrion/register'; import { DEBUG_BUILD } from '../debug-build'; import { childProcessIntegration } from '../integrations/childProcess'; +import { workerIntegration } from '../integrations/workerIntegration'; import { consoleIntegration } from '../integrations/console'; import { nodeContextIntegration } from '../integrations/context'; import { contextLinesIntegration } from '../integrations/contextlines'; @@ -68,6 +69,7 @@ function getBaseDefaultIntegrations(): Integration[] { localVariablesIntegration(), nodeContextIntegration(), childProcessIntegration(), + workerIntegration(), processSessionIntegration(), modulesIntegration(), ];