@sentry/node 9.40.0 → 9.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,19 +7,52 @@ const debugBuild = require('../../../debug-build.js');
7
7
  const index = require('./fastify-otel/index.js');
8
8
  const instrumentation = require('./v3/instrumentation.js');
9
9
 
10
+ /**
11
+ * Options for the Fastify integration.
12
+ *
13
+ * `shouldHandleError` - Callback method deciding whether error should be captured and sent to Sentry
14
+ * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.
15
+ * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.
16
+ *
17
+ * @example
18
+ *
19
+ * ```javascript
20
+ * Sentry.init({
21
+ * integrations: [
22
+ * Sentry.fastifyIntegration({
23
+ * shouldHandleError(_error, _request, reply) {
24
+ * return reply.statusCode >= 500;
25
+ * },
26
+ * });
27
+ * },
28
+ * });
29
+ * ```
30
+ *
31
+ */
32
+
10
33
  const INTEGRATION_NAME = 'Fastify';
34
+ const INTEGRATION_NAME_V5 = 'Fastify-V5';
11
35
  const INTEGRATION_NAME_V3 = 'Fastify-V3';
12
36
 
13
37
  const instrumentFastifyV3 = nodeCore.generateInstrumentOnce(INTEGRATION_NAME_V3, () => new instrumentation.FastifyInstrumentationV3());
14
38
 
39
+ function getFastifyIntegration() {
40
+ const client = core.getClient();
41
+ if (!client) {
42
+ return undefined;
43
+ } else {
44
+ return client.getIntegrationByName(INTEGRATION_NAME) ;
45
+ }
46
+ }
47
+
15
48
  function handleFastifyError(
16
49
 
17
50
  error,
18
51
  request,
19
52
  reply,
20
- shouldHandleError,
21
53
  handlerOrigin,
22
54
  ) {
55
+ const shouldHandleError = getFastifyIntegration()?.getShouldHandleError() || defaultShouldHandleError;
23
56
  // Diagnostics channel runs before the onError hook, so we can use it to check if the handler was already registered
24
57
  if (handlerOrigin === 'diagnostics-channel') {
25
58
  this.diagnosticsChannelExists = true;
@@ -29,7 +62,7 @@ function handleFastifyError(
29
62
  debugBuild.DEBUG_BUILD &&
30
63
  core.debug.warn(
31
64
  'Fastify error handler was already registered via diagnostics channel.',
32
- 'You can safely remove `setupFastifyErrorHandler` call.',
65
+ 'You can safely remove `setupFastifyErrorHandler` call and set `shouldHandleError` on the integration options.',
33
66
  );
34
67
 
35
68
  // If the diagnostics channel already exists, we don't need to handle the error again
@@ -41,11 +74,9 @@ function handleFastifyError(
41
74
  }
42
75
  }
43
76
 
44
- const instrumentFastify = nodeCore.generateInstrumentOnce(INTEGRATION_NAME, () => {
77
+ const instrumentFastify = nodeCore.generateInstrumentOnce(INTEGRATION_NAME_V5, () => {
45
78
  const fastifyOtelInstrumentationInstance = new index.FastifyOtelInstrumentation();
46
79
  const plugin = fastifyOtelInstrumentationInstance.plugin();
47
- const options = fastifyOtelInstrumentationInstance.getConfig();
48
- const shouldHandleError = (options )?.shouldHandleError || defaultShouldHandleError;
49
80
 
50
81
  // This message handler works for Fastify versions 3, 4 and 5
51
82
  dc.subscribe('fastify.initialization', message => {
@@ -71,20 +102,30 @@ const instrumentFastify = nodeCore.generateInstrumentOnce(INTEGRATION_NAME, () =
71
102
 
72
103
  ;
73
104
 
74
- handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'diagnostics-channel');
105
+ handleFastifyError.call(handleFastifyError, error, request, reply, 'diagnostics-channel');
75
106
  });
76
107
 
77
108
  // Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation
78
109
  return fastifyOtelInstrumentationInstance ;
79
110
  });
80
111
 
81
- const _fastifyIntegration = (() => {
112
+ const _fastifyIntegration = (({ shouldHandleError }) => {
113
+ let _shouldHandleError;
114
+
82
115
  return {
83
116
  name: INTEGRATION_NAME,
84
117
  setupOnce() {
118
+ _shouldHandleError = shouldHandleError || defaultShouldHandleError;
119
+
85
120
  instrumentFastifyV3();
86
121
  instrumentFastify();
87
122
  },
123
+ getShouldHandleError() {
124
+ return _shouldHandleError;
125
+ },
126
+ setShouldHandleError(fn) {
127
+ _shouldHandleError = fn;
128
+ },
88
129
  };
89
130
  }) ;
90
131
 
@@ -104,7 +145,9 @@ const _fastifyIntegration = (() => {
104
145
  * })
105
146
  * ```
106
147
  */
107
- const fastifyIntegration = core.defineIntegration(_fastifyIntegration);
148
+ const fastifyIntegration = core.defineIntegration((options = {}) =>
149
+ _fastifyIntegration(options),
150
+ );
108
151
 
109
152
  /**
110
153
  * Default function to determine if an error should be sent to Sentry
@@ -138,11 +181,14 @@ function defaultShouldHandleError(_error, _request, reply) {
138
181
  * ```
139
182
  */
140
183
  function setupFastifyErrorHandler(fastify, options) {
141
- const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError;
184
+ if (options?.shouldHandleError) {
185
+ getFastifyIntegration()?.setShouldHandleError(options.shouldHandleError);
186
+ }
187
+
142
188
  const plugin = Object.assign(
143
189
  function (fastify, _options, done) {
144
190
  fastify.addHook('onError', async (request, reply, error) => {
145
- handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'onError-hook');
191
+ handleFastifyError.call(handleFastifyError, error, request, reply, 'onError-hook');
146
192
  });
147
193
  done();
148
194
  },
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/fastify/index.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n captureException,\n debug,\n defineIntegration,\n getClient,\n getIsolationScope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n} from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { FastifyOtelInstrumentation } from './fastify-otel/index';\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from './types';\nimport { FastifyInstrumentationV3 } from './v3/instrumentation';\n\ninterface FastifyHandlerOptions {\n /**\n * Callback method deciding whether error should be captured and sent to Sentry\n *\n * @param error Captured Fastify error\n * @param request Fastify request (or any object containing at least method, routeOptions.url, and routerPath)\n * @param reply Fastify reply (or any object containing at least statusCode)\n *\n * @example\n *\n * ```javascript\n * setupFastifyErrorHandler(app, {\n * shouldHandleError(_error, _request, reply) {\n * return reply.statusCode >= 400;\n * },\n * });\n * ```\n *\n * If using TypeScript, you can cast the request and reply to get full type safety.\n *\n * ```typescript\n * import type { FastifyRequest, FastifyReply } from 'fastify';\n *\n * setupFastifyErrorHandler(app, {\n * shouldHandleError(error, minimalRequest, minimalReply) {\n * const request = minimalRequest as FastifyRequest;\n * const reply = minimalReply as FastifyReply;\n * return reply.statusCode >= 500;\n * },\n * });\n * ```\n */\n shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;\n}\n\nconst INTEGRATION_NAME = 'Fastify';\nconst INTEGRATION_NAME_V3 = 'Fastify-V3';\n\nexport const instrumentFastifyV3 = generateInstrumentOnce(INTEGRATION_NAME_V3, () => new FastifyInstrumentationV3());\n\nfunction handleFastifyError(\n this: {\n diagnosticsChannelExists?: boolean;\n },\n error: Error,\n request: FastifyRequest & { opentelemetry?: () => { span?: Span } },\n reply: FastifyReply,\n shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean,\n handlerOrigin: 'diagnostics-channel' | 'onError-hook',\n): void {\n // Diagnostics channel runs before the onError hook, so we can use it to check if the handler was already registered\n if (handlerOrigin === 'diagnostics-channel') {\n this.diagnosticsChannelExists = true;\n }\n\n if (this.diagnosticsChannelExists && handlerOrigin === 'onError-hook') {\n DEBUG_BUILD &&\n debug.warn(\n 'Fastify error handler was already registered via diagnostics channel.',\n 'You can safely remove `setupFastifyErrorHandler` call.',\n );\n\n // If the diagnostics channel already exists, we don't need to handle the error again\n return;\n }\n\n if (shouldHandleError(error, request, reply)) {\n captureException(error);\n }\n}\n\nexport const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME, () => {\n const fastifyOtelInstrumentationInstance = new FastifyOtelInstrumentation();\n const plugin = fastifyOtelInstrumentationInstance.plugin();\n const options = fastifyOtelInstrumentationInstance.getConfig();\n const shouldHandleError = (options as FastifyHandlerOptions)?.shouldHandleError || defaultShouldHandleError;\n\n // This message handler works for Fastify versions 3, 4 and 5\n diagnosticsChannel.subscribe('fastify.initialization', message => {\n const fastifyInstance = (message as { fastify?: FastifyInstance }).fastify;\n\n fastifyInstance?.register(plugin).after(err => {\n if (err) {\n DEBUG_BUILD && debug.error('Failed to setup Fastify instrumentation', err);\n } else {\n instrumentClient();\n\n if (fastifyInstance) {\n instrumentOnRequest(fastifyInstance);\n }\n }\n });\n });\n\n // This diagnostics channel only works on Fastify version 5\n // For versions 3 and 4, we use `setupFastifyErrorHandler` instead\n diagnosticsChannel.subscribe('tracing:fastify.request.handler:error', message => {\n const { error, request, reply } = message as {\n error: Error;\n request: FastifyRequest & { opentelemetry?: () => { span?: Span } };\n reply: FastifyReply;\n };\n\n handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'diagnostics-channel');\n });\n\n // Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation\n return fastifyOtelInstrumentationInstance as Instrumentation<InstrumentationConfig & FastifyHandlerOptions>;\n});\n\nconst _fastifyIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentFastifyV3();\n instrumentFastify();\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for [Fastify](https://fastify.dev/).\n *\n * If you also want to capture errors, you need to call `setupFastifyErrorHandler(app)` after you set up your Fastify server.\n *\n * For more information, see the [fastify documentation](https://docs.sentry.io/platforms/javascript/guides/fastify/).\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.fastifyIntegration()],\n * })\n * ```\n */\nexport const fastifyIntegration = defineIntegration(_fastifyIntegration);\n\n/**\n * Default function to determine if an error should be sent to Sentry\n *\n * 3xx and 4xx errors are not sent by default.\n */\nfunction defaultShouldHandleError(_error: Error, _request: FastifyRequest, reply: FastifyReply): boolean {\n const statusCode = reply.statusCode;\n // 3xx and 4xx errors are not sent by default.\n return statusCode >= 500 || statusCode <= 299;\n}\n\n/**\n * Add an Fastify error handler to capture errors to Sentry.\n *\n * @param fastify The Fastify instance to which to add the error handler\n * @param options Configuration options for the handler\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n * const Fastify = require(\"fastify\");\n *\n * const app = Fastify();\n *\n * Sentry.setupFastifyErrorHandler(app);\n *\n * // Add your routes, etc.\n *\n * app.listen({ port: 3000 });\n * ```\n */\nexport function setupFastifyErrorHandler(fastify: FastifyInstance, options?: Partial<FastifyHandlerOptions>): void {\n const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError;\n const plugin = Object.assign(\n function (fastify: FastifyInstance, _options: unknown, done: () => void): void {\n fastify.addHook('onError', async (request, reply, error) => {\n handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'onError-hook');\n });\n done();\n },\n {\n [Symbol.for('skip-override')]: true,\n [Symbol.for('fastify.display-name')]: 'sentry-fastify-error-handler',\n },\n );\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n fastify.register(plugin);\n}\n\nfunction addFastifySpanAttributes(span: Span): void {\n const spanJSON = spanToJSON(span);\n const spanName = spanJSON.description;\n const attributes = spanJSON.data;\n\n const type = attributes['fastify.type'];\n\n const isHook = type === 'hook';\n const isHandler = type === spanName?.startsWith('handler -');\n // In @fastify/otel `request-handler` is separated by dash, not underscore\n const isRequestHandler = spanName === 'request' || type === 'request-handler';\n\n // If this is already set, or we have no fastify span, no need to process again...\n if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || (!isHandler && !isRequestHandler && !isHook)) {\n return;\n }\n\n const opPrefix = isHook ? 'hook' : isHandler ? 'middleware' : isRequestHandler ? 'request-handler' : '<unknown>';\n\n span.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.fastify',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${opPrefix}.fastify`,\n });\n\n const attrName = attributes['fastify.name'] || attributes['plugin.name'] || attributes['hook.name'];\n if (typeof attrName === 'string') {\n // Try removing `fastify -> ` and `@fastify/otel -> ` prefixes\n // This is a bit of a hack, and not always working for all spans\n // But it's the best we can do without a proper API\n const updatedName = attrName.replace(/^fastify -> /, '').replace(/^@fastify\\/otel -> /, '');\n\n span.updateName(updatedName);\n }\n}\n\nfunction instrumentClient(): void {\n const client = getClient();\n if (client) {\n client.on('spanStart', (span: Span) => {\n addFastifySpanAttributes(span);\n });\n }\n}\n\nfunction instrumentOnRequest(fastify: FastifyInstance): void {\n fastify.addHook('onRequest', async (request: FastifyRequest & { opentelemetry?: () => { span?: Span } }, _reply) => {\n if (request.opentelemetry) {\n const { span } = request.opentelemetry();\n\n if (span) {\n addFastifySpanAttributes(span);\n }\n }\n\n const routeName = request.routeOptions?.url;\n const method = request.method || 'GET';\n\n getIsolationScope().setTransactionName(`${method} ${routeName}`);\n });\n}\n"],"names":["generateInstrumentOnce","FastifyInstrumentationV3","DEBUG_BUILD","debug","captureException","FastifyOtelInstrumentation","diagnosticsChannel","defineIntegration","spanToJSON","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","getClient","getIsolationScope"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/fastify/index.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n captureException,\n debug,\n defineIntegration,\n getClient,\n getIsolationScope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n} from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { FastifyOtelInstrumentation } from './fastify-otel/index';\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from './types';\nimport { FastifyInstrumentationV3 } from './v3/instrumentation';\n\n/**\n * Options for the Fastify integration.\n *\n * `shouldHandleError` - Callback method deciding whether error should be captured and sent to Sentry\n * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.\n * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.\n *\n * @example\n *\n * ```javascript\n * Sentry.init({\n * integrations: [\n * Sentry.fastifyIntegration({\n * shouldHandleError(_error, _request, reply) {\n * return reply.statusCode >= 500;\n * },\n * });\n * },\n * });\n * ```\n *\n */\ninterface FastifyIntegrationOptions {\n /**\n * Callback method deciding whether error should be captured and sent to Sentry\n * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.\n * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.\n *\n * @param error Captured Fastify error\n * @param request Fastify request (or any object containing at least method, routeOptions.url, and routerPath)\n * @param reply Fastify reply (or any object containing at least statusCode)\n */\n shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;\n}\n\ninterface FastifyHandlerOptions {\n /**\n * Callback method deciding whether error should be captured and sent to Sentry\n *\n * @param error Captured Fastify error\n * @param request Fastify request (or any object containing at least method, routeOptions.url, and routerPath)\n * @param reply Fastify reply (or any object containing at least statusCode)\n *\n * @example\n *\n *\n * ```javascript\n * setupFastifyErrorHandler(app, {\n * shouldHandleError(_error, _request, reply) {\n * return reply.statusCode >= 400;\n * },\n * });\n * ```\n *\n *\n * If using TypeScript, you can cast the request and reply to get full type safety.\n *\n * ```typescript\n * import type { FastifyRequest, FastifyReply } from 'fastify';\n *\n * setupFastifyErrorHandler(app, {\n * shouldHandleError(error, minimalRequest, minimalReply) {\n * const request = minimalRequest as FastifyRequest;\n * const reply = minimalReply as FastifyReply;\n * return reply.statusCode >= 500;\n * },\n * });\n * ```\n */\n shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;\n}\n\nconst INTEGRATION_NAME = 'Fastify';\nconst INTEGRATION_NAME_V5 = 'Fastify-V5';\nconst INTEGRATION_NAME_V3 = 'Fastify-V3';\n\nexport const instrumentFastifyV3 = generateInstrumentOnce(INTEGRATION_NAME_V3, () => new FastifyInstrumentationV3());\n\nfunction getFastifyIntegration(): ReturnType<typeof _fastifyIntegration> | undefined {\n const client = getClient();\n if (!client) {\n return undefined;\n } else {\n return client.getIntegrationByName(INTEGRATION_NAME) as ReturnType<typeof _fastifyIntegration> | undefined;\n }\n}\n\nfunction handleFastifyError(\n this: {\n diagnosticsChannelExists?: boolean;\n },\n error: Error,\n request: FastifyRequest & { opentelemetry?: () => { span?: Span } },\n reply: FastifyReply,\n handlerOrigin: 'diagnostics-channel' | 'onError-hook',\n): void {\n const shouldHandleError = getFastifyIntegration()?.getShouldHandleError() || defaultShouldHandleError;\n // Diagnostics channel runs before the onError hook, so we can use it to check if the handler was already registered\n if (handlerOrigin === 'diagnostics-channel') {\n this.diagnosticsChannelExists = true;\n }\n\n if (this.diagnosticsChannelExists && handlerOrigin === 'onError-hook') {\n DEBUG_BUILD &&\n debug.warn(\n 'Fastify error handler was already registered via diagnostics channel.',\n 'You can safely remove `setupFastifyErrorHandler` call and set `shouldHandleError` on the integration options.',\n );\n\n // If the diagnostics channel already exists, we don't need to handle the error again\n return;\n }\n\n if (shouldHandleError(error, request, reply)) {\n captureException(error);\n }\n}\n\nexport const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME_V5, () => {\n const fastifyOtelInstrumentationInstance = new FastifyOtelInstrumentation();\n const plugin = fastifyOtelInstrumentationInstance.plugin();\n\n // This message handler works for Fastify versions 3, 4 and 5\n diagnosticsChannel.subscribe('fastify.initialization', message => {\n const fastifyInstance = (message as { fastify?: FastifyInstance }).fastify;\n\n fastifyInstance?.register(plugin).after(err => {\n if (err) {\n DEBUG_BUILD && debug.error('Failed to setup Fastify instrumentation', err);\n } else {\n instrumentClient();\n\n if (fastifyInstance) {\n instrumentOnRequest(fastifyInstance);\n }\n }\n });\n });\n\n // This diagnostics channel only works on Fastify version 5\n // For versions 3 and 4, we use `setupFastifyErrorHandler` instead\n diagnosticsChannel.subscribe('tracing:fastify.request.handler:error', message => {\n const { error, request, reply } = message as {\n error: Error;\n request: FastifyRequest & { opentelemetry?: () => { span?: Span } };\n reply: FastifyReply;\n };\n\n handleFastifyError.call(handleFastifyError, error, request, reply, 'diagnostics-channel');\n });\n\n // Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation\n return fastifyOtelInstrumentationInstance as Instrumentation<InstrumentationConfig & FastifyIntegrationOptions>;\n});\n\nconst _fastifyIntegration = (({ shouldHandleError }: Partial<FastifyIntegrationOptions>) => {\n let _shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n _shouldHandleError = shouldHandleError || defaultShouldHandleError;\n\n instrumentFastifyV3();\n instrumentFastify();\n },\n getShouldHandleError() {\n return _shouldHandleError;\n },\n setShouldHandleError(fn: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean): void {\n _shouldHandleError = fn;\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for [Fastify](https://fastify.dev/).\n *\n * If you also want to capture errors, you need to call `setupFastifyErrorHandler(app)` after you set up your Fastify server.\n *\n * For more information, see the [fastify documentation](https://docs.sentry.io/platforms/javascript/guides/fastify/).\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.fastifyIntegration()],\n * })\n * ```\n */\nexport const fastifyIntegration = defineIntegration((options: Partial<FastifyIntegrationOptions> = {}) =>\n _fastifyIntegration(options),\n);\n\n/**\n * Default function to determine if an error should be sent to Sentry\n *\n * 3xx and 4xx errors are not sent by default.\n */\nfunction defaultShouldHandleError(_error: Error, _request: FastifyRequest, reply: FastifyReply): boolean {\n const statusCode = reply.statusCode;\n // 3xx and 4xx errors are not sent by default.\n return statusCode >= 500 || statusCode <= 299;\n}\n\n/**\n * Add an Fastify error handler to capture errors to Sentry.\n *\n * @param fastify The Fastify instance to which to add the error handler\n * @param options Configuration options for the handler\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n * const Fastify = require(\"fastify\");\n *\n * const app = Fastify();\n *\n * Sentry.setupFastifyErrorHandler(app);\n *\n * // Add your routes, etc.\n *\n * app.listen({ port: 3000 });\n * ```\n */\nexport function setupFastifyErrorHandler(fastify: FastifyInstance, options?: Partial<FastifyHandlerOptions>): void {\n if (options?.shouldHandleError) {\n getFastifyIntegration()?.setShouldHandleError(options.shouldHandleError);\n }\n\n const plugin = Object.assign(\n function (fastify: FastifyInstance, _options: unknown, done: () => void): void {\n fastify.addHook('onError', async (request, reply, error) => {\n handleFastifyError.call(handleFastifyError, error, request, reply, 'onError-hook');\n });\n done();\n },\n {\n [Symbol.for('skip-override')]: true,\n [Symbol.for('fastify.display-name')]: 'sentry-fastify-error-handler',\n },\n );\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n fastify.register(plugin);\n}\n\nfunction addFastifySpanAttributes(span: Span): void {\n const spanJSON = spanToJSON(span);\n const spanName = spanJSON.description;\n const attributes = spanJSON.data;\n\n const type = attributes['fastify.type'];\n\n const isHook = type === 'hook';\n const isHandler = type === spanName?.startsWith('handler -');\n // In @fastify/otel `request-handler` is separated by dash, not underscore\n const isRequestHandler = spanName === 'request' || type === 'request-handler';\n\n // If this is already set, or we have no fastify span, no need to process again...\n if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || (!isHandler && !isRequestHandler && !isHook)) {\n return;\n }\n\n const opPrefix = isHook ? 'hook' : isHandler ? 'middleware' : isRequestHandler ? 'request-handler' : '<unknown>';\n\n span.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.fastify',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${opPrefix}.fastify`,\n });\n\n const attrName = attributes['fastify.name'] || attributes['plugin.name'] || attributes['hook.name'];\n if (typeof attrName === 'string') {\n // Try removing `fastify -> ` and `@fastify/otel -> ` prefixes\n // This is a bit of a hack, and not always working for all spans\n // But it's the best we can do without a proper API\n const updatedName = attrName.replace(/^fastify -> /, '').replace(/^@fastify\\/otel -> /, '');\n\n span.updateName(updatedName);\n }\n}\n\nfunction instrumentClient(): void {\n const client = getClient();\n if (client) {\n client.on('spanStart', (span: Span) => {\n addFastifySpanAttributes(span);\n });\n }\n}\n\nfunction instrumentOnRequest(fastify: FastifyInstance): void {\n fastify.addHook('onRequest', async (request: FastifyRequest & { opentelemetry?: () => { span?: Span } }, _reply) => {\n if (request.opentelemetry) {\n const { span } = request.opentelemetry();\n\n if (span) {\n addFastifySpanAttributes(span);\n }\n }\n\n const routeName = request.routeOptions?.url;\n const method = request.method || 'GET';\n\n getIsolationScope().setTransactionName(`${method} ${routeName}`);\n });\n}\n"],"names":["generateInstrumentOnce","FastifyInstrumentationV3","getClient","DEBUG_BUILD","debug","captureException","FastifyOtelInstrumentation","diagnosticsChannel","defineIntegration","spanToJSON","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","getIsolationScope"],"mappings":"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"}
@@ -117,8 +117,21 @@ class SentryVercelAiInstrumentation extends instrumentation.InstrumentationBase
117
117
  recordOutputs,
118
118
  };
119
119
 
120
- // @ts-expect-error we know that the method exists
121
- return originalMethod.apply(this, args);
120
+ return core.handleCallbackErrors(
121
+ () => {
122
+ // @ts-expect-error we know that the method exists
123
+ return originalMethod.apply(this, args);
124
+ },
125
+ error => {
126
+ // This error bubbles up to unhandledrejection handler (if not handled before),
127
+ // where we do not know the active span anymore
128
+ // So to circumvent this, we set the active span on the error object
129
+ // which is picked up by the unhandledrejection handler
130
+ if (error && typeof error === 'object') {
131
+ core.addNonEnumerableProperty(error, '_sentry_active_span', core.getActiveSpan());
132
+ }
133
+ },
134
+ );
122
135
  };
123
136
  }
124
137
 
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing/vercelai/instrumentation.ts"],"sourcesContent":["import type { InstrumentationConfig, InstrumentationModuleDefinition } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { getCurrentScope, SDK_VERSION } from '@sentry/core';\nimport { INTEGRATION_NAME } from './constants';\nimport type { TelemetrySettings, VercelAiIntegration } from './types';\n\n// List of patched methods\n// From: https://sdk.vercel.ai/docs/ai-sdk-core/telemetry#collected-data\nconst INSTRUMENTED_METHODS = [\n 'generateText',\n 'streamText',\n 'generateObject',\n 'streamObject',\n 'embed',\n 'embedMany',\n] as const;\n\ninterface MethodFirstArg extends Record<string, unknown> {\n experimental_telemetry?: TelemetrySettings;\n}\n\ntype MethodArgs = [MethodFirstArg, ...unknown[]];\n\ntype PatchedModuleExports = Record<(typeof INSTRUMENTED_METHODS)[number], (...args: MethodArgs) => unknown> &\n Record<string, unknown>;\n\ninterface RecordingOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n}\n\n/**\n * Determines whether to record inputs and outputs for Vercel AI telemetry based on the configuration hierarchy.\n *\n * The order of precedence is:\n * 1. The vercel ai integration options\n * 2. The experimental_telemetry options in the vercel ai method calls\n * 3. When telemetry is explicitly enabled (isEnabled: true), default to recording\n * 4. Otherwise, use the sendDefaultPii option from client options\n */\nexport function determineRecordingSettings(\n integrationRecordingOptions: RecordingOptions | undefined,\n methodTelemetryOptions: RecordingOptions,\n telemetryExplicitlyEnabled: boolean | undefined,\n defaultRecordingEnabled: boolean,\n): { recordInputs: boolean; recordOutputs: boolean } {\n const recordInputs =\n integrationRecordingOptions?.recordInputs !== undefined\n ? integrationRecordingOptions.recordInputs\n : methodTelemetryOptions.recordInputs !== undefined\n ? methodTelemetryOptions.recordInputs\n : telemetryExplicitlyEnabled === true\n ? true // When telemetry is explicitly enabled, default to recording inputs\n : defaultRecordingEnabled;\n\n const recordOutputs =\n integrationRecordingOptions?.recordOutputs !== undefined\n ? integrationRecordingOptions.recordOutputs\n : methodTelemetryOptions.recordOutputs !== undefined\n ? methodTelemetryOptions.recordOutputs\n : telemetryExplicitlyEnabled === true\n ? true // When telemetry is explicitly enabled, default to recording inputs\n : defaultRecordingEnabled;\n\n return { recordInputs, recordOutputs };\n}\n\n/**\n * This detects is added by the Sentry Vercel AI Integration to detect if the integration should\n * be enabled.\n *\n * It also patches the `ai` module to enable Vercel AI telemetry automatically for all methods.\n */\nexport class SentryVercelAiInstrumentation extends InstrumentationBase {\n private _isPatched = false;\n private _callbacks: (() => void)[] = [];\n\n public constructor(config: InstrumentationConfig = {}) {\n super('@sentry/instrumentation-vercel-ai', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the modules to be patched.\n */\n public init(): InstrumentationModuleDefinition {\n const module = new InstrumentationNodeModuleDefinition('ai', ['>=3.0.0 <5'], this._patch.bind(this));\n return module;\n }\n\n /**\n * Call the provided callback when the module is patched.\n * If it has already been patched, the callback will be called immediately.\n */\n public callWhenPatched(callback: () => void): void {\n if (this._isPatched) {\n callback();\n } else {\n this._callbacks.push(callback);\n }\n }\n\n /**\n * Patches module exports to enable Vercel AI telemetry.\n */\n private _patch(moduleExports: PatchedModuleExports): unknown {\n this._isPatched = true;\n\n this._callbacks.forEach(callback => callback());\n this._callbacks = [];\n\n function generatePatch(originalMethod: (...args: MethodArgs) => unknown) {\n return (...args: MethodArgs) => {\n const existingExperimentalTelemetry = args[0].experimental_telemetry || {};\n const isEnabled = existingExperimentalTelemetry.isEnabled;\n\n const client = getCurrentScope().getClient();\n const integration = client?.getIntegrationByName<VercelAiIntegration>(INTEGRATION_NAME);\n const integrationOptions = integration?.options;\n const shouldRecordInputsAndOutputs = integration ? Boolean(client?.getOptions().sendDefaultPii) : false;\n\n const { recordInputs, recordOutputs } = determineRecordingSettings(\n integrationOptions,\n existingExperimentalTelemetry,\n isEnabled,\n shouldRecordInputsAndOutputs,\n );\n\n args[0].experimental_telemetry = {\n ...existingExperimentalTelemetry,\n isEnabled: isEnabled !== undefined ? isEnabled : true,\n recordInputs,\n recordOutputs,\n };\n\n // @ts-expect-error we know that the method exists\n return originalMethod.apply(this, args);\n };\n }\n\n // Is this an ESM module?\n // https://tc39.es/ecma262/#sec-module-namespace-objects\n if (Object.prototype.toString.call(moduleExports) === '[object Module]') {\n // In ESM we take the usual route and just replace the exports we want to instrument\n for (const method of INSTRUMENTED_METHODS) {\n moduleExports[method] = generatePatch(moduleExports[method]);\n }\n\n return moduleExports;\n } else {\n // In CJS we can't replace the exports in the original module because they\n // don't have setters, so we create a new object with the same properties\n const patchedModuleExports = INSTRUMENTED_METHODS.reduce((acc, curr) => {\n acc[curr] = generatePatch(moduleExports[curr]);\n return acc;\n }, {} as PatchedModuleExports);\n\n return { ...moduleExports, ...patchedModuleExports };\n }\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","InstrumentationNodeModuleDefinition","getCurrentScope","INTEGRATION_NAME"],"mappings":"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"}
1
+ {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing/vercelai/instrumentation.ts"],"sourcesContent":["import type { InstrumentationConfig, InstrumentationModuleDefinition } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport {\n addNonEnumerableProperty,\n getActiveSpan,\n getCurrentScope,\n handleCallbackErrors,\n SDK_VERSION,\n} from '@sentry/core';\nimport { INTEGRATION_NAME } from './constants';\nimport type { TelemetrySettings, VercelAiIntegration } from './types';\n\n// List of patched methods\n// From: https://sdk.vercel.ai/docs/ai-sdk-core/telemetry#collected-data\nconst INSTRUMENTED_METHODS = [\n 'generateText',\n 'streamText',\n 'generateObject',\n 'streamObject',\n 'embed',\n 'embedMany',\n] as const;\n\ninterface MethodFirstArg extends Record<string, unknown> {\n experimental_telemetry?: TelemetrySettings;\n}\n\ntype MethodArgs = [MethodFirstArg, ...unknown[]];\n\ntype PatchedModuleExports = Record<(typeof INSTRUMENTED_METHODS)[number], (...args: MethodArgs) => unknown> &\n Record<string, unknown>;\n\ninterface RecordingOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n}\n\n/**\n * Determines whether to record inputs and outputs for Vercel AI telemetry based on the configuration hierarchy.\n *\n * The order of precedence is:\n * 1. The vercel ai integration options\n * 2. The experimental_telemetry options in the vercel ai method calls\n * 3. When telemetry is explicitly enabled (isEnabled: true), default to recording\n * 4. Otherwise, use the sendDefaultPii option from client options\n */\nexport function determineRecordingSettings(\n integrationRecordingOptions: RecordingOptions | undefined,\n methodTelemetryOptions: RecordingOptions,\n telemetryExplicitlyEnabled: boolean | undefined,\n defaultRecordingEnabled: boolean,\n): { recordInputs: boolean; recordOutputs: boolean } {\n const recordInputs =\n integrationRecordingOptions?.recordInputs !== undefined\n ? integrationRecordingOptions.recordInputs\n : methodTelemetryOptions.recordInputs !== undefined\n ? methodTelemetryOptions.recordInputs\n : telemetryExplicitlyEnabled === true\n ? true // When telemetry is explicitly enabled, default to recording inputs\n : defaultRecordingEnabled;\n\n const recordOutputs =\n integrationRecordingOptions?.recordOutputs !== undefined\n ? integrationRecordingOptions.recordOutputs\n : methodTelemetryOptions.recordOutputs !== undefined\n ? methodTelemetryOptions.recordOutputs\n : telemetryExplicitlyEnabled === true\n ? true // When telemetry is explicitly enabled, default to recording inputs\n : defaultRecordingEnabled;\n\n return { recordInputs, recordOutputs };\n}\n\n/**\n * This detects is added by the Sentry Vercel AI Integration to detect if the integration should\n * be enabled.\n *\n * It also patches the `ai` module to enable Vercel AI telemetry automatically for all methods.\n */\nexport class SentryVercelAiInstrumentation extends InstrumentationBase {\n private _isPatched = false;\n private _callbacks: (() => void)[] = [];\n\n public constructor(config: InstrumentationConfig = {}) {\n super('@sentry/instrumentation-vercel-ai', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the modules to be patched.\n */\n public init(): InstrumentationModuleDefinition {\n const module = new InstrumentationNodeModuleDefinition('ai', ['>=3.0.0 <5'], this._patch.bind(this));\n return module;\n }\n\n /**\n * Call the provided callback when the module is patched.\n * If it has already been patched, the callback will be called immediately.\n */\n public callWhenPatched(callback: () => void): void {\n if (this._isPatched) {\n callback();\n } else {\n this._callbacks.push(callback);\n }\n }\n\n /**\n * Patches module exports to enable Vercel AI telemetry.\n */\n private _patch(moduleExports: PatchedModuleExports): unknown {\n this._isPatched = true;\n\n this._callbacks.forEach(callback => callback());\n this._callbacks = [];\n\n function generatePatch(originalMethod: (...args: MethodArgs) => unknown) {\n return (...args: MethodArgs) => {\n const existingExperimentalTelemetry = args[0].experimental_telemetry || {};\n const isEnabled = existingExperimentalTelemetry.isEnabled;\n\n const client = getCurrentScope().getClient();\n const integration = client?.getIntegrationByName<VercelAiIntegration>(INTEGRATION_NAME);\n const integrationOptions = integration?.options;\n const shouldRecordInputsAndOutputs = integration ? Boolean(client?.getOptions().sendDefaultPii) : false;\n\n const { recordInputs, recordOutputs } = determineRecordingSettings(\n integrationOptions,\n existingExperimentalTelemetry,\n isEnabled,\n shouldRecordInputsAndOutputs,\n );\n\n args[0].experimental_telemetry = {\n ...existingExperimentalTelemetry,\n isEnabled: isEnabled !== undefined ? isEnabled : true,\n recordInputs,\n recordOutputs,\n };\n\n return handleCallbackErrors(\n () => {\n // @ts-expect-error we know that the method exists\n return originalMethod.apply(this, args);\n },\n error => {\n // This error bubbles up to unhandledrejection handler (if not handled before),\n // where we do not know the active span anymore\n // So to circumvent this, we set the active span on the error object\n // which is picked up by the unhandledrejection handler\n if (error && typeof error === 'object') {\n addNonEnumerableProperty(error, '_sentry_active_span', getActiveSpan());\n }\n },\n );\n };\n }\n\n // Is this an ESM module?\n // https://tc39.es/ecma262/#sec-module-namespace-objects\n if (Object.prototype.toString.call(moduleExports) === '[object Module]') {\n // In ESM we take the usual route and just replace the exports we want to instrument\n for (const method of INSTRUMENTED_METHODS) {\n moduleExports[method] = generatePatch(moduleExports[method]);\n }\n\n return moduleExports;\n } else {\n // In CJS we can't replace the exports in the original module because they\n // don't have setters, so we create a new object with the same properties\n const patchedModuleExports = INSTRUMENTED_METHODS.reduce((acc, curr) => {\n acc[curr] = generatePatch(moduleExports[curr]);\n return acc;\n }, {} as PatchedModuleExports);\n\n return { ...moduleExports, ...patchedModuleExports };\n }\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","InstrumentationNodeModuleDefinition","getCurrentScope","INTEGRATION_NAME","handleCallbackErrors","addNonEnumerableProperty","getActiveSpan"],"mappings":"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"}
@@ -5,19 +5,52 @@ import { DEBUG_BUILD } from '../../../debug-build.js';
5
5
  import { FastifyOtelInstrumentation } from './fastify-otel/index.js';
6
6
  import { FastifyInstrumentationV3 } from './v3/instrumentation.js';
7
7
 
8
+ /**
9
+ * Options for the Fastify integration.
10
+ *
11
+ * `shouldHandleError` - Callback method deciding whether error should be captured and sent to Sentry
12
+ * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.
13
+ * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.
14
+ *
15
+ * @example
16
+ *
17
+ * ```javascript
18
+ * Sentry.init({
19
+ * integrations: [
20
+ * Sentry.fastifyIntegration({
21
+ * shouldHandleError(_error, _request, reply) {
22
+ * return reply.statusCode >= 500;
23
+ * },
24
+ * });
25
+ * },
26
+ * });
27
+ * ```
28
+ *
29
+ */
30
+
8
31
  const INTEGRATION_NAME = 'Fastify';
32
+ const INTEGRATION_NAME_V5 = 'Fastify-V5';
9
33
  const INTEGRATION_NAME_V3 = 'Fastify-V3';
10
34
 
11
35
  const instrumentFastifyV3 = generateInstrumentOnce(INTEGRATION_NAME_V3, () => new FastifyInstrumentationV3());
12
36
 
37
+ function getFastifyIntegration() {
38
+ const client = getClient();
39
+ if (!client) {
40
+ return undefined;
41
+ } else {
42
+ return client.getIntegrationByName(INTEGRATION_NAME) ;
43
+ }
44
+ }
45
+
13
46
  function handleFastifyError(
14
47
 
15
48
  error,
16
49
  request,
17
50
  reply,
18
- shouldHandleError,
19
51
  handlerOrigin,
20
52
  ) {
53
+ const shouldHandleError = getFastifyIntegration()?.getShouldHandleError() || defaultShouldHandleError;
21
54
  // Diagnostics channel runs before the onError hook, so we can use it to check if the handler was already registered
22
55
  if (handlerOrigin === 'diagnostics-channel') {
23
56
  this.diagnosticsChannelExists = true;
@@ -27,7 +60,7 @@ function handleFastifyError(
27
60
  DEBUG_BUILD &&
28
61
  debug.warn(
29
62
  'Fastify error handler was already registered via diagnostics channel.',
30
- 'You can safely remove `setupFastifyErrorHandler` call.',
63
+ 'You can safely remove `setupFastifyErrorHandler` call and set `shouldHandleError` on the integration options.',
31
64
  );
32
65
 
33
66
  // If the diagnostics channel already exists, we don't need to handle the error again
@@ -39,11 +72,9 @@ function handleFastifyError(
39
72
  }
40
73
  }
41
74
 
42
- const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME, () => {
75
+ const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME_V5, () => {
43
76
  const fastifyOtelInstrumentationInstance = new FastifyOtelInstrumentation();
44
77
  const plugin = fastifyOtelInstrumentationInstance.plugin();
45
- const options = fastifyOtelInstrumentationInstance.getConfig();
46
- const shouldHandleError = (options )?.shouldHandleError || defaultShouldHandleError;
47
78
 
48
79
  // This message handler works for Fastify versions 3, 4 and 5
49
80
  dc.subscribe('fastify.initialization', message => {
@@ -69,20 +100,30 @@ const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME, () => {
69
100
 
70
101
  ;
71
102
 
72
- handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'diagnostics-channel');
103
+ handleFastifyError.call(handleFastifyError, error, request, reply, 'diagnostics-channel');
73
104
  });
74
105
 
75
106
  // Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation
76
107
  return fastifyOtelInstrumentationInstance ;
77
108
  });
78
109
 
79
- const _fastifyIntegration = (() => {
110
+ const _fastifyIntegration = (({ shouldHandleError }) => {
111
+ let _shouldHandleError;
112
+
80
113
  return {
81
114
  name: INTEGRATION_NAME,
82
115
  setupOnce() {
116
+ _shouldHandleError = shouldHandleError || defaultShouldHandleError;
117
+
83
118
  instrumentFastifyV3();
84
119
  instrumentFastify();
85
120
  },
121
+ getShouldHandleError() {
122
+ return _shouldHandleError;
123
+ },
124
+ setShouldHandleError(fn) {
125
+ _shouldHandleError = fn;
126
+ },
86
127
  };
87
128
  }) ;
88
129
 
@@ -102,7 +143,9 @@ const _fastifyIntegration = (() => {
102
143
  * })
103
144
  * ```
104
145
  */
105
- const fastifyIntegration = defineIntegration(_fastifyIntegration);
146
+ const fastifyIntegration = defineIntegration((options = {}) =>
147
+ _fastifyIntegration(options),
148
+ );
106
149
 
107
150
  /**
108
151
  * Default function to determine if an error should be sent to Sentry
@@ -136,11 +179,14 @@ function defaultShouldHandleError(_error, _request, reply) {
136
179
  * ```
137
180
  */
138
181
  function setupFastifyErrorHandler(fastify, options) {
139
- const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError;
182
+ if (options?.shouldHandleError) {
183
+ getFastifyIntegration()?.setShouldHandleError(options.shouldHandleError);
184
+ }
185
+
140
186
  const plugin = Object.assign(
141
187
  function (fastify, _options, done) {
142
188
  fastify.addHook('onError', async (request, reply, error) => {
143
- handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'onError-hook');
189
+ handleFastifyError.call(handleFastifyError, error, request, reply, 'onError-hook');
144
190
  });
145
191
  done();
146
192
  },
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/fastify/index.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n captureException,\n debug,\n defineIntegration,\n getClient,\n getIsolationScope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n} from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { FastifyOtelInstrumentation } from './fastify-otel/index';\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from './types';\nimport { FastifyInstrumentationV3 } from './v3/instrumentation';\n\ninterface FastifyHandlerOptions {\n /**\n * Callback method deciding whether error should be captured and sent to Sentry\n *\n * @param error Captured Fastify error\n * @param request Fastify request (or any object containing at least method, routeOptions.url, and routerPath)\n * @param reply Fastify reply (or any object containing at least statusCode)\n *\n * @example\n *\n * ```javascript\n * setupFastifyErrorHandler(app, {\n * shouldHandleError(_error, _request, reply) {\n * return reply.statusCode >= 400;\n * },\n * });\n * ```\n *\n * If using TypeScript, you can cast the request and reply to get full type safety.\n *\n * ```typescript\n * import type { FastifyRequest, FastifyReply } from 'fastify';\n *\n * setupFastifyErrorHandler(app, {\n * shouldHandleError(error, minimalRequest, minimalReply) {\n * const request = minimalRequest as FastifyRequest;\n * const reply = minimalReply as FastifyReply;\n * return reply.statusCode >= 500;\n * },\n * });\n * ```\n */\n shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;\n}\n\nconst INTEGRATION_NAME = 'Fastify';\nconst INTEGRATION_NAME_V3 = 'Fastify-V3';\n\nexport const instrumentFastifyV3 = generateInstrumentOnce(INTEGRATION_NAME_V3, () => new FastifyInstrumentationV3());\n\nfunction handleFastifyError(\n this: {\n diagnosticsChannelExists?: boolean;\n },\n error: Error,\n request: FastifyRequest & { opentelemetry?: () => { span?: Span } },\n reply: FastifyReply,\n shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean,\n handlerOrigin: 'diagnostics-channel' | 'onError-hook',\n): void {\n // Diagnostics channel runs before the onError hook, so we can use it to check if the handler was already registered\n if (handlerOrigin === 'diagnostics-channel') {\n this.diagnosticsChannelExists = true;\n }\n\n if (this.diagnosticsChannelExists && handlerOrigin === 'onError-hook') {\n DEBUG_BUILD &&\n debug.warn(\n 'Fastify error handler was already registered via diagnostics channel.',\n 'You can safely remove `setupFastifyErrorHandler` call.',\n );\n\n // If the diagnostics channel already exists, we don't need to handle the error again\n return;\n }\n\n if (shouldHandleError(error, request, reply)) {\n captureException(error);\n }\n}\n\nexport const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME, () => {\n const fastifyOtelInstrumentationInstance = new FastifyOtelInstrumentation();\n const plugin = fastifyOtelInstrumentationInstance.plugin();\n const options = fastifyOtelInstrumentationInstance.getConfig();\n const shouldHandleError = (options as FastifyHandlerOptions)?.shouldHandleError || defaultShouldHandleError;\n\n // This message handler works for Fastify versions 3, 4 and 5\n diagnosticsChannel.subscribe('fastify.initialization', message => {\n const fastifyInstance = (message as { fastify?: FastifyInstance }).fastify;\n\n fastifyInstance?.register(plugin).after(err => {\n if (err) {\n DEBUG_BUILD && debug.error('Failed to setup Fastify instrumentation', err);\n } else {\n instrumentClient();\n\n if (fastifyInstance) {\n instrumentOnRequest(fastifyInstance);\n }\n }\n });\n });\n\n // This diagnostics channel only works on Fastify version 5\n // For versions 3 and 4, we use `setupFastifyErrorHandler` instead\n diagnosticsChannel.subscribe('tracing:fastify.request.handler:error', message => {\n const { error, request, reply } = message as {\n error: Error;\n request: FastifyRequest & { opentelemetry?: () => { span?: Span } };\n reply: FastifyReply;\n };\n\n handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'diagnostics-channel');\n });\n\n // Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation\n return fastifyOtelInstrumentationInstance as Instrumentation<InstrumentationConfig & FastifyHandlerOptions>;\n});\n\nconst _fastifyIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentFastifyV3();\n instrumentFastify();\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for [Fastify](https://fastify.dev/).\n *\n * If you also want to capture errors, you need to call `setupFastifyErrorHandler(app)` after you set up your Fastify server.\n *\n * For more information, see the [fastify documentation](https://docs.sentry.io/platforms/javascript/guides/fastify/).\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.fastifyIntegration()],\n * })\n * ```\n */\nexport const fastifyIntegration = defineIntegration(_fastifyIntegration);\n\n/**\n * Default function to determine if an error should be sent to Sentry\n *\n * 3xx and 4xx errors are not sent by default.\n */\nfunction defaultShouldHandleError(_error: Error, _request: FastifyRequest, reply: FastifyReply): boolean {\n const statusCode = reply.statusCode;\n // 3xx and 4xx errors are not sent by default.\n return statusCode >= 500 || statusCode <= 299;\n}\n\n/**\n * Add an Fastify error handler to capture errors to Sentry.\n *\n * @param fastify The Fastify instance to which to add the error handler\n * @param options Configuration options for the handler\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n * const Fastify = require(\"fastify\");\n *\n * const app = Fastify();\n *\n * Sentry.setupFastifyErrorHandler(app);\n *\n * // Add your routes, etc.\n *\n * app.listen({ port: 3000 });\n * ```\n */\nexport function setupFastifyErrorHandler(fastify: FastifyInstance, options?: Partial<FastifyHandlerOptions>): void {\n const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError;\n const plugin = Object.assign(\n function (fastify: FastifyInstance, _options: unknown, done: () => void): void {\n fastify.addHook('onError', async (request, reply, error) => {\n handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'onError-hook');\n });\n done();\n },\n {\n [Symbol.for('skip-override')]: true,\n [Symbol.for('fastify.display-name')]: 'sentry-fastify-error-handler',\n },\n );\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n fastify.register(plugin);\n}\n\nfunction addFastifySpanAttributes(span: Span): void {\n const spanJSON = spanToJSON(span);\n const spanName = spanJSON.description;\n const attributes = spanJSON.data;\n\n const type = attributes['fastify.type'];\n\n const isHook = type === 'hook';\n const isHandler = type === spanName?.startsWith('handler -');\n // In @fastify/otel `request-handler` is separated by dash, not underscore\n const isRequestHandler = spanName === 'request' || type === 'request-handler';\n\n // If this is already set, or we have no fastify span, no need to process again...\n if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || (!isHandler && !isRequestHandler && !isHook)) {\n return;\n }\n\n const opPrefix = isHook ? 'hook' : isHandler ? 'middleware' : isRequestHandler ? 'request-handler' : '<unknown>';\n\n span.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.fastify',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${opPrefix}.fastify`,\n });\n\n const attrName = attributes['fastify.name'] || attributes['plugin.name'] || attributes['hook.name'];\n if (typeof attrName === 'string') {\n // Try removing `fastify -> ` and `@fastify/otel -> ` prefixes\n // This is a bit of a hack, and not always working for all spans\n // But it's the best we can do without a proper API\n const updatedName = attrName.replace(/^fastify -> /, '').replace(/^@fastify\\/otel -> /, '');\n\n span.updateName(updatedName);\n }\n}\n\nfunction instrumentClient(): void {\n const client = getClient();\n if (client) {\n client.on('spanStart', (span: Span) => {\n addFastifySpanAttributes(span);\n });\n }\n}\n\nfunction instrumentOnRequest(fastify: FastifyInstance): void {\n fastify.addHook('onRequest', async (request: FastifyRequest & { opentelemetry?: () => { span?: Span } }, _reply) => {\n if (request.opentelemetry) {\n const { span } = request.opentelemetry();\n\n if (span) {\n addFastifySpanAttributes(span);\n }\n }\n\n const routeName = request.routeOptions?.url;\n const method = request.method || 'GET';\n\n getIsolationScope().setTransactionName(`${method} ${routeName}`);\n });\n}\n"],"names":["diagnosticsChannel"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/fastify/index.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n captureException,\n debug,\n defineIntegration,\n getClient,\n getIsolationScope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n} from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport { DEBUG_BUILD } from '../../../debug-build';\nimport { FastifyOtelInstrumentation } from './fastify-otel/index';\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from './types';\nimport { FastifyInstrumentationV3 } from './v3/instrumentation';\n\n/**\n * Options for the Fastify integration.\n *\n * `shouldHandleError` - Callback method deciding whether error should be captured and sent to Sentry\n * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.\n * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.\n *\n * @example\n *\n * ```javascript\n * Sentry.init({\n * integrations: [\n * Sentry.fastifyIntegration({\n * shouldHandleError(_error, _request, reply) {\n * return reply.statusCode >= 500;\n * },\n * });\n * },\n * });\n * ```\n *\n */\ninterface FastifyIntegrationOptions {\n /**\n * Callback method deciding whether error should be captured and sent to Sentry\n * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.\n * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.\n *\n * @param error Captured Fastify error\n * @param request Fastify request (or any object containing at least method, routeOptions.url, and routerPath)\n * @param reply Fastify reply (or any object containing at least statusCode)\n */\n shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;\n}\n\ninterface FastifyHandlerOptions {\n /**\n * Callback method deciding whether error should be captured and sent to Sentry\n *\n * @param error Captured Fastify error\n * @param request Fastify request (or any object containing at least method, routeOptions.url, and routerPath)\n * @param reply Fastify reply (or any object containing at least statusCode)\n *\n * @example\n *\n *\n * ```javascript\n * setupFastifyErrorHandler(app, {\n * shouldHandleError(_error, _request, reply) {\n * return reply.statusCode >= 400;\n * },\n * });\n * ```\n *\n *\n * If using TypeScript, you can cast the request and reply to get full type safety.\n *\n * ```typescript\n * import type { FastifyRequest, FastifyReply } from 'fastify';\n *\n * setupFastifyErrorHandler(app, {\n * shouldHandleError(error, minimalRequest, minimalReply) {\n * const request = minimalRequest as FastifyRequest;\n * const reply = minimalReply as FastifyReply;\n * return reply.statusCode >= 500;\n * },\n * });\n * ```\n */\n shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;\n}\n\nconst INTEGRATION_NAME = 'Fastify';\nconst INTEGRATION_NAME_V5 = 'Fastify-V5';\nconst INTEGRATION_NAME_V3 = 'Fastify-V3';\n\nexport const instrumentFastifyV3 = generateInstrumentOnce(INTEGRATION_NAME_V3, () => new FastifyInstrumentationV3());\n\nfunction getFastifyIntegration(): ReturnType<typeof _fastifyIntegration> | undefined {\n const client = getClient();\n if (!client) {\n return undefined;\n } else {\n return client.getIntegrationByName(INTEGRATION_NAME) as ReturnType<typeof _fastifyIntegration> | undefined;\n }\n}\n\nfunction handleFastifyError(\n this: {\n diagnosticsChannelExists?: boolean;\n },\n error: Error,\n request: FastifyRequest & { opentelemetry?: () => { span?: Span } },\n reply: FastifyReply,\n handlerOrigin: 'diagnostics-channel' | 'onError-hook',\n): void {\n const shouldHandleError = getFastifyIntegration()?.getShouldHandleError() || defaultShouldHandleError;\n // Diagnostics channel runs before the onError hook, so we can use it to check if the handler was already registered\n if (handlerOrigin === 'diagnostics-channel') {\n this.diagnosticsChannelExists = true;\n }\n\n if (this.diagnosticsChannelExists && handlerOrigin === 'onError-hook') {\n DEBUG_BUILD &&\n debug.warn(\n 'Fastify error handler was already registered via diagnostics channel.',\n 'You can safely remove `setupFastifyErrorHandler` call and set `shouldHandleError` on the integration options.',\n );\n\n // If the diagnostics channel already exists, we don't need to handle the error again\n return;\n }\n\n if (shouldHandleError(error, request, reply)) {\n captureException(error);\n }\n}\n\nexport const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME_V5, () => {\n const fastifyOtelInstrumentationInstance = new FastifyOtelInstrumentation();\n const plugin = fastifyOtelInstrumentationInstance.plugin();\n\n // This message handler works for Fastify versions 3, 4 and 5\n diagnosticsChannel.subscribe('fastify.initialization', message => {\n const fastifyInstance = (message as { fastify?: FastifyInstance }).fastify;\n\n fastifyInstance?.register(plugin).after(err => {\n if (err) {\n DEBUG_BUILD && debug.error('Failed to setup Fastify instrumentation', err);\n } else {\n instrumentClient();\n\n if (fastifyInstance) {\n instrumentOnRequest(fastifyInstance);\n }\n }\n });\n });\n\n // This diagnostics channel only works on Fastify version 5\n // For versions 3 and 4, we use `setupFastifyErrorHandler` instead\n diagnosticsChannel.subscribe('tracing:fastify.request.handler:error', message => {\n const { error, request, reply } = message as {\n error: Error;\n request: FastifyRequest & { opentelemetry?: () => { span?: Span } };\n reply: FastifyReply;\n };\n\n handleFastifyError.call(handleFastifyError, error, request, reply, 'diagnostics-channel');\n });\n\n // Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation\n return fastifyOtelInstrumentationInstance as Instrumentation<InstrumentationConfig & FastifyIntegrationOptions>;\n});\n\nconst _fastifyIntegration = (({ shouldHandleError }: Partial<FastifyIntegrationOptions>) => {\n let _shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n _shouldHandleError = shouldHandleError || defaultShouldHandleError;\n\n instrumentFastifyV3();\n instrumentFastify();\n },\n getShouldHandleError() {\n return _shouldHandleError;\n },\n setShouldHandleError(fn: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean): void {\n _shouldHandleError = fn;\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for [Fastify](https://fastify.dev/).\n *\n * If you also want to capture errors, you need to call `setupFastifyErrorHandler(app)` after you set up your Fastify server.\n *\n * For more information, see the [fastify documentation](https://docs.sentry.io/platforms/javascript/guides/fastify/).\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.fastifyIntegration()],\n * })\n * ```\n */\nexport const fastifyIntegration = defineIntegration((options: Partial<FastifyIntegrationOptions> = {}) =>\n _fastifyIntegration(options),\n);\n\n/**\n * Default function to determine if an error should be sent to Sentry\n *\n * 3xx and 4xx errors are not sent by default.\n */\nfunction defaultShouldHandleError(_error: Error, _request: FastifyRequest, reply: FastifyReply): boolean {\n const statusCode = reply.statusCode;\n // 3xx and 4xx errors are not sent by default.\n return statusCode >= 500 || statusCode <= 299;\n}\n\n/**\n * Add an Fastify error handler to capture errors to Sentry.\n *\n * @param fastify The Fastify instance to which to add the error handler\n * @param options Configuration options for the handler\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n * const Fastify = require(\"fastify\");\n *\n * const app = Fastify();\n *\n * Sentry.setupFastifyErrorHandler(app);\n *\n * // Add your routes, etc.\n *\n * app.listen({ port: 3000 });\n * ```\n */\nexport function setupFastifyErrorHandler(fastify: FastifyInstance, options?: Partial<FastifyHandlerOptions>): void {\n if (options?.shouldHandleError) {\n getFastifyIntegration()?.setShouldHandleError(options.shouldHandleError);\n }\n\n const plugin = Object.assign(\n function (fastify: FastifyInstance, _options: unknown, done: () => void): void {\n fastify.addHook('onError', async (request, reply, error) => {\n handleFastifyError.call(handleFastifyError, error, request, reply, 'onError-hook');\n });\n done();\n },\n {\n [Symbol.for('skip-override')]: true,\n [Symbol.for('fastify.display-name')]: 'sentry-fastify-error-handler',\n },\n );\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n fastify.register(plugin);\n}\n\nfunction addFastifySpanAttributes(span: Span): void {\n const spanJSON = spanToJSON(span);\n const spanName = spanJSON.description;\n const attributes = spanJSON.data;\n\n const type = attributes['fastify.type'];\n\n const isHook = type === 'hook';\n const isHandler = type === spanName?.startsWith('handler -');\n // In @fastify/otel `request-handler` is separated by dash, not underscore\n const isRequestHandler = spanName === 'request' || type === 'request-handler';\n\n // If this is already set, or we have no fastify span, no need to process again...\n if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || (!isHandler && !isRequestHandler && !isHook)) {\n return;\n }\n\n const opPrefix = isHook ? 'hook' : isHandler ? 'middleware' : isRequestHandler ? 'request-handler' : '<unknown>';\n\n span.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.fastify',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${opPrefix}.fastify`,\n });\n\n const attrName = attributes['fastify.name'] || attributes['plugin.name'] || attributes['hook.name'];\n if (typeof attrName === 'string') {\n // Try removing `fastify -> ` and `@fastify/otel -> ` prefixes\n // This is a bit of a hack, and not always working for all spans\n // But it's the best we can do without a proper API\n const updatedName = attrName.replace(/^fastify -> /, '').replace(/^@fastify\\/otel -> /, '');\n\n span.updateName(updatedName);\n }\n}\n\nfunction instrumentClient(): void {\n const client = getClient();\n if (client) {\n client.on('spanStart', (span: Span) => {\n addFastifySpanAttributes(span);\n });\n }\n}\n\nfunction instrumentOnRequest(fastify: FastifyInstance): void {\n fastify.addHook('onRequest', async (request: FastifyRequest & { opentelemetry?: () => { span?: Span } }, _reply) => {\n if (request.opentelemetry) {\n const { span } = request.opentelemetry();\n\n if (span) {\n addFastifySpanAttributes(span);\n }\n }\n\n const routeName = request.routeOptions?.url;\n const method = request.method || 'GET';\n\n getIsolationScope().setTransactionName(`${method} ${routeName}`);\n });\n}\n"],"names":["diagnosticsChannel"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
2
- import { SDK_VERSION, getCurrentScope } from '@sentry/core';
2
+ import { SDK_VERSION, getCurrentScope, handleCallbackErrors, addNonEnumerableProperty, getActiveSpan } from '@sentry/core';
3
3
  import { INTEGRATION_NAME } from './constants.js';
4
4
 
5
5
  // List of patched methods
@@ -115,8 +115,21 @@ class SentryVercelAiInstrumentation extends InstrumentationBase {
115
115
  recordOutputs,
116
116
  };
117
117
 
118
- // @ts-expect-error we know that the method exists
119
- return originalMethod.apply(this, args);
118
+ return handleCallbackErrors(
119
+ () => {
120
+ // @ts-expect-error we know that the method exists
121
+ return originalMethod.apply(this, args);
122
+ },
123
+ error => {
124
+ // This error bubbles up to unhandledrejection handler (if not handled before),
125
+ // where we do not know the active span anymore
126
+ // So to circumvent this, we set the active span on the error object
127
+ // which is picked up by the unhandledrejection handler
128
+ if (error && typeof error === 'object') {
129
+ addNonEnumerableProperty(error, '_sentry_active_span', getActiveSpan());
130
+ }
131
+ },
132
+ );
120
133
  };
121
134
  }
122
135
 
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing/vercelai/instrumentation.ts"],"sourcesContent":["import type { InstrumentationConfig, InstrumentationModuleDefinition } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { getCurrentScope, SDK_VERSION } from '@sentry/core';\nimport { INTEGRATION_NAME } from './constants';\nimport type { TelemetrySettings, VercelAiIntegration } from './types';\n\n// List of patched methods\n// From: https://sdk.vercel.ai/docs/ai-sdk-core/telemetry#collected-data\nconst INSTRUMENTED_METHODS = [\n 'generateText',\n 'streamText',\n 'generateObject',\n 'streamObject',\n 'embed',\n 'embedMany',\n] as const;\n\ninterface MethodFirstArg extends Record<string, unknown> {\n experimental_telemetry?: TelemetrySettings;\n}\n\ntype MethodArgs = [MethodFirstArg, ...unknown[]];\n\ntype PatchedModuleExports = Record<(typeof INSTRUMENTED_METHODS)[number], (...args: MethodArgs) => unknown> &\n Record<string, unknown>;\n\ninterface RecordingOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n}\n\n/**\n * Determines whether to record inputs and outputs for Vercel AI telemetry based on the configuration hierarchy.\n *\n * The order of precedence is:\n * 1. The vercel ai integration options\n * 2. The experimental_telemetry options in the vercel ai method calls\n * 3. When telemetry is explicitly enabled (isEnabled: true), default to recording\n * 4. Otherwise, use the sendDefaultPii option from client options\n */\nexport function determineRecordingSettings(\n integrationRecordingOptions: RecordingOptions | undefined,\n methodTelemetryOptions: RecordingOptions,\n telemetryExplicitlyEnabled: boolean | undefined,\n defaultRecordingEnabled: boolean,\n): { recordInputs: boolean; recordOutputs: boolean } {\n const recordInputs =\n integrationRecordingOptions?.recordInputs !== undefined\n ? integrationRecordingOptions.recordInputs\n : methodTelemetryOptions.recordInputs !== undefined\n ? methodTelemetryOptions.recordInputs\n : telemetryExplicitlyEnabled === true\n ? true // When telemetry is explicitly enabled, default to recording inputs\n : defaultRecordingEnabled;\n\n const recordOutputs =\n integrationRecordingOptions?.recordOutputs !== undefined\n ? integrationRecordingOptions.recordOutputs\n : methodTelemetryOptions.recordOutputs !== undefined\n ? methodTelemetryOptions.recordOutputs\n : telemetryExplicitlyEnabled === true\n ? true // When telemetry is explicitly enabled, default to recording inputs\n : defaultRecordingEnabled;\n\n return { recordInputs, recordOutputs };\n}\n\n/**\n * This detects is added by the Sentry Vercel AI Integration to detect if the integration should\n * be enabled.\n *\n * It also patches the `ai` module to enable Vercel AI telemetry automatically for all methods.\n */\nexport class SentryVercelAiInstrumentation extends InstrumentationBase {\n private _isPatched = false;\n private _callbacks: (() => void)[] = [];\n\n public constructor(config: InstrumentationConfig = {}) {\n super('@sentry/instrumentation-vercel-ai', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the modules to be patched.\n */\n public init(): InstrumentationModuleDefinition {\n const module = new InstrumentationNodeModuleDefinition('ai', ['>=3.0.0 <5'], this._patch.bind(this));\n return module;\n }\n\n /**\n * Call the provided callback when the module is patched.\n * If it has already been patched, the callback will be called immediately.\n */\n public callWhenPatched(callback: () => void): void {\n if (this._isPatched) {\n callback();\n } else {\n this._callbacks.push(callback);\n }\n }\n\n /**\n * Patches module exports to enable Vercel AI telemetry.\n */\n private _patch(moduleExports: PatchedModuleExports): unknown {\n this._isPatched = true;\n\n this._callbacks.forEach(callback => callback());\n this._callbacks = [];\n\n function generatePatch(originalMethod: (...args: MethodArgs) => unknown) {\n return (...args: MethodArgs) => {\n const existingExperimentalTelemetry = args[0].experimental_telemetry || {};\n const isEnabled = existingExperimentalTelemetry.isEnabled;\n\n const client = getCurrentScope().getClient();\n const integration = client?.getIntegrationByName<VercelAiIntegration>(INTEGRATION_NAME);\n const integrationOptions = integration?.options;\n const shouldRecordInputsAndOutputs = integration ? Boolean(client?.getOptions().sendDefaultPii) : false;\n\n const { recordInputs, recordOutputs } = determineRecordingSettings(\n integrationOptions,\n existingExperimentalTelemetry,\n isEnabled,\n shouldRecordInputsAndOutputs,\n );\n\n args[0].experimental_telemetry = {\n ...existingExperimentalTelemetry,\n isEnabled: isEnabled !== undefined ? isEnabled : true,\n recordInputs,\n recordOutputs,\n };\n\n // @ts-expect-error we know that the method exists\n return originalMethod.apply(this, args);\n };\n }\n\n // Is this an ESM module?\n // https://tc39.es/ecma262/#sec-module-namespace-objects\n if (Object.prototype.toString.call(moduleExports) === '[object Module]') {\n // In ESM we take the usual route and just replace the exports we want to instrument\n for (const method of INSTRUMENTED_METHODS) {\n moduleExports[method] = generatePatch(moduleExports[method]);\n }\n\n return moduleExports;\n } else {\n // In CJS we can't replace the exports in the original module because they\n // don't have setters, so we create a new object with the same properties\n const patchedModuleExports = INSTRUMENTED_METHODS.reduce((acc, curr) => {\n acc[curr] = generatePatch(moduleExports[curr]);\n return acc;\n }, {} as PatchedModuleExports);\n\n return { ...moduleExports, ...patchedModuleExports };\n }\n }\n}\n"],"names":[],"mappings":";;;;AAMA;AACA;AACA,MAAM,uBAAuB;AAC7B,EAAE,cAAc;AAChB,EAAE,YAAY;AACd,EAAE,gBAAgB;AAClB,EAAE,cAAc;AAChB,EAAE,OAAO;AACT,EAAE,WAAW;AACb,CAAA;;AAgBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,0BAA0B;AAC1C,EAAE,2BAA2B;AAC7B,EAAE,sBAAsB;AACxB,EAAE,0BAA0B;AAC5B,EAAE,uBAAuB;AACzB,EAAqD;AACrD,EAAE,MAAM,YAAA;AACR,IAAI,2BAA2B,EAAE,YAAA,KAAiB;AAClD,QAAQ,2BAA2B,CAAC;AACpC,QAAQ,sBAAsB,CAAC,YAAA,KAAiB;AAChD,UAAU,sBAAsB,CAAC;AACjC,UAAU,+BAA+B;AACzC,YAAY,IAAA;AACZ,YAAY,uBAAuB;;AAEnC,EAAE,MAAM,aAAA;AACR,IAAI,2BAA2B,EAAE,aAAA,KAAkB;AACnD,QAAQ,2BAA2B,CAAC;AACpC,QAAQ,sBAAsB,CAAC,aAAA,KAAkB;AACjD,UAAU,sBAAsB,CAAC;AACjC,UAAU,+BAA+B;AACzC,YAAY,IAAA;AACZ,YAAY,uBAAuB;;AAEnC,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe;AACxC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,6BAAA,SAAsC,mBAAA,CAAoB;AACvE,GAAE,MAAA,GAAA,CAAA,IAAA,CAAQ,UAAA,GAAa,MAAA;AACvB,GAAE,OAAA,GAAA,CAAA,IAAA,CAAQ,UAAU,GAAmB,GAAC;;AAExC,GAAS,WAAW,CAAC,MAAM,GAA0B,EAAE,EAAE;AACzD,IAAI,KAAK,CAAC,mCAAmC,EAAE,WAAW,EAAE,MAAM,CAAA,CAAA,6BAAA,CAAA,SAAA,CAAA,MAAA,CAAA,IAAA,CAAA,IAAA,CAAA,CAAA,6BAAA,CAAA,SAAA,CAAA,OAAA,CAAA,IAAA,CAAA,IAAA,CAAA,CAClE;;AAEA;AACA;AACA;AACA,GAAS,IAAI,GAAoC;AACjD,IAAI,MAAM,SAAS,IAAI,mCAAmC,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxG,IAAI,OAAO,MAAM;AACjB;;AAEA;AACA;AACA;AACA;AACA,GAAS,eAAe,CAAC,QAAQ,EAAoB;AACrD,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,QAAQ,EAAE;AAChB,WAAW;AACX,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;AACpC;AACA;;AAEA;AACA;AACA;AACA,GAAU,MAAM,CAAC,aAAa,EAAiC;AAC/D,IAAI,IAAI,CAAC,UAAA,GAAa,IAAI;;AAE1B,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAA,IAAY,QAAQ,EAAE,CAAC;AACnD,IAAI,IAAI,CAAC,UAAA,GAAa,EAAE;;AAExB,IAAI,SAAS,aAAa,CAAC,cAAc,EAAoC;AAC7E,MAAM,OAAO,CAAC,GAAG,IAAI,KAAiB;AACtC,QAAQ,MAAM,6BAAA,GAAgC,IAAI,CAAC,CAAC,CAAC,CAAC,sBAAA,IAA0B,EAAE;AAClF,QAAQ,MAAM,SAAA,GAAY,6BAA6B,CAAC,SAAS;;AAEjE,QAAQ,MAAM,SAAS,eAAe,EAAE,CAAC,SAAS,EAAE;AACpD,QAAQ,MAAM,cAAc,MAAM,EAAE,oBAAoB,CAAsB,gBAAgB,CAAC;AAC/F,QAAQ,MAAM,kBAAA,GAAqB,WAAW,EAAE,OAAO;AACvD,QAAQ,MAAM,4BAAA,GAA+B,WAAA,GAAc,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,cAAc,CAAA,GAAI,KAAK;;AAE/G,QAAQ,MAAM,EAAE,YAAY,EAAE,aAAA,EAAc,GAAI,0BAA0B;AAC1E,UAAU,kBAAkB;AAC5B,UAAU,6BAA6B;AACvC,UAAU,SAAS;AACnB,UAAU,4BAA4B;AACtC,SAAS;;AAET,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,yBAAyB;AACzC,UAAU,GAAG,6BAA6B;AAC1C,UAAU,SAAS,EAAE,SAAA,KAAc,YAAY,SAAA,GAAY,IAAI;AAC/D,UAAU,YAAY;AACtB,UAAU,aAAa;AACvB,SAAS;;AAET;AACA,QAAQ,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;AAC/C,OAAO;AACP;;AAEA;AACA;AACA,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAA,KAAM,iBAAiB,EAAE;AAC7E;AACA,MAAM,KAAK,MAAM,MAAA,IAAU,oBAAoB,EAAE;AACjD,QAAQ,aAAa,CAAC,MAAM,CAAA,GAAI,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;AACpE;;AAEA,MAAM,OAAO,aAAa;AAC1B,WAAW;AACX;AACA;AACA,MAAM,MAAM,oBAAA,GAAuB,oBAAoB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK;AAC9E,QAAQ,GAAG,CAAC,IAAI,CAAA,GAAI,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;AACtD,QAAQ,OAAO,GAAG;AAClB,OAAO,EAAE,EAAC,EAA0B;;AAEpC,MAAM,OAAO,EAAE,GAAG,aAAa,EAAE,GAAG,sBAAsB;AAC1D;AACA;AACA;;;;"}
1
+ {"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing/vercelai/instrumentation.ts"],"sourcesContent":["import type { InstrumentationConfig, InstrumentationModuleDefinition } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport {\n addNonEnumerableProperty,\n getActiveSpan,\n getCurrentScope,\n handleCallbackErrors,\n SDK_VERSION,\n} from '@sentry/core';\nimport { INTEGRATION_NAME } from './constants';\nimport type { TelemetrySettings, VercelAiIntegration } from './types';\n\n// List of patched methods\n// From: https://sdk.vercel.ai/docs/ai-sdk-core/telemetry#collected-data\nconst INSTRUMENTED_METHODS = [\n 'generateText',\n 'streamText',\n 'generateObject',\n 'streamObject',\n 'embed',\n 'embedMany',\n] as const;\n\ninterface MethodFirstArg extends Record<string, unknown> {\n experimental_telemetry?: TelemetrySettings;\n}\n\ntype MethodArgs = [MethodFirstArg, ...unknown[]];\n\ntype PatchedModuleExports = Record<(typeof INSTRUMENTED_METHODS)[number], (...args: MethodArgs) => unknown> &\n Record<string, unknown>;\n\ninterface RecordingOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n}\n\n/**\n * Determines whether to record inputs and outputs for Vercel AI telemetry based on the configuration hierarchy.\n *\n * The order of precedence is:\n * 1. The vercel ai integration options\n * 2. The experimental_telemetry options in the vercel ai method calls\n * 3. When telemetry is explicitly enabled (isEnabled: true), default to recording\n * 4. Otherwise, use the sendDefaultPii option from client options\n */\nexport function determineRecordingSettings(\n integrationRecordingOptions: RecordingOptions | undefined,\n methodTelemetryOptions: RecordingOptions,\n telemetryExplicitlyEnabled: boolean | undefined,\n defaultRecordingEnabled: boolean,\n): { recordInputs: boolean; recordOutputs: boolean } {\n const recordInputs =\n integrationRecordingOptions?.recordInputs !== undefined\n ? integrationRecordingOptions.recordInputs\n : methodTelemetryOptions.recordInputs !== undefined\n ? methodTelemetryOptions.recordInputs\n : telemetryExplicitlyEnabled === true\n ? true // When telemetry is explicitly enabled, default to recording inputs\n : defaultRecordingEnabled;\n\n const recordOutputs =\n integrationRecordingOptions?.recordOutputs !== undefined\n ? integrationRecordingOptions.recordOutputs\n : methodTelemetryOptions.recordOutputs !== undefined\n ? methodTelemetryOptions.recordOutputs\n : telemetryExplicitlyEnabled === true\n ? true // When telemetry is explicitly enabled, default to recording inputs\n : defaultRecordingEnabled;\n\n return { recordInputs, recordOutputs };\n}\n\n/**\n * This detects is added by the Sentry Vercel AI Integration to detect if the integration should\n * be enabled.\n *\n * It also patches the `ai` module to enable Vercel AI telemetry automatically for all methods.\n */\nexport class SentryVercelAiInstrumentation extends InstrumentationBase {\n private _isPatched = false;\n private _callbacks: (() => void)[] = [];\n\n public constructor(config: InstrumentationConfig = {}) {\n super('@sentry/instrumentation-vercel-ai', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the modules to be patched.\n */\n public init(): InstrumentationModuleDefinition {\n const module = new InstrumentationNodeModuleDefinition('ai', ['>=3.0.0 <5'], this._patch.bind(this));\n return module;\n }\n\n /**\n * Call the provided callback when the module is patched.\n * If it has already been patched, the callback will be called immediately.\n */\n public callWhenPatched(callback: () => void): void {\n if (this._isPatched) {\n callback();\n } else {\n this._callbacks.push(callback);\n }\n }\n\n /**\n * Patches module exports to enable Vercel AI telemetry.\n */\n private _patch(moduleExports: PatchedModuleExports): unknown {\n this._isPatched = true;\n\n this._callbacks.forEach(callback => callback());\n this._callbacks = [];\n\n function generatePatch(originalMethod: (...args: MethodArgs) => unknown) {\n return (...args: MethodArgs) => {\n const existingExperimentalTelemetry = args[0].experimental_telemetry || {};\n const isEnabled = existingExperimentalTelemetry.isEnabled;\n\n const client = getCurrentScope().getClient();\n const integration = client?.getIntegrationByName<VercelAiIntegration>(INTEGRATION_NAME);\n const integrationOptions = integration?.options;\n const shouldRecordInputsAndOutputs = integration ? Boolean(client?.getOptions().sendDefaultPii) : false;\n\n const { recordInputs, recordOutputs } = determineRecordingSettings(\n integrationOptions,\n existingExperimentalTelemetry,\n isEnabled,\n shouldRecordInputsAndOutputs,\n );\n\n args[0].experimental_telemetry = {\n ...existingExperimentalTelemetry,\n isEnabled: isEnabled !== undefined ? isEnabled : true,\n recordInputs,\n recordOutputs,\n };\n\n return handleCallbackErrors(\n () => {\n // @ts-expect-error we know that the method exists\n return originalMethod.apply(this, args);\n },\n error => {\n // This error bubbles up to unhandledrejection handler (if not handled before),\n // where we do not know the active span anymore\n // So to circumvent this, we set the active span on the error object\n // which is picked up by the unhandledrejection handler\n if (error && typeof error === 'object') {\n addNonEnumerableProperty(error, '_sentry_active_span', getActiveSpan());\n }\n },\n );\n };\n }\n\n // Is this an ESM module?\n // https://tc39.es/ecma262/#sec-module-namespace-objects\n if (Object.prototype.toString.call(moduleExports) === '[object Module]') {\n // In ESM we take the usual route and just replace the exports we want to instrument\n for (const method of INSTRUMENTED_METHODS) {\n moduleExports[method] = generatePatch(moduleExports[method]);\n }\n\n return moduleExports;\n } else {\n // In CJS we can't replace the exports in the original module because they\n // don't have setters, so we create a new object with the same properties\n const patchedModuleExports = INSTRUMENTED_METHODS.reduce((acc, curr) => {\n acc[curr] = generatePatch(moduleExports[curr]);\n return acc;\n }, {} as PatchedModuleExports);\n\n return { ...moduleExports, ...patchedModuleExports };\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"type":"module","version":"9.40.0","sideEffects":false}
1
+ {"type":"module","version":"9.42.0","sideEffects":false}
@@ -1,6 +1,40 @@
1
1
  import type { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation';
2
2
  import type { FastifyInstance, FastifyReply, FastifyRequest } from './types';
3
3
  import { FastifyInstrumentationV3 } from './v3/instrumentation';
4
+ /**
5
+ * Options for the Fastify integration.
6
+ *
7
+ * `shouldHandleError` - Callback method deciding whether error should be captured and sent to Sentry
8
+ * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.
9
+ * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.
10
+ *
11
+ * @example
12
+ *
13
+ * ```javascript
14
+ * Sentry.init({
15
+ * integrations: [
16
+ * Sentry.fastifyIntegration({
17
+ * shouldHandleError(_error, _request, reply) {
18
+ * return reply.statusCode >= 500;
19
+ * },
20
+ * });
21
+ * },
22
+ * });
23
+ * ```
24
+ *
25
+ */
26
+ interface FastifyIntegrationOptions {
27
+ /**
28
+ * Callback method deciding whether error should be captured and sent to Sentry
29
+ * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.
30
+ * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.
31
+ *
32
+ * @param error Captured Fastify error
33
+ * @param request Fastify request (or any object containing at least method, routeOptions.url, and routerPath)
34
+ * @param reply Fastify reply (or any object containing at least statusCode)
35
+ */
36
+ shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;
37
+ }
4
38
  interface FastifyHandlerOptions {
5
39
  /**
6
40
  * Callback method deciding whether error should be captured and sent to Sentry
@@ -11,6 +45,7 @@ interface FastifyHandlerOptions {
11
45
  *
12
46
  * @example
13
47
  *
48
+ *
14
49
  * ```javascript
15
50
  * setupFastifyErrorHandler(app, {
16
51
  * shouldHandleError(_error, _request, reply) {
@@ -19,6 +54,7 @@ interface FastifyHandlerOptions {
19
54
  * });
20
55
  * ```
21
56
  *
57
+ *
22
58
  * If using TypeScript, you can cast the request and reply to get full type safety.
23
59
  *
24
60
  * ```typescript
@@ -38,7 +74,7 @@ interface FastifyHandlerOptions {
38
74
  export declare const instrumentFastifyV3: ((options?: unknown) => FastifyInstrumentationV3) & {
39
75
  id: string;
40
76
  };
41
- export declare const instrumentFastify: ((options?: unknown) => Instrumentation<InstrumentationConfig & FastifyHandlerOptions>) & {
77
+ export declare const instrumentFastify: ((options?: unknown) => Instrumentation<InstrumentationConfig & FastifyIntegrationOptions>) & {
42
78
  id: string;
43
79
  };
44
80
  /**
@@ -57,7 +93,7 @@ export declare const instrumentFastify: ((options?: unknown) => Instrumentation<
57
93
  * })
58
94
  * ```
59
95
  */
60
- export declare const fastifyIntegration: () => import("@sentry/core").Integration;
96
+ export declare const fastifyIntegration: (options?: Partial<FastifyIntegrationOptions> | undefined) => import("@sentry/core").Integration;
61
97
  /**
62
98
  * Add an Fastify error handler to capture errors to Sentry.
63
99
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing/fastify/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAe7F,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,UAAU,qBAAqB;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,iBAAiB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;CAC5F;AAKD,eAAO,MAAM,mBAAmB;;CAAoF,CAAC;AAiCrH,eAAO,MAAM,iBAAiB;;CAqC5B,CAAC;AAYH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,0CAAyC,CAAC;AAazE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,IAAI,CAiBjH"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing/fastify/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAe7F,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,UAAU,yBAAyB;IACjC;;;;;;;;OAQG;IACH,iBAAiB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;CAC5F;AAED,UAAU,qBAAqB;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,iBAAiB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;CAC5F;AAMD,eAAO,MAAM,mBAAmB;;CAAoF,CAAC;AA0CrH,eAAO,MAAM,iBAAiB;;CAmC5B,CAAC;AAsBH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,kGAE9B,CAAC;AAaF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,IAAI,CAoBjH"}
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing/vercelai/instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAC;AAC7G,OAAO,EAAE,mBAAmB,EAAuC,MAAM,gCAAgC,CAAC;AAyB1G,UAAU,gBAAgB;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,2BAA2B,EAAE,gBAAgB,GAAG,SAAS,EACzD,sBAAsB,EAAE,gBAAgB,EACxC,0BAA0B,EAAE,OAAO,GAAG,SAAS,EAC/C,uBAAuB,EAAE,OAAO,GAC/B;IAAE,YAAY,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAoBnD;AAED;;;;;GAKG;AACH,qBAAa,6BAA8B,SAAQ,mBAAmB;IACpE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAsB;gBAErB,MAAM,GAAE,qBAA0B;IAIrD;;OAEG;IACI,IAAI,IAAI,+BAA+B;IAK9C;;;OAGG;IACI,eAAe,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAQlD;;OAEG;IACH,OAAO,CAAC,MAAM;CAuDf"}
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../../../../src/integrations/tracing/vercelai/instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAC;AAC7G,OAAO,EAAE,mBAAmB,EAAuC,MAAM,gCAAgC,CAAC;AA+B1G,UAAU,gBAAgB;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,2BAA2B,EAAE,gBAAgB,GAAG,SAAS,EACzD,sBAAsB,EAAE,gBAAgB,EACxC,0BAA0B,EAAE,OAAO,GAAG,SAAS,EAC/C,uBAAuB,EAAE,OAAO,GAC/B;IAAE,YAAY,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAoBnD;AAED;;;;;GAKG;AACH,qBAAa,6BAA8B,SAAQ,mBAAmB;IACpE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAsB;gBAErB,MAAM,GAAE,qBAA0B;IAIrD;;OAEG;IACI,IAAI,IAAI,+BAA+B;IAK9C;;;OAGG;IACI,eAAe,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAQlD;;OAEG;IACH,OAAO,CAAC,MAAM;CAoEf"}
@@ -1,6 +1,40 @@
1
1
  import { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation';
2
2
  import { FastifyInstance, FastifyReply, FastifyRequest } from './types';
3
3
  import { FastifyInstrumentationV3 } from './v3/instrumentation';
4
+ /**
5
+ * Options for the Fastify integration.
6
+ *
7
+ * `shouldHandleError` - Callback method deciding whether error should be captured and sent to Sentry
8
+ * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.
9
+ * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.
10
+ *
11
+ * @example
12
+ *
13
+ * ```javascript
14
+ * Sentry.init({
15
+ * integrations: [
16
+ * Sentry.fastifyIntegration({
17
+ * shouldHandleError(_error, _request, reply) {
18
+ * return reply.statusCode >= 500;
19
+ * },
20
+ * });
21
+ * },
22
+ * });
23
+ * ```
24
+ *
25
+ */
26
+ interface FastifyIntegrationOptions {
27
+ /**
28
+ * Callback method deciding whether error should be captured and sent to Sentry
29
+ * This is used on Fastify v5 where Sentry handles errors in the diagnostics channel.
30
+ * Fastify v3 and v4 use `setupFastifyErrorHandler` instead.
31
+ *
32
+ * @param error Captured Fastify error
33
+ * @param request Fastify request (or any object containing at least method, routeOptions.url, and routerPath)
34
+ * @param reply Fastify reply (or any object containing at least statusCode)
35
+ */
36
+ shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;
37
+ }
4
38
  interface FastifyHandlerOptions {
5
39
  /**
6
40
  * Callback method deciding whether error should be captured and sent to Sentry
@@ -11,6 +45,7 @@ interface FastifyHandlerOptions {
11
45
  *
12
46
  * @example
13
47
  *
48
+ *
14
49
  * ```javascript
15
50
  * setupFastifyErrorHandler(app, {
16
51
  * shouldHandleError(_error, _request, reply) {
@@ -19,6 +54,7 @@ interface FastifyHandlerOptions {
19
54
  * });
20
55
  * ```
21
56
  *
57
+ *
22
58
  * If using TypeScript, you can cast the request and reply to get full type safety.
23
59
  *
24
60
  * ```typescript
@@ -38,7 +74,7 @@ interface FastifyHandlerOptions {
38
74
  export declare const instrumentFastifyV3: ((options?: unknown) => FastifyInstrumentationV3) & {
39
75
  id: string;
40
76
  };
41
- export declare const instrumentFastify: ((options?: unknown) => Instrumentation<InstrumentationConfig & FastifyHandlerOptions>) & {
77
+ export declare const instrumentFastify: ((options?: unknown) => Instrumentation<InstrumentationConfig & FastifyIntegrationOptions>) & {
42
78
  id: string;
43
79
  };
44
80
  /**
@@ -57,7 +93,7 @@ export declare const instrumentFastify: ((options?: unknown) => Instrumentation<
57
93
  * })
58
94
  * ```
59
95
  */
60
- export declare const fastifyIntegration: () => import("@sentry/core").Integration;
96
+ export declare const fastifyIntegration: (options?: Partial<FastifyIntegrationOptions> | undefined) => import("@sentry/core").Integration;
61
97
  /**
62
98
  * Add an Fastify error handler to capture errors to Sentry.
63
99
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/node",
3
- "version": "9.40.0",
3
+ "version": "9.42.0",
4
4
  "description": "Sentry Node SDK using OpenTelemetry for performance instrumentation",
5
5
  "repository": "git://github.com/getsentry/sentry-javascript.git",
6
6
  "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node",
@@ -95,9 +95,9 @@
95
95
  "@opentelemetry/sdk-trace-base": "^1.30.1",
96
96
  "@opentelemetry/semantic-conventions": "^1.34.0",
97
97
  "@prisma/instrumentation": "6.11.1",
98
- "@sentry/core": "9.40.0",
99
- "@sentry/node-core": "9.40.0",
100
- "@sentry/opentelemetry": "9.40.0",
98
+ "@sentry/core": "9.42.0",
99
+ "@sentry/node-core": "9.42.0",
100
+ "@sentry/opentelemetry": "9.42.0",
101
101
  "import-in-the-middle": "^1.14.2",
102
102
  "minimatch": "^9.0.0"
103
103
  },