|
| 1 | +import { context } from '@opentelemetry/api'; |
| 2 | +import { RPCType, getRPCMetadata } from '@opentelemetry/core'; |
| 3 | +import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'; |
| 4 | +import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getActiveSpan, getRootSpan } from '@sentry/core'; |
| 5 | +import type { AppLoadContext, EntryContext } from 'react-router'; |
| 6 | + |
| 7 | +type OriginalHandleRequest = ( |
| 8 | + request: Request, |
| 9 | + responseStatusCode: number, |
| 10 | + responseHeaders: Headers, |
| 11 | + routerContext: EntryContext, |
| 12 | + loadContext: AppLoadContext, |
| 13 | +) => Promise<unknown>; |
| 14 | + |
| 15 | +/** |
| 16 | + * Wraps the original handleRequest function to add Sentry instrumentation. |
| 17 | + * |
| 18 | + * @param originalHandle - The original handleRequest function to wrap |
| 19 | + * @returns A wrapped version of the handle request function with Sentry instrumentation |
| 20 | + */ |
| 21 | +export function sentryHandleRequest(originalHandle: OriginalHandleRequest): OriginalHandleRequest { |
| 22 | + return async function sentryInstrumentedHandleRequest( |
| 23 | + request: Request, |
| 24 | + responseStatusCode: number, |
| 25 | + responseHeaders: Headers, |
| 26 | + routerContext: EntryContext, |
| 27 | + loadContext: AppLoadContext, |
| 28 | + ) { |
| 29 | + const parameterizedPath = |
| 30 | + routerContext?.staticHandlerContext?.matches?.[routerContext.staticHandlerContext.matches.length - 1]?.route.path; |
| 31 | + if (parameterizedPath) { |
| 32 | + const activeSpan = getActiveSpan(); |
| 33 | + if (activeSpan) { |
| 34 | + const rootSpan = getRootSpan(activeSpan); |
| 35 | + const routeName = `/${parameterizedPath}`; |
| 36 | + |
| 37 | + // The express instrumentation writes on the rpcMetadata and that ends up stomping on the `http.route` attribute. |
| 38 | + const rpcMetadata = getRPCMetadata(context.active()); |
| 39 | + if (rpcMetadata?.type === RPCType.HTTP) { |
| 40 | + rpcMetadata.route = routeName; |
| 41 | + } |
| 42 | + |
| 43 | + // The span exporter picks up the `http.route` (ATTR_HTTP_ROUTE) attribute to set the transaction name |
| 44 | + rootSpan.setAttributes({ |
| 45 | + [ATTR_HTTP_ROUTE]: routeName, |
| 46 | + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', |
| 47 | + }); |
| 48 | + } |
| 49 | + } |
| 50 | + return originalHandle(request, responseStatusCode, responseHeaders, routerContext, loadContext); |
| 51 | + }; |
| 52 | +} |
0 commit comments