@sentry/react-router 10.53.1 → 10.54.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.
- package/build/cjs/client/createClientInstrumentation.js +104 -175
- package/build/cjs/client/createClientInstrumentation.js.map +1 -1
- package/build/cjs/client/hydratedRouter.js +27 -74
- package/build/cjs/client/hydratedRouter.js.map +1 -1
- package/build/cjs/client/sdk.js +5 -16
- package/build/cjs/client/sdk.js.map +1 -1
- package/build/cjs/client/sentryOnError.js +4 -27
- package/build/cjs/client/sentryOnError.js.map +1 -1
- package/build/cjs/client/tracingIntegration.js +4 -18
- package/build/cjs/client/tracingIntegration.js.map +1 -1
- package/build/cjs/client/utils.js +5 -17
- package/build/cjs/client/utils.js.map +1 -1
- package/build/cjs/cloudflare/index.js +2 -15
- package/build/cjs/cloudflare/index.js.map +1 -1
- package/build/cjs/common/debug-build.js +0 -5
- package/build/cjs/common/debug-build.js.map +1 -1
- package/build/cjs/common/utils.js +8 -33
- package/build/cjs/common/utils.js.map +1 -1
- package/build/cjs/server/createSentryHandleError.js +4 -13
- package/build/cjs/server/createSentryHandleError.js.map +1 -1
- package/build/cjs/server/createSentryHandleRequest.js +14 -49
- package/build/cjs/server/createSentryHandleRequest.js.map +1 -1
- package/build/cjs/server/createServerInstrumentation.js +76 -114
- package/build/cjs/server/createServerInstrumentation.js.map +1 -1
- package/build/cjs/server/getMetaTagTransformer.js +2 -8
- package/build/cjs/server/getMetaTagTransformer.js.map +1 -1
- package/build/cjs/server/instrumentation/reactRouter.js +21 -47
- package/build/cjs/server/instrumentation/reactRouter.js.map +1 -1
- package/build/cjs/server/instrumentation/util.js +5 -41
- package/build/cjs/server/instrumentation/util.js.map +1 -1
- package/build/cjs/server/integration/lowQualityTransactionsFilterIntegration.js +5 -12
- package/build/cjs/server/integration/lowQualityTransactionsFilterIntegration.js.map +1 -1
- package/build/cjs/server/integration/reactRouterServer.js +10 -42
- package/build/cjs/server/integration/reactRouterServer.js.map +1 -1
- package/build/cjs/server/sdk.js +6 -20
- package/build/cjs/server/sdk.js.map +1 -1
- package/build/cjs/server/serverBuild.js +6 -31
- package/build/cjs/server/serverBuild.js.map +1 -1
- package/build/cjs/server/serverGlobals.js +6 -26
- package/build/cjs/server/serverGlobals.js.map +1 -1
- package/build/cjs/server/wrapSentryHandleRequest.js +13 -53
- package/build/cjs/server/wrapSentryHandleRequest.js.map +1 -1
- package/build/cjs/server/wrapServerAction.js +12 -44
- package/build/cjs/server/wrapServerAction.js.map +1 -1
- package/build/cjs/server/wrapServerLoader.js +12 -44
- package/build/cjs/server/wrapServerLoader.js.map +1 -1
- package/build/cjs/vite/buildEnd/handleOnBuildEnd.js +38 -72
- package/build/cjs/vite/buildEnd/handleOnBuildEnd.js.map +1 -1
- package/build/cjs/vite/makeConfigInjectorPlugin.js +4 -12
- package/build/cjs/vite/makeConfigInjectorPlugin.js.map +1 -1
- package/build/cjs/vite/makeCustomSentryVitePlugins.js +10 -15
- package/build/cjs/vite/makeCustomSentryVitePlugins.js.map +1 -1
- package/build/cjs/vite/makeEnableSourceMapsPlugin.js +19 -51
- package/build/cjs/vite/makeEnableSourceMapsPlugin.js.map +1 -1
- package/build/cjs/vite/makeServerBuildCapturePlugin.js +4 -15
- package/build/cjs/vite/makeServerBuildCapturePlugin.js.map +1 -1
- package/build/cjs/vite/plugin.js +3 -16
- package/build/cjs/vite/plugin.js.map +1 -1
- package/build/esm/client/createClientInstrumentation.js +104 -175
- package/build/esm/client/createClientInstrumentation.js.map +1 -1
- package/build/esm/client/hydratedRouter.js +27 -74
- package/build/esm/client/hydratedRouter.js.map +1 -1
- package/build/esm/client/sdk.js +5 -16
- package/build/esm/client/sdk.js.map +1 -1
- package/build/esm/client/sentryOnError.js +4 -27
- package/build/esm/client/sentryOnError.js.map +1 -1
- package/build/esm/client/tracingIntegration.js +4 -18
- package/build/esm/client/tracingIntegration.js.map +1 -1
- package/build/esm/client/utils.js +5 -17
- package/build/esm/client/utils.js.map +1 -1
- package/build/esm/cloudflare/index.js +2 -15
- package/build/esm/cloudflare/index.js.map +1 -1
- package/build/esm/common/debug-build.js +0 -5
- package/build/esm/common/debug-build.js.map +1 -1
- package/build/esm/common/utils.js +8 -33
- package/build/esm/common/utils.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/server/createSentryHandleError.js +4 -13
- package/build/esm/server/createSentryHandleError.js.map +1 -1
- package/build/esm/server/createSentryHandleRequest.js +14 -49
- package/build/esm/server/createSentryHandleRequest.js.map +1 -1
- package/build/esm/server/createServerInstrumentation.js +76 -114
- package/build/esm/server/createServerInstrumentation.js.map +1 -1
- package/build/esm/server/getMetaTagTransformer.js +2 -8
- package/build/esm/server/getMetaTagTransformer.js.map +1 -1
- package/build/esm/server/instrumentation/reactRouter.js +21 -47
- package/build/esm/server/instrumentation/reactRouter.js.map +1 -1
- package/build/esm/server/instrumentation/util.js +5 -41
- package/build/esm/server/instrumentation/util.js.map +1 -1
- package/build/esm/server/integration/lowQualityTransactionsFilterIntegration.js +5 -12
- package/build/esm/server/integration/lowQualityTransactionsFilterIntegration.js.map +1 -1
- package/build/esm/server/integration/reactRouterServer.js +10 -42
- package/build/esm/server/integration/reactRouterServer.js.map +1 -1
- package/build/esm/server/sdk.js +6 -20
- package/build/esm/server/sdk.js.map +1 -1
- package/build/esm/server/serverBuild.js +6 -31
- package/build/esm/server/serverBuild.js.map +1 -1
- package/build/esm/server/serverGlobals.js +6 -26
- package/build/esm/server/serverGlobals.js.map +1 -1
- package/build/esm/server/wrapSentryHandleRequest.js +13 -53
- package/build/esm/server/wrapSentryHandleRequest.js.map +1 -1
- package/build/esm/server/wrapServerAction.js +12 -44
- package/build/esm/server/wrapServerAction.js.map +1 -1
- package/build/esm/server/wrapServerLoader.js +12 -44
- package/build/esm/server/wrapServerLoader.js.map +1 -1
- package/build/esm/vite/buildEnd/handleOnBuildEnd.js +38 -72
- package/build/esm/vite/buildEnd/handleOnBuildEnd.js.map +1 -1
- package/build/esm/vite/makeConfigInjectorPlugin.js +4 -12
- package/build/esm/vite/makeConfigInjectorPlugin.js.map +1 -1
- package/build/esm/vite/makeCustomSentryVitePlugins.js +10 -15
- package/build/esm/vite/makeCustomSentryVitePlugins.js.map +1 -1
- package/build/esm/vite/makeEnableSourceMapsPlugin.js +19 -51
- package/build/esm/vite/makeEnableSourceMapsPlugin.js.map +1 -1
- package/build/esm/vite/makeServerBuildCapturePlugin.js +4 -15
- package/build/esm/vite/makeServerBuildCapturePlugin.js.map +1 -1
- package/build/esm/vite/plugin.js +3 -16
- package/build/esm/vite/plugin.js.map +1 -1
- package/build/types/client/hydratedRouter.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createServerInstrumentation.js","sources":["../../../src/server/createServerInstrumentation.ts"],"sourcesContent":["import { context, createContextKey } from '@opentelemetry/api';\nimport { getRPCMetadata, RPCType } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport {\n debug,\n flushIfServerless,\n getActiveSpan,\n getCurrentScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n SPAN_STATUS_ERROR,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport type { InstrumentableRequestHandler, InstrumentableRoute, ServerInstrumentation } from '../common/types';\nimport { captureInstrumentationError, getPathFromRequest, getPattern, normalizeRoutePath } from '../common/utils';\nimport { getMiddlewareName } from './serverBuild';\nimport { markInstrumentationApiUsed } from './serverGlobals';\n\nconst MIDDLEWARE_COUNTER_KEY = createContextKey('sentry_react_router_middleware_counter');\n\n// Re-export for backward compatibility and external use\nexport { isInstrumentationApiUsed } from './serverGlobals';\n\n/**\n * Options for creating Sentry server instrumentation.\n */\nexport interface CreateSentryServerInstrumentationOptions {\n /**\n * Whether to capture errors from loaders/actions automatically.\n * @default true\n */\n captureErrors?: boolean;\n}\n\n/**\n * Creates a Sentry server instrumentation for React Router's instrumentation API.\n * @experimental\n */\nexport function createSentryServerInstrumentation(\n options: CreateSentryServerInstrumentationOptions = {},\n): ServerInstrumentation {\n const { captureErrors = true } = options;\n\n markInstrumentationApiUsed();\n DEBUG_BUILD && debug.log('React Router server instrumentation API enabled.');\n\n return {\n handler(handler: InstrumentableRequestHandler) {\n handler.instrument({\n async request(handleRequest, info) {\n const pathname = getPathFromRequest(info.request);\n const activeSpan = getActiveSpan();\n const existingRootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;\n\n const counterStore = { counters: {} as Record<string, number> };\n const ctx = context.active().setValue(MIDDLEWARE_COUNTER_KEY, counterStore);\n\n await context.with(ctx, async () => {\n if (existingRootSpan) {\n updateSpanName(existingRootSpan, `${info.request.method} ${pathname}`);\n existingRootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.instrumentation_api',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n });\n\n try {\n const result = await handleRequest();\n if (result.status === 'error' && result.error instanceof Error) {\n existingRootSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.request_handler', {\n 'http.method': info.request.method,\n 'http.url': pathname,\n });\n }\n } finally {\n await flushIfServerless();\n }\n } else {\n await startSpan(\n {\n name: `${info.request.method} ${pathname}`,\n forceTransaction: true,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.instrumentation_api',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n 'http.request.method': info.request.method,\n 'url.path': pathname,\n 'url.full': info.request.url,\n },\n },\n async span => {\n try {\n const result = await handleRequest();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.request_handler', {\n 'http.method': info.request.method,\n 'http.url': pathname,\n });\n }\n } finally {\n await flushIfServerless();\n }\n },\n );\n }\n });\n },\n });\n },\n\n route(route: InstrumentableRoute) {\n const routeId = route.id;\n\n route.instrument({\n async loader(callLoader, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.loader',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLoader();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.loader', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async action(callAction, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.action',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callAction();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.action', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async middleware(callMiddleware, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n const counterStore = context.active().getValue(MIDDLEWARE_COUNTER_KEY) as\n | { counters: Record<string, number> }\n | undefined;\n let middlewareIndex = 0;\n if (counterStore) {\n middlewareIndex = counterStore.counters[routeId] ?? 0;\n counterStore.counters[routeId] = middlewareIndex + 1;\n }\n\n const middlewareName = getMiddlewareName(routeId, middlewareIndex);\n\n await startSpan(\n {\n name: `middleware ${middlewareName || routeId}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.middleware',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n 'react_router.route.id': routeId,\n [ATTR_HTTP_ROUTE]: routePattern,\n ...(middlewareName && { 'react_router.middleware.name': middlewareName }),\n 'react_router.middleware.index': middlewareIndex,\n },\n },\n async span => {\n const result = await callMiddleware();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.middleware', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async lazy(callLazy) {\n await startSpan(\n {\n name: 'Lazy Route Load',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.lazy',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLazy();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.lazy', {});\n }\n },\n );\n },\n });\n },\n };\n}\n\nfunction updateRootSpanWithRoute(method: string, pattern: string | undefined, urlPath: string): void {\n const activeSpan = getActiveSpan();\n if (!activeSpan) return;\n const rootSpan = getRootSpan(activeSpan);\n if (!rootSpan) return;\n\n // Skip update if URL path is invalid (failed to parse)\n if (!urlPath || urlPath === '<unknown>') {\n DEBUG_BUILD && debug.warn('Cannot update span with invalid URL path:', urlPath);\n return;\n }\n\n const hasPattern = !!pattern;\n const routeName = hasPattern ? normalizeRoutePath(pattern) || urlPath : urlPath;\n\n const rpcMetadata = getRPCMetadata(context.active());\n if (rpcMetadata?.type === RPCType.HTTP) {\n rpcMetadata.route = routeName;\n }\n\n const transactionName = `${method} ${routeName}`;\n updateSpanName(rootSpan, transactionName);\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: hasPattern ? 'route' : 'url',\n });\n\n // Also update the scope's transaction name so errors captured during this request\n // have the correct transaction name (not the initial placeholder like \"GET *\")\n getCurrentScope().setTransactionName(transactionName);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createServerInstrumentation.js","sources":["../../../src/server/createServerInstrumentation.ts"],"sourcesContent":["import { context, createContextKey } from '@opentelemetry/api';\nimport { getRPCMetadata, RPCType } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport {\n debug,\n flushIfServerless,\n getActiveSpan,\n getCurrentScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n SPAN_STATUS_ERROR,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport type { InstrumentableRequestHandler, InstrumentableRoute, ServerInstrumentation } from '../common/types';\nimport { captureInstrumentationError, getPathFromRequest, getPattern, normalizeRoutePath } from '../common/utils';\nimport { getMiddlewareName } from './serverBuild';\nimport { markInstrumentationApiUsed } from './serverGlobals';\n\nconst MIDDLEWARE_COUNTER_KEY = createContextKey('sentry_react_router_middleware_counter');\n\n// Re-export for backward compatibility and external use\nexport { isInstrumentationApiUsed } from './serverGlobals';\n\n/**\n * Options for creating Sentry server instrumentation.\n */\nexport interface CreateSentryServerInstrumentationOptions {\n /**\n * Whether to capture errors from loaders/actions automatically.\n * @default true\n */\n captureErrors?: boolean;\n}\n\n/**\n * Creates a Sentry server instrumentation for React Router's instrumentation API.\n * @experimental\n */\nexport function createSentryServerInstrumentation(\n options: CreateSentryServerInstrumentationOptions = {},\n): ServerInstrumentation {\n const { captureErrors = true } = options;\n\n markInstrumentationApiUsed();\n DEBUG_BUILD && debug.log('React Router server instrumentation API enabled.');\n\n return {\n handler(handler: InstrumentableRequestHandler) {\n handler.instrument({\n async request(handleRequest, info) {\n const pathname = getPathFromRequest(info.request);\n const activeSpan = getActiveSpan();\n const existingRootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;\n\n const counterStore = { counters: {} as Record<string, number> };\n const ctx = context.active().setValue(MIDDLEWARE_COUNTER_KEY, counterStore);\n\n await context.with(ctx, async () => {\n if (existingRootSpan) {\n updateSpanName(existingRootSpan, `${info.request.method} ${pathname}`);\n existingRootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.instrumentation_api',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n });\n\n try {\n const result = await handleRequest();\n if (result.status === 'error' && result.error instanceof Error) {\n existingRootSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.request_handler', {\n 'http.method': info.request.method,\n 'http.url': pathname,\n });\n }\n } finally {\n await flushIfServerless();\n }\n } else {\n await startSpan(\n {\n name: `${info.request.method} ${pathname}`,\n forceTransaction: true,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.instrumentation_api',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n 'http.request.method': info.request.method,\n 'url.path': pathname,\n 'url.full': info.request.url,\n },\n },\n async span => {\n try {\n const result = await handleRequest();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.request_handler', {\n 'http.method': info.request.method,\n 'http.url': pathname,\n });\n }\n } finally {\n await flushIfServerless();\n }\n },\n );\n }\n });\n },\n });\n },\n\n route(route: InstrumentableRoute) {\n const routeId = route.id;\n\n route.instrument({\n async loader(callLoader, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.loader',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLoader();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.loader', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async action(callAction, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.action',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callAction();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.action', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async middleware(callMiddleware, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n const counterStore = context.active().getValue(MIDDLEWARE_COUNTER_KEY) as\n | { counters: Record<string, number> }\n | undefined;\n let middlewareIndex = 0;\n if (counterStore) {\n middlewareIndex = counterStore.counters[routeId] ?? 0;\n counterStore.counters[routeId] = middlewareIndex + 1;\n }\n\n const middlewareName = getMiddlewareName(routeId, middlewareIndex);\n\n await startSpan(\n {\n name: `middleware ${middlewareName || routeId}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.middleware',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n 'react_router.route.id': routeId,\n [ATTR_HTTP_ROUTE]: routePattern,\n ...(middlewareName && { 'react_router.middleware.name': middlewareName }),\n 'react_router.middleware.index': middlewareIndex,\n },\n },\n async span => {\n const result = await callMiddleware();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.middleware', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async lazy(callLazy) {\n await startSpan(\n {\n name: 'Lazy Route Load',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.lazy',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLazy();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.lazy', {});\n }\n },\n );\n },\n });\n },\n };\n}\n\nfunction updateRootSpanWithRoute(method: string, pattern: string | undefined, urlPath: string): void {\n const activeSpan = getActiveSpan();\n if (!activeSpan) return;\n const rootSpan = getRootSpan(activeSpan);\n if (!rootSpan) return;\n\n // Skip update if URL path is invalid (failed to parse)\n if (!urlPath || urlPath === '<unknown>') {\n DEBUG_BUILD && debug.warn('Cannot update span with invalid URL path:', urlPath);\n return;\n }\n\n const hasPattern = !!pattern;\n const routeName = hasPattern ? normalizeRoutePath(pattern) || urlPath : urlPath;\n\n const rpcMetadata = getRPCMetadata(context.active());\n if (rpcMetadata?.type === RPCType.HTTP) {\n rpcMetadata.route = routeName;\n }\n\n const transactionName = `${method} ${routeName}`;\n updateSpanName(rootSpan, transactionName);\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: hasPattern ? 'route' : 'url',\n });\n\n // Also update the scope's transaction name so errors captured during this request\n // have the correct transaction name (not the initial placeholder like \"GET *\")\n getCurrentScope().setTransactionName(transactionName);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { Transform } from 'node:stream';
|
|
2
2
|
import { getTraceMetaTags } from '@sentry/core';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Injects Sentry trace meta tags into the HTML response by piping through a transform stream.
|
|
6
|
-
* This enables distributed tracing by adding trace context to the HTML document head.
|
|
7
|
-
*
|
|
8
|
-
* @param body - PassThrough stream containing the HTML response body to modify
|
|
9
|
-
*/
|
|
10
4
|
function getMetaTagTransformer(body) {
|
|
11
|
-
const headClosingTag =
|
|
5
|
+
const headClosingTag = "</head>";
|
|
12
6
|
const htmlMetaTagTransformer = new Transform({
|
|
13
7
|
transform(chunk, _encoding, callback) {
|
|
14
8
|
const html = Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);
|
|
@@ -18,7 +12,7 @@ function getMetaTagTransformer(body) {
|
|
|
18
12
|
return;
|
|
19
13
|
}
|
|
20
14
|
callback(null, chunk);
|
|
21
|
-
}
|
|
15
|
+
}
|
|
22
16
|
});
|
|
23
17
|
htmlMetaTagTransformer.pipe(body);
|
|
24
18
|
return htmlMetaTagTransformer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getMetaTagTransformer.js","sources":["../../../src/server/getMetaTagTransformer.ts"],"sourcesContent":["import type { PassThrough } from 'node:stream';\nimport { Transform } from 'node:stream';\nimport { getTraceMetaTags } from '@sentry/core';\n\n/**\n * Injects Sentry trace meta tags into the HTML response by piping through a transform stream.\n * This enables distributed tracing by adding trace context to the HTML document head.\n *\n * @param body - PassThrough stream containing the HTML response body to modify\n */\nexport function getMetaTagTransformer(body: PassThrough): Transform {\n const headClosingTag = '</head>';\n const htmlMetaTagTransformer = new Transform({\n transform(chunk, _encoding, callback) {\n const html = Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);\n if (html.includes(headClosingTag)) {\n const modifiedHtml = html.replace(headClosingTag, `${getTraceMetaTags()}${headClosingTag}`);\n callback(null, modifiedHtml);\n return;\n }\n callback(null, chunk);\n },\n });\n htmlMetaTagTransformer.pipe(body);\n return htmlMetaTagTransformer;\n}\n"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"getMetaTagTransformer.js","sources":["../../../src/server/getMetaTagTransformer.ts"],"sourcesContent":["import type { PassThrough } from 'node:stream';\nimport { Transform } from 'node:stream';\nimport { getTraceMetaTags } from '@sentry/core';\n\n/**\n * Injects Sentry trace meta tags into the HTML response by piping through a transform stream.\n * This enables distributed tracing by adding trace context to the HTML document head.\n *\n * @param body - PassThrough stream containing the HTML response body to modify\n */\nexport function getMetaTagTransformer(body: PassThrough): Transform {\n const headClosingTag = '</head>';\n const htmlMetaTagTransformer = new Transform({\n transform(chunk, _encoding, callback) {\n const html = Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);\n if (html.includes(headClosingTag)) {\n const modifiedHtml = html.replace(headClosingTag, `${getTraceMetaTags()}${headClosingTag}`);\n callback(null, modifiedHtml);\n return;\n }\n callback(null, chunk);\n },\n });\n htmlMetaTagTransformer.pipe(body);\n return htmlMetaTagTransformer;\n}\n"],"names":[],"mappings":";;;AAUO,SAAS,sBAAsB,IAAA,EAA8B;AAClE,EAAA,MAAM,cAAA,GAAiB,SAAA;AACvB,EAAA,MAAM,sBAAA,GAAyB,IAAI,SAAA,CAAU;AAAA,IAC3C,SAAA,CAAU,KAAA,EAAO,SAAA,EAAW,QAAA,EAAU;AACpC,MAAA,MAAM,IAAA,GAAO,OAAO,QAAA,CAAS,KAAK,IAAI,KAAA,CAAM,QAAA,EAAS,GAAI,MAAA,CAAO,KAAK,CAAA;AACrE,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,cAAc,CAAA,EAAG;AACjC,QAAA,MAAM,YAAA,GAAe,KAAK,OAAA,CAAQ,cAAA,EAAgB,GAAG,gBAAA,EAAkB,CAAA,EAAG,cAAc,CAAA,CAAE,CAAA;AAC1F,QAAA,QAAA,CAAS,MAAM,YAAY,CAAA;AAC3B,QAAA;AAAA,MACF;AACA,MAAA,QAAA,CAAS,MAAM,KAAK,CAAA;AAAA,IACtB;AAAA,GACD,CAAA;AACD,EAAA,sBAAA,CAAuB,KAAK,IAAI,CAAA;AAChC,EAAA,OAAO,sBAAA;AACT;;;;"}
|
|
@@ -6,23 +6,17 @@ import { isServerBuildLike, setServerBuild } from '../serverBuild.js';
|
|
|
6
6
|
import { isInstrumentationApiUsed, isOtelDataLoaderSpanCreationEnabled } from '../serverGlobals.js';
|
|
7
7
|
import { isDataRequest, getOpName, getSpanName } from './util.js';
|
|
8
8
|
|
|
9
|
-
const supportedVersions = [
|
|
10
|
-
const COMPONENT =
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Instrumentation for React Router's server request handler.
|
|
14
|
-
* This patches the requestHandler function to add Sentry performance monitoring for data loaders.
|
|
15
|
-
*/
|
|
9
|
+
const supportedVersions = [">=7.0.0"];
|
|
10
|
+
const COMPONENT = "react-router";
|
|
16
11
|
class ReactRouterInstrumentation extends InstrumentationBase {
|
|
17
|
-
|
|
18
|
-
super(
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
super("ReactRouterInstrumentation", SDK_VERSION, config);
|
|
19
14
|
}
|
|
20
|
-
|
|
21
15
|
/**
|
|
22
16
|
* Initializes the instrumentation by defining the React Router server modules to be patched.
|
|
23
17
|
*/
|
|
24
18
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
25
|
-
|
|
19
|
+
init() {
|
|
26
20
|
const reactRouterServerModule = new InstrumentationNodeModuleDefinition(
|
|
27
21
|
COMPONENT,
|
|
28
22
|
supportedVersions,
|
|
@@ -30,31 +24,26 @@ class ReactRouterInstrumentation extends InstrumentationBase {
|
|
|
30
24
|
return this._createPatchedModuleProxy(moduleExports);
|
|
31
25
|
},
|
|
32
26
|
(_moduleExports) => {
|
|
33
|
-
// nothing to unwrap here
|
|
34
27
|
return _moduleExports;
|
|
35
|
-
}
|
|
28
|
+
}
|
|
36
29
|
);
|
|
37
|
-
|
|
38
30
|
return reactRouterServerModule;
|
|
39
31
|
}
|
|
40
|
-
|
|
41
32
|
/**
|
|
42
33
|
* Creates a proxy around the React Router module exports that patches the createRequestHandler function.
|
|
43
34
|
* This allows us to wrap the request handler to add performance monitoring for data loaders and actions.
|
|
44
35
|
*/
|
|
45
|
-
|
|
36
|
+
_createPatchedModuleProxy(moduleExports) {
|
|
46
37
|
return new Proxy(moduleExports, {
|
|
47
38
|
get(target, prop, receiver) {
|
|
48
|
-
if (prop ===
|
|
39
|
+
if (prop === "createRequestHandler") {
|
|
49
40
|
const original = target[prop];
|
|
50
|
-
return function sentryWrappedCreateRequestHandler(
|
|
51
|
-
// Capture the ServerBuild reference for middleware name lookup
|
|
41
|
+
return function sentryWrappedCreateRequestHandler(...args) {
|
|
52
42
|
const build = args[0];
|
|
53
43
|
if (isServerBuildLike(build)) {
|
|
54
44
|
setServerBuild(build);
|
|
55
|
-
} else if (typeof build ===
|
|
56
|
-
|
|
57
|
-
const originalBuildFn = build ;
|
|
45
|
+
} else if (typeof build === "function") {
|
|
46
|
+
const originalBuildFn = build;
|
|
58
47
|
args[0] = async function sentryWrappedBuildFn() {
|
|
59
48
|
const resolvedBuild = await originalBuildFn();
|
|
60
49
|
if (isServerBuildLike(resolvedBuild)) {
|
|
@@ -63,65 +52,50 @@ class ReactRouterInstrumentation extends InstrumentationBase {
|
|
|
63
52
|
return resolvedBuild;
|
|
64
53
|
};
|
|
65
54
|
}
|
|
66
|
-
|
|
67
55
|
const originalRequestHandler = original.apply(this, args);
|
|
68
|
-
|
|
69
56
|
return async function sentryWrappedRequestHandler(request, initialContext) {
|
|
70
|
-
// Skip OTEL span creation when instrumentation API is active or when span creation is not enabled.
|
|
71
|
-
// Checked per-request (not at handler-creation time) because in dev, createRequestHandler runs before entry.server.tsx.
|
|
72
57
|
if (isInstrumentationApiUsed() || !isOtelDataLoaderSpanCreationEnabled()) {
|
|
73
58
|
return originalRequestHandler(request, initialContext);
|
|
74
59
|
}
|
|
75
|
-
|
|
76
60
|
let url;
|
|
77
61
|
try {
|
|
78
62
|
url = new URL(request.url);
|
|
79
63
|
} catch {
|
|
80
64
|
return originalRequestHandler(request, initialContext);
|
|
81
65
|
}
|
|
82
|
-
|
|
83
|
-
// We currently just want to trace loaders and actions
|
|
84
66
|
if (!isDataRequest(url.pathname)) {
|
|
85
67
|
return originalRequestHandler(request, initialContext);
|
|
86
68
|
}
|
|
87
|
-
|
|
88
69
|
const activeSpan = getActiveSpan();
|
|
89
70
|
const rootSpan = activeSpan && getRootSpan(activeSpan);
|
|
90
|
-
|
|
91
71
|
if (!rootSpan) {
|
|
92
|
-
DEBUG_BUILD && debug.log(
|
|
72
|
+
DEBUG_BUILD && debug.log("No active root span found, skipping tracing for data request");
|
|
93
73
|
return originalRequestHandler(request, initialContext);
|
|
94
74
|
}
|
|
95
|
-
|
|
96
|
-
// We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route
|
|
97
|
-
// So we force this to be a more sensible name here
|
|
98
|
-
// TODO: try to set derived parameterized route from build here (args[0])
|
|
99
75
|
const spanData = spanToJSON(rootSpan);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
updateSpanName(rootSpan, `${request.method} ${target}`);
|
|
76
|
+
const target2 = spanData.data[SEMATTRS_HTTP_TARGET] || url.pathname;
|
|
77
|
+
updateSpanName(rootSpan, `${request.method} ${target2}`);
|
|
103
78
|
rootSpan.setAttributes({
|
|
104
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]:
|
|
105
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]:
|
|
79
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "url",
|
|
80
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.react_router.server"
|
|
106
81
|
});
|
|
107
|
-
|
|
108
82
|
return startSpan(
|
|
109
83
|
{
|
|
110
84
|
name: getSpanName(url.pathname, request.method),
|
|
111
85
|
attributes: {
|
|
112
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]:
|
|
113
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method)
|
|
114
|
-
}
|
|
86
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.react_router.server",
|
|
87
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method)
|
|
88
|
+
}
|
|
115
89
|
},
|
|
116
90
|
() => {
|
|
117
91
|
return originalRequestHandler(request, initialContext);
|
|
118
|
-
}
|
|
92
|
+
}
|
|
119
93
|
);
|
|
120
94
|
};
|
|
121
95
|
};
|
|
122
96
|
}
|
|
123
97
|
return Reflect.get(target, prop, receiver);
|
|
124
|
-
}
|
|
98
|
+
}
|
|
125
99
|
});
|
|
126
100
|
}
|
|
127
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactRouter.js","sources":["../../../../src/server/instrumentation/reactRouter.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport {\n debug,\n getActiveSpan,\n getRootSpan,\n SDK_VERSION,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport type * as reactRouter from 'react-router';\nimport { DEBUG_BUILD } from '../../common/debug-build';\nimport { isServerBuildLike, setServerBuild } from '../serverBuild';\nimport { isInstrumentationApiUsed, isOtelDataLoaderSpanCreationEnabled } from '../serverGlobals';\nimport { getOpName, getSpanName, isDataRequest } from './util';\n\ntype ReactRouterModuleExports = typeof reactRouter;\n\nconst supportedVersions = ['>=7.0.0'];\nconst COMPONENT = 'react-router';\n\n/**\n * Instrumentation for React Router's server request handler.\n * This patches the requestHandler function to add Sentry performance monitoring for data loaders.\n */\nexport class ReactRouterInstrumentation extends InstrumentationBase<InstrumentationConfig> {\n public constructor(config: InstrumentationConfig = {}) {\n super('ReactRouterInstrumentation', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the React Router server modules to be patched.\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition {\n const reactRouterServerModule = new InstrumentationNodeModuleDefinition(\n COMPONENT,\n supportedVersions,\n (moduleExports: ReactRouterModuleExports) => {\n return this._createPatchedModuleProxy(moduleExports);\n },\n (_moduleExports: unknown) => {\n // nothing to unwrap here\n return _moduleExports;\n },\n );\n\n return reactRouterServerModule;\n }\n\n /**\n * Creates a proxy around the React Router module exports that patches the createRequestHandler function.\n * This allows us to wrap the request handler to add performance monitoring for data loaders and actions.\n */\n private _createPatchedModuleProxy(moduleExports: ReactRouterModuleExports): ReactRouterModuleExports {\n return new Proxy(moduleExports, {\n get(target, prop, receiver) {\n if (prop === 'createRequestHandler') {\n const original = target[prop];\n return function sentryWrappedCreateRequestHandler(this: unknown, ...args: unknown[]) {\n // Capture the ServerBuild reference for middleware name lookup\n const build = args[0];\n if (isServerBuildLike(build)) {\n setServerBuild(build);\n } else if (typeof build === 'function') {\n // Build arg can be a factory function (dev mode HMR). Wrap to capture resolved build.\n const originalBuildFn = build as () => unknown;\n args[0] = async function sentryWrappedBuildFn() {\n const resolvedBuild = await originalBuildFn();\n if (isServerBuildLike(resolvedBuild)) {\n setServerBuild(resolvedBuild);\n }\n return resolvedBuild;\n };\n }\n\n const originalRequestHandler = original.apply(this, args);\n\n return async function sentryWrappedRequestHandler(request: Request, initialContext?: unknown) {\n // Skip OTEL span creation when instrumentation API is active or when span creation is not enabled.\n // Checked per-request (not at handler-creation time) because in dev, createRequestHandler runs before entry.server.tsx.\n if (isInstrumentationApiUsed() || !isOtelDataLoaderSpanCreationEnabled()) {\n return originalRequestHandler(request, initialContext);\n }\n\n let url: URL;\n try {\n url = new URL(request.url);\n } catch {\n return originalRequestHandler(request, initialContext);\n }\n\n // We currently just want to trace loaders and actions\n if (!isDataRequest(url.pathname)) {\n return originalRequestHandler(request, initialContext);\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n\n if (!rootSpan) {\n DEBUG_BUILD && debug.log('No active root span found, skipping tracing for data request');\n return originalRequestHandler(request, initialContext);\n }\n\n // We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route\n // So we force this to be a more sensible name here\n // TODO: try to set derived parameterized route from build here (args[0])\n const spanData = spanToJSON(rootSpan);\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET] || url.pathname;\n updateSpanName(rootSpan, `${request.method} ${target}`);\n rootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.server',\n });\n\n return startSpan(\n {\n name: getSpanName(url.pathname, request.method),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method),\n },\n },\n () => {\n return originalRequestHandler(request, initialContext);\n },\n );\n };\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n }\n}\n"],"names":[],"mappings":";;;;;;;;AAuBA,MAAM,iBAAA,GAAoB,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"reactRouter.js","sources":["../../../../src/server/instrumentation/reactRouter.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport {\n debug,\n getActiveSpan,\n getRootSpan,\n SDK_VERSION,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport type * as reactRouter from 'react-router';\nimport { DEBUG_BUILD } from '../../common/debug-build';\nimport { isServerBuildLike, setServerBuild } from '../serverBuild';\nimport { isInstrumentationApiUsed, isOtelDataLoaderSpanCreationEnabled } from '../serverGlobals';\nimport { getOpName, getSpanName, isDataRequest } from './util';\n\ntype ReactRouterModuleExports = typeof reactRouter;\n\nconst supportedVersions = ['>=7.0.0'];\nconst COMPONENT = 'react-router';\n\n/**\n * Instrumentation for React Router's server request handler.\n * This patches the requestHandler function to add Sentry performance monitoring for data loaders.\n */\nexport class ReactRouterInstrumentation extends InstrumentationBase<InstrumentationConfig> {\n public constructor(config: InstrumentationConfig = {}) {\n super('ReactRouterInstrumentation', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the React Router server modules to be patched.\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition {\n const reactRouterServerModule = new InstrumentationNodeModuleDefinition(\n COMPONENT,\n supportedVersions,\n (moduleExports: ReactRouterModuleExports) => {\n return this._createPatchedModuleProxy(moduleExports);\n },\n (_moduleExports: unknown) => {\n // nothing to unwrap here\n return _moduleExports;\n },\n );\n\n return reactRouterServerModule;\n }\n\n /**\n * Creates a proxy around the React Router module exports that patches the createRequestHandler function.\n * This allows us to wrap the request handler to add performance monitoring for data loaders and actions.\n */\n private _createPatchedModuleProxy(moduleExports: ReactRouterModuleExports): ReactRouterModuleExports {\n return new Proxy(moduleExports, {\n get(target, prop, receiver) {\n if (prop === 'createRequestHandler') {\n const original = target[prop];\n return function sentryWrappedCreateRequestHandler(this: unknown, ...args: unknown[]) {\n // Capture the ServerBuild reference for middleware name lookup\n const build = args[0];\n if (isServerBuildLike(build)) {\n setServerBuild(build);\n } else if (typeof build === 'function') {\n // Build arg can be a factory function (dev mode HMR). Wrap to capture resolved build.\n const originalBuildFn = build as () => unknown;\n args[0] = async function sentryWrappedBuildFn() {\n const resolvedBuild = await originalBuildFn();\n if (isServerBuildLike(resolvedBuild)) {\n setServerBuild(resolvedBuild);\n }\n return resolvedBuild;\n };\n }\n\n const originalRequestHandler = original.apply(this, args);\n\n return async function sentryWrappedRequestHandler(request: Request, initialContext?: unknown) {\n // Skip OTEL span creation when instrumentation API is active or when span creation is not enabled.\n // Checked per-request (not at handler-creation time) because in dev, createRequestHandler runs before entry.server.tsx.\n if (isInstrumentationApiUsed() || !isOtelDataLoaderSpanCreationEnabled()) {\n return originalRequestHandler(request, initialContext);\n }\n\n let url: URL;\n try {\n url = new URL(request.url);\n } catch {\n return originalRequestHandler(request, initialContext);\n }\n\n // We currently just want to trace loaders and actions\n if (!isDataRequest(url.pathname)) {\n return originalRequestHandler(request, initialContext);\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n\n if (!rootSpan) {\n DEBUG_BUILD && debug.log('No active root span found, skipping tracing for data request');\n return originalRequestHandler(request, initialContext);\n }\n\n // We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route\n // So we force this to be a more sensible name here\n // TODO: try to set derived parameterized route from build here (args[0])\n const spanData = spanToJSON(rootSpan);\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET] || url.pathname;\n updateSpanName(rootSpan, `${request.method} ${target}`);\n rootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.server',\n });\n\n return startSpan(\n {\n name: getSpanName(url.pathname, request.method),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method),\n },\n },\n () => {\n return originalRequestHandler(request, initialContext);\n },\n );\n };\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n }\n}\n"],"names":["target"],"mappings":"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"}
|
|
@@ -1,53 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gets the op name for a request based on whether it's a loader or action request.
|
|
3
|
-
* @param pathName The URL pathname to check
|
|
4
|
-
* @param requestMethod The HTTP request method
|
|
5
|
-
*/
|
|
6
1
|
function getOpName(pathName, requestMethod) {
|
|
7
|
-
return isLoaderRequest(pathName, requestMethod)
|
|
8
|
-
? 'function.react_router.loader'
|
|
9
|
-
: isActionRequest(pathName, requestMethod)
|
|
10
|
-
? 'function.react_router.action'
|
|
11
|
-
: 'function.react_router';
|
|
2
|
+
return isLoaderRequest(pathName, requestMethod) ? "function.react_router.loader" : isActionRequest(pathName, requestMethod) ? "function.react_router.action" : "function.react_router";
|
|
12
3
|
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Gets the span name for a request based on whether it's a loader or action request.
|
|
16
|
-
* @param pathName The URL pathname to check
|
|
17
|
-
* @param requestMethod The HTTP request method
|
|
18
|
-
*/
|
|
19
4
|
function getSpanName(pathName, requestMethod) {
|
|
20
|
-
return isLoaderRequest(pathName, requestMethod)
|
|
21
|
-
? 'Executing Server Loader'
|
|
22
|
-
: isActionRequest(pathName, requestMethod)
|
|
23
|
-
? 'Executing Server Action'
|
|
24
|
-
: 'Unknown Data Request';
|
|
5
|
+
return isLoaderRequest(pathName, requestMethod) ? "Executing Server Loader" : isActionRequest(pathName, requestMethod) ? "Executing Server Action" : "Unknown Data Request";
|
|
25
6
|
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Checks if the request is a server loader request
|
|
29
|
-
* @param pathname The URL pathname to check
|
|
30
|
-
* @param requestMethod The HTTP request method
|
|
31
|
-
*/
|
|
32
7
|
function isLoaderRequest(pathname, requestMethod) {
|
|
33
|
-
return isDataRequest(pathname) && requestMethod ===
|
|
8
|
+
return isDataRequest(pathname) && requestMethod === "GET";
|
|
34
9
|
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Checks if the request is a server action request
|
|
38
|
-
* @param pathname The URL pathname to check
|
|
39
|
-
* @param requestMethod The HTTP request method
|
|
40
|
-
*/
|
|
41
10
|
function isActionRequest(pathname, requestMethod) {
|
|
42
|
-
return isDataRequest(pathname) && requestMethod ===
|
|
11
|
+
return isDataRequest(pathname) && requestMethod === "POST";
|
|
43
12
|
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Checks if the request is a react-router data request
|
|
47
|
-
* @param pathname The URL pathname to check
|
|
48
|
-
*/
|
|
49
13
|
function isDataRequest(pathname) {
|
|
50
|
-
return pathname.endsWith(
|
|
14
|
+
return pathname.endsWith(".data");
|
|
51
15
|
}
|
|
52
16
|
|
|
53
17
|
export { getOpName, getSpanName, isActionRequest, isDataRequest, isLoaderRequest };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sources":["../../../../src/server/instrumentation/util.ts"],"sourcesContent":["/**\n * Gets the op name for a request based on whether it's a loader or action request.\n * @param pathName The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function getOpName(pathName: string, requestMethod: string): string {\n return isLoaderRequest(pathName, requestMethod)\n ? 'function.react_router.loader'\n : isActionRequest(pathName, requestMethod)\n ? 'function.react_router.action'\n : 'function.react_router';\n}\n\n/**\n * Gets the span name for a request based on whether it's a loader or action request.\n * @param pathName The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function getSpanName(pathName: string, requestMethod: string): string {\n return isLoaderRequest(pathName, requestMethod)\n ? 'Executing Server Loader'\n : isActionRequest(pathName, requestMethod)\n ? 'Executing Server Action'\n : 'Unknown Data Request';\n}\n\n/**\n * Checks if the request is a server loader request\n * @param pathname The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function isLoaderRequest(pathname: string, requestMethod: string): boolean {\n return isDataRequest(pathname) && requestMethod === 'GET';\n}\n\n/**\n * Checks if the request is a server action request\n * @param pathname The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function isActionRequest(pathname: string, requestMethod: string): boolean {\n return isDataRequest(pathname) && requestMethod === 'POST';\n}\n\n/**\n * Checks if the request is a react-router data request\n * @param pathname The URL pathname to check\n */\nexport function isDataRequest(pathname: string): boolean {\n return pathname.endsWith('.data');\n}\n\nexport const SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE = 'sentry.overwrite-route';\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"util.js","sources":["../../../../src/server/instrumentation/util.ts"],"sourcesContent":["/**\n * Gets the op name for a request based on whether it's a loader or action request.\n * @param pathName The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function getOpName(pathName: string, requestMethod: string): string {\n return isLoaderRequest(pathName, requestMethod)\n ? 'function.react_router.loader'\n : isActionRequest(pathName, requestMethod)\n ? 'function.react_router.action'\n : 'function.react_router';\n}\n\n/**\n * Gets the span name for a request based on whether it's a loader or action request.\n * @param pathName The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function getSpanName(pathName: string, requestMethod: string): string {\n return isLoaderRequest(pathName, requestMethod)\n ? 'Executing Server Loader'\n : isActionRequest(pathName, requestMethod)\n ? 'Executing Server Action'\n : 'Unknown Data Request';\n}\n\n/**\n * Checks if the request is a server loader request\n * @param pathname The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function isLoaderRequest(pathname: string, requestMethod: string): boolean {\n return isDataRequest(pathname) && requestMethod === 'GET';\n}\n\n/**\n * Checks if the request is a server action request\n * @param pathname The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function isActionRequest(pathname: string, requestMethod: string): boolean {\n return isDataRequest(pathname) && requestMethod === 'POST';\n}\n\n/**\n * Checks if the request is a react-router data request\n * @param pathname The URL pathname to check\n */\nexport function isDataRequest(pathname: string): boolean {\n return pathname.endsWith('.data');\n}\n\nexport const SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE = 'sentry.overwrite-route';\n"],"names":[],"mappings":"AAKO,SAAS,SAAA,CAAU,UAAkB,aAAA,EAA+B;AACzE,EAAA,OAAO,eAAA,CAAgB,UAAU,aAAa,CAAA,GAC1C,iCACA,eAAA,CAAgB,QAAA,EAAU,aAAa,CAAA,GACrC,8BAAA,GACA,uBAAA;AACR;AAOO,SAAS,WAAA,CAAY,UAAkB,aAAA,EAA+B;AAC3E,EAAA,OAAO,eAAA,CAAgB,UAAU,aAAa,CAAA,GAC1C,4BACA,eAAA,CAAgB,QAAA,EAAU,aAAa,CAAA,GACrC,yBAAA,GACA,sBAAA;AACR;AAOO,SAAS,eAAA,CAAgB,UAAkB,aAAA,EAAgC;AAChF,EAAA,OAAO,aAAA,CAAc,QAAQ,CAAA,IAAK,aAAA,KAAkB,KAAA;AACtD;AAOO,SAAS,eAAA,CAAgB,UAAkB,aAAA,EAAgC;AAChF,EAAA,OAAO,aAAA,CAAc,QAAQ,CAAA,IAAK,aAAA,KAAkB,MAAA;AACtD;AAMO,SAAS,cAAc,QAAA,EAA2B;AACvD,EAAA,OAAO,QAAA,CAAS,SAAS,OAAO,CAAA;AAClC;;;;"}
|
|
@@ -6,22 +6,15 @@ const LOW_QUALITY_TRANSACTIONS_FILTERS = [
|
|
|
6
6
|
/GET \/@id\//,
|
|
7
7
|
// The span description for the `__manifest` endpoint is `GET *` (`http.route` resolves to `*`).
|
|
8
8
|
// Filter by `http.target` instead, which carries the raw request path.
|
|
9
|
-
{ attributes: {
|
|
9
|
+
{ attributes: { "http.target": /\/__manifest/ } }
|
|
10
10
|
];
|
|
11
|
-
|
|
12
|
-
// TODO(v11): Remove the `_options` parameter (unused and only kept for back-compat with the previous signature)
|
|
13
11
|
const _lowQualityTransactionsFilterIntegration = ((_options) => ({
|
|
14
|
-
name:
|
|
12
|
+
name: "LowQualityTransactionsFilter",
|
|
15
13
|
beforeSetup(client) {
|
|
16
14
|
const opts = client.getOptions();
|
|
17
|
-
opts.ignoreSpans = [...
|
|
18
|
-
}
|
|
19
|
-
}))
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Integration that filters out noisy http transactions such as requests to node_modules, favicon.ico, @id/, __manifest.
|
|
23
|
-
* Adds entries to `ignoreSpans` so the filter applies in both static and streaming trace lifecycles.
|
|
24
|
-
*/
|
|
15
|
+
opts.ignoreSpans = [...opts.ignoreSpans || [], ...LOW_QUALITY_TRANSACTIONS_FILTERS];
|
|
16
|
+
}
|
|
17
|
+
}));
|
|
25
18
|
const lowQualityTransactionsFilterIntegration = defineIntegration(_lowQualityTransactionsFilterIntegration);
|
|
26
19
|
|
|
27
20
|
export { lowQualityTransactionsFilterIntegration };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lowQualityTransactionsFilterIntegration.js","sources":["../../../../src/server/integration/lowQualityTransactionsFilterIntegration.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport type { NodeOptions } from '@sentry/node';\n\nconst LOW_QUALITY_TRANSACTIONS_FILTERS = [\n /GET \\/node_modules\\//,\n /GET \\/favicon\\.ico/,\n /GET \\/@id\\//,\n // The span description for the `__manifest` endpoint is `GET *` (`http.route` resolves to `*`).\n // Filter by `http.target` instead, which carries the raw request path.\n { attributes: { 'http.target': /\\/__manifest/ } },\n];\n\n// TODO(v11): Remove the `_options` parameter (unused and only kept for back-compat with the previous signature)\nconst _lowQualityTransactionsFilterIntegration = ((_options?: NodeOptions) => ({\n name: 'LowQualityTransactionsFilter',\n beforeSetup(client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...LOW_QUALITY_TRANSACTIONS_FILTERS];\n },\n})) satisfies IntegrationFn;\n\n/**\n * Integration that filters out noisy http transactions such as requests to node_modules, favicon.ico, @id/, __manifest.\n * Adds entries to `ignoreSpans` so the filter applies in both static and streaming trace lifecycles.\n */\nexport const lowQualityTransactionsFilterIntegration = defineIntegration(_lowQualityTransactionsFilterIntegration);\n"],"names":[],"mappings":";;AAIA,MAAM,
|
|
1
|
+
{"version":3,"file":"lowQualityTransactionsFilterIntegration.js","sources":["../../../../src/server/integration/lowQualityTransactionsFilterIntegration.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport type { NodeOptions } from '@sentry/node';\n\nconst LOW_QUALITY_TRANSACTIONS_FILTERS = [\n /GET \\/node_modules\\//,\n /GET \\/favicon\\.ico/,\n /GET \\/@id\\//,\n // The span description for the `__manifest` endpoint is `GET *` (`http.route` resolves to `*`).\n // Filter by `http.target` instead, which carries the raw request path.\n { attributes: { 'http.target': /\\/__manifest/ } },\n];\n\n// TODO(v11): Remove the `_options` parameter (unused and only kept for back-compat with the previous signature)\nconst _lowQualityTransactionsFilterIntegration = ((_options?: NodeOptions) => ({\n name: 'LowQualityTransactionsFilter',\n beforeSetup(client) {\n const opts = client.getOptions();\n opts.ignoreSpans = [...(opts.ignoreSpans || []), ...LOW_QUALITY_TRANSACTIONS_FILTERS];\n },\n})) satisfies IntegrationFn;\n\n/**\n * Integration that filters out noisy http transactions such as requests to node_modules, favicon.ico, @id/, __manifest.\n * Adds entries to `ignoreSpans` so the filter applies in both static and streaming trace lifecycles.\n */\nexport const lowQualityTransactionsFilterIntegration = defineIntegration(_lowQualityTransactionsFilterIntegration);\n"],"names":[],"mappings":";;AAIA,MAAM,gCAAA,GAAmC;AAAA,EACvC,sBAAA;AAAA,EACA,oBAAA;AAAA,EACA,aAAA;AAAA;AAAA;AAAA,EAGA,EAAE,UAAA,EAAY,EAAE,aAAA,EAAe,gBAAe;AAChD,CAAA;AAGA,MAAM,wCAAA,IAA4C,CAAC,QAAA,MAA4B;AAAA,EAC7E,IAAA,EAAM,8BAAA;AAAA,EACN,YAAY,MAAA,EAAQ;AAClB,IAAA,MAAM,IAAA,GAAO,OAAO,UAAA,EAAW;AAC/B,IAAA,IAAA,CAAK,WAAA,GAAc,CAAC,GAAI,IAAA,CAAK,eAAe,EAAC,EAAI,GAAG,gCAAgC,CAAA;AAAA,EACtF;AACF,CAAA,CAAA,CAAA;AAMO,MAAM,uCAAA,GAA0C,kBAAkB,wCAAwC;;;;"}
|
|
@@ -5,79 +5,47 @@ import { ReactRouterInstrumentation } from '../instrumentation/reactRouter.js';
|
|
|
5
5
|
import { registerServerBuildGlobal } from '../serverBuild.js';
|
|
6
6
|
import { enableOtelDataLoaderSpanCreation } from '../serverGlobals.js';
|
|
7
7
|
|
|
8
|
-
const INTEGRATION_NAME =
|
|
9
|
-
|
|
8
|
+
const INTEGRATION_NAME = "ReactRouterServer";
|
|
10
9
|
const instrumentReactRouter = generateInstrumentOnce(INTEGRATION_NAME, () => {
|
|
11
10
|
return new ReactRouterInstrumentation();
|
|
12
11
|
});
|
|
13
|
-
|
|
14
12
|
const instrumentReactRouterServer = Object.assign(
|
|
15
13
|
() => {
|
|
16
14
|
instrumentReactRouter();
|
|
17
|
-
// Register global for Vite plugin ServerBuild capture
|
|
18
15
|
registerServerBuildGlobal();
|
|
19
16
|
},
|
|
20
|
-
{ id: INTEGRATION_NAME }
|
|
17
|
+
{ id: INTEGRATION_NAME }
|
|
21
18
|
);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Integration capturing tracing data for React Router server functions.
|
|
25
|
-
*/
|
|
26
19
|
const reactRouterServerIntegration = defineIntegration(() => {
|
|
27
20
|
return {
|
|
28
21
|
name: INTEGRATION_NAME,
|
|
29
22
|
setupOnce() {
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
(NODE_VERSION.major === 20 && NODE_VERSION.minor < 19) ||
|
|
33
|
-
(NODE_VERSION.major === 22 && NODE_VERSION.minor < 12)
|
|
34
|
-
) {
|
|
23
|
+
if (NODE_VERSION.major === 20 && NODE_VERSION.minor < 19 || NODE_VERSION.major === 22 && NODE_VERSION.minor < 12) {
|
|
35
24
|
enableOtelDataLoaderSpanCreation();
|
|
36
25
|
}
|
|
37
|
-
|
|
38
|
-
// Always install to capture ServerBuild for middleware names.
|
|
39
|
-
// Skips per-request wrapping when instrumentation API is active or OTEL span creation is disabled.
|
|
40
26
|
instrumentReactRouterServer();
|
|
41
27
|
},
|
|
42
28
|
processEvent(event) {
|
|
43
|
-
|
|
44
|
-
// we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point
|
|
45
|
-
if (
|
|
46
|
-
event.type === 'transaction' &&
|
|
47
|
-
event.contexts?.trace?.data &&
|
|
48
|
-
event.contexts.trace.data[ATTR_HTTP_ROUTE] === '*'
|
|
49
|
-
) {
|
|
29
|
+
if (event.type === "transaction" && event.contexts?.trace?.data && event.contexts.trace.data[ATTR_HTTP_ROUTE] === "*") {
|
|
50
30
|
const origin = event.contexts.trace.origin;
|
|
51
|
-
const isInstrumentationApiOrigin = origin?.includes(
|
|
52
|
-
|
|
53
|
-
// For instrumentation_api, always clean up bogus `*` route since we set better names
|
|
54
|
-
// For legacy, only clean up if the name has been adjusted (not METHOD *)
|
|
55
|
-
if (isInstrumentationApiOrigin || !event.transaction?.endsWith(' *')) {
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
31
|
+
const isInstrumentationApiOrigin = origin?.includes("instrumentation_api");
|
|
32
|
+
if (isInstrumentationApiOrigin || !event.transaction?.endsWith(" *")) {
|
|
57
33
|
delete event.contexts.trace.data[ATTR_HTTP_ROUTE];
|
|
58
34
|
}
|
|
59
35
|
}
|
|
60
|
-
|
|
61
36
|
return event;
|
|
62
37
|
},
|
|
63
38
|
processSegmentSpan(span) {
|
|
64
|
-
// Express generates bogus `*` routes for data loaders, which we want to remove here
|
|
65
|
-
// we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point
|
|
66
39
|
const attributes = span.attributes;
|
|
67
|
-
if (attributes?.[ATTR_HTTP_ROUTE] !==
|
|
40
|
+
if (attributes?.[ATTR_HTTP_ROUTE] !== "*") {
|
|
68
41
|
return;
|
|
69
42
|
}
|
|
70
|
-
|
|
71
43
|
const origin = attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN];
|
|
72
|
-
const isInstrumentationApiOrigin = typeof origin ===
|
|
73
|
-
|
|
74
|
-
// For instrumentation_api, always clean up bogus `*` route since we set better names
|
|
75
|
-
// For legacy, only clean up if the name has been adjusted (not METHOD *)
|
|
76
|
-
if (isInstrumentationApiOrigin || !span.name?.endsWith(' *')) {
|
|
77
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
44
|
+
const isInstrumentationApiOrigin = typeof origin === "string" && origin.includes("instrumentation_api");
|
|
45
|
+
if (isInstrumentationApiOrigin || !span.name?.endsWith(" *")) {
|
|
78
46
|
delete attributes[ATTR_HTTP_ROUTE];
|
|
79
47
|
}
|
|
80
|
-
}
|
|
48
|
+
}
|
|
81
49
|
};
|
|
82
50
|
});
|
|
83
51
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactRouterServer.js","sources":["../../../../src/server/integration/reactRouterServer.ts"],"sourcesContent":["import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\nimport { generateInstrumentOnce, NODE_VERSION } from '@sentry/node';\nimport { ReactRouterInstrumentation } from '../instrumentation/reactRouter';\nimport { registerServerBuildGlobal } from '../serverBuild';\nimport { enableOtelDataLoaderSpanCreation } from '../serverGlobals';\n\nconst INTEGRATION_NAME = 'ReactRouterServer';\n\nconst instrumentReactRouter = generateInstrumentOnce(INTEGRATION_NAME, () => {\n return new ReactRouterInstrumentation();\n});\n\nexport const instrumentReactRouterServer = Object.assign(\n (): void => {\n instrumentReactRouter();\n // Register global for Vite plugin ServerBuild capture\n registerServerBuildGlobal();\n },\n { id: INTEGRATION_NAME },\n);\n\n/**\n * Integration capturing tracing data for React Router server functions.\n */\nexport const reactRouterServerIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // Enable OTEL data-loader spans only on Node versions without the diagnostics_channel-based instrumentation API.\n if (\n (NODE_VERSION.major === 20 && NODE_VERSION.minor < 19) ||\n (NODE_VERSION.major === 22 && NODE_VERSION.minor < 12)\n ) {\n enableOtelDataLoaderSpanCreation();\n }\n\n // Always install to capture ServerBuild for middleware names.\n // Skips per-request wrapping when instrumentation API is active or OTEL span creation is disabled.\n instrumentReactRouterServer();\n },\n processEvent(event) {\n // Express generates bogus `*` routes for data loaders, which we want to remove here\n // we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point\n if (\n event.type === 'transaction' &&\n event.contexts?.trace?.data &&\n event.contexts.trace.data[ATTR_HTTP_ROUTE] === '*'\n ) {\n const origin = event.contexts.trace.origin;\n const isInstrumentationApiOrigin = origin?.includes('instrumentation_api');\n\n // For instrumentation_api, always clean up bogus `*` route since we set better names\n // For legacy, only clean up if the name has been adjusted (not METHOD *)\n if (isInstrumentationApiOrigin || !event.transaction?.endsWith(' *')) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete event.contexts.trace.data[ATTR_HTTP_ROUTE];\n }\n }\n\n return event;\n },\n processSegmentSpan(span) {\n // Express generates bogus `*` routes for data loaders, which we want to remove here\n // we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point\n const attributes = span.attributes;\n if (attributes?.[ATTR_HTTP_ROUTE] !== '*') {\n return;\n }\n\n const origin = attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN];\n const isInstrumentationApiOrigin = typeof origin === 'string' && origin.includes('instrumentation_api');\n\n // For instrumentation_api, always clean up bogus `*` route since we set better names\n // For legacy, only clean up if the name has been adjusted (not METHOD *)\n if (isInstrumentationApiOrigin || !span.name?.endsWith(' *')) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete attributes[ATTR_HTTP_ROUTE];\n }\n },\n };\n});\n"],"names":[],"mappings":";;;;;;;AAOA,MAAM,gBAAA,GAAmB,
|
|
1
|
+
{"version":3,"file":"reactRouterServer.js","sources":["../../../../src/server/integration/reactRouterServer.ts"],"sourcesContent":["import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\nimport { generateInstrumentOnce, NODE_VERSION } from '@sentry/node';\nimport { ReactRouterInstrumentation } from '../instrumentation/reactRouter';\nimport { registerServerBuildGlobal } from '../serverBuild';\nimport { enableOtelDataLoaderSpanCreation } from '../serverGlobals';\n\nconst INTEGRATION_NAME = 'ReactRouterServer';\n\nconst instrumentReactRouter = generateInstrumentOnce(INTEGRATION_NAME, () => {\n return new ReactRouterInstrumentation();\n});\n\nexport const instrumentReactRouterServer = Object.assign(\n (): void => {\n instrumentReactRouter();\n // Register global for Vite plugin ServerBuild capture\n registerServerBuildGlobal();\n },\n { id: INTEGRATION_NAME },\n);\n\n/**\n * Integration capturing tracing data for React Router server functions.\n */\nexport const reactRouterServerIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // Enable OTEL data-loader spans only on Node versions without the diagnostics_channel-based instrumentation API.\n if (\n (NODE_VERSION.major === 20 && NODE_VERSION.minor < 19) ||\n (NODE_VERSION.major === 22 && NODE_VERSION.minor < 12)\n ) {\n enableOtelDataLoaderSpanCreation();\n }\n\n // Always install to capture ServerBuild for middleware names.\n // Skips per-request wrapping when instrumentation API is active or OTEL span creation is disabled.\n instrumentReactRouterServer();\n },\n processEvent(event) {\n // Express generates bogus `*` routes for data loaders, which we want to remove here\n // we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point\n if (\n event.type === 'transaction' &&\n event.contexts?.trace?.data &&\n event.contexts.trace.data[ATTR_HTTP_ROUTE] === '*'\n ) {\n const origin = event.contexts.trace.origin;\n const isInstrumentationApiOrigin = origin?.includes('instrumentation_api');\n\n // For instrumentation_api, always clean up bogus `*` route since we set better names\n // For legacy, only clean up if the name has been adjusted (not METHOD *)\n if (isInstrumentationApiOrigin || !event.transaction?.endsWith(' *')) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete event.contexts.trace.data[ATTR_HTTP_ROUTE];\n }\n }\n\n return event;\n },\n processSegmentSpan(span) {\n // Express generates bogus `*` routes for data loaders, which we want to remove here\n // we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point\n const attributes = span.attributes;\n if (attributes?.[ATTR_HTTP_ROUTE] !== '*') {\n return;\n }\n\n const origin = attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN];\n const isInstrumentationApiOrigin = typeof origin === 'string' && origin.includes('instrumentation_api');\n\n // For instrumentation_api, always clean up bogus `*` route since we set better names\n // For legacy, only clean up if the name has been adjusted (not METHOD *)\n if (isInstrumentationApiOrigin || !span.name?.endsWith(' *')) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete attributes[ATTR_HTTP_ROUTE];\n }\n },\n };\n});\n"],"names":[],"mappings":";;;;;;;AAOA,MAAM,gBAAA,GAAmB,mBAAA;AAEzB,MAAM,qBAAA,GAAwB,sBAAA,CAAuB,gBAAA,EAAkB,MAAM;AAC3E,EAAA,OAAO,IAAI,0BAAA,EAA2B;AACxC,CAAC,CAAA;AAEM,MAAM,8BAA8B,MAAA,CAAO,MAAA;AAAA,EAChD,MAAY;AACV,IAAA,qBAAA,EAAsB;AAEtB,IAAA,yBAAA,EAA0B;AAAA,EAC5B,CAAA;AAAA,EACA,EAAE,IAAI,gBAAA;AACR;AAKO,MAAM,4BAAA,GAA+B,kBAAkB,MAAM;AAClE,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IACG,YAAA,CAAa,KAAA,KAAU,EAAA,IAAM,YAAA,CAAa,KAAA,GAAQ,EAAA,IAClD,YAAA,CAAa,KAAA,KAAU,EAAA,IAAM,YAAA,CAAa,KAAA,GAAQ,EAAA,EACnD;AACA,QAAA,gCAAA,EAAiC;AAAA,MACnC;AAIA,MAAA,2BAAA,EAA4B;AAAA,IAC9B,CAAA;AAAA,IACA,aAAa,KAAA,EAAO;AAGlB,MAAA,IACE,KAAA,CAAM,IAAA,KAAS,aAAA,IACf,KAAA,CAAM,QAAA,EAAU,KAAA,EAAO,IAAA,IACvB,KAAA,CAAM,QAAA,CAAS,KAAA,CAAM,IAAA,CAAK,eAAe,MAAM,GAAA,EAC/C;AACA,QAAA,MAAM,MAAA,GAAS,KAAA,CAAM,QAAA,CAAS,KAAA,CAAM,MAAA;AACpC,QAAA,MAAM,0BAAA,GAA6B,MAAA,EAAQ,QAAA,CAAS,qBAAqB,CAAA;AAIzE,QAAA,IAAI,8BAA8B,CAAC,KAAA,CAAM,WAAA,EAAa,QAAA,CAAS,IAAI,CAAA,EAAG;AAEpE,UAAA,OAAO,KAAA,CAAM,QAAA,CAAS,KAAA,CAAM,IAAA,CAAK,eAAe,CAAA;AAAA,QAClD;AAAA,MACF;AAEA,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA,IACA,mBAAmB,IAAA,EAAM;AAGvB,MAAA,MAAM,aAAa,IAAA,CAAK,UAAA;AACxB,MAAA,IAAI,UAAA,GAAa,eAAe,CAAA,KAAM,GAAA,EAAK;AACzC,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,MAAA,GAAS,WAAW,gCAAgC,CAAA;AAC1D,MAAA,MAAM,6BAA6B,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,SAAS,qBAAqB,CAAA;AAItG,MAAA,IAAI,8BAA8B,CAAC,IAAA,CAAK,IAAA,EAAM,QAAA,CAAS,IAAI,CAAA,EAAG;AAE5D,QAAA,OAAO,WAAW,eAAe,CAAA;AAAA,MACnC;AAAA,IACF;AAAA,GACF;AACF,CAAC;;;;"}
|