11/* eslint-disable max-lines */
22import {
3+ SEMANTIC_ATTRIBUTE_SENTRY_OP ,
34 SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ,
45 SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ,
6+ continueTrace ,
57 getActiveSpan ,
68 getActiveTransaction ,
79 getClient ,
8- getCurrentScope ,
910 getDynamicSamplingContextFromSpan ,
11+ getRootSpan ,
12+ handleCallbackErrors ,
1013 hasTracingEnabled ,
1114 setHttpStatus ,
1215 spanToJSON ,
1316 spanToTraceHeader ,
17+ startSpan ,
1418 withIsolationScope ,
1519} from '@sentry/core' ;
16- import { captureException , getCurrentHub } from '@sentry/node-experimental' ;
17- import type { Hub , Transaction , TransactionSource , WrappedFunction } from '@sentry/types' ;
20+ import { captureException } from '@sentry/node-experimental' ;
21+ import type { Span , TransactionSource , WrappedFunction } from '@sentry/types' ;
1822import {
1923 addExceptionMechanism ,
2024 dynamicSamplingContextToSentryBaggageHeader ,
@@ -24,7 +28,6 @@ import {
2428 loadModule ,
2529 logger ,
2630 objectify ,
27- tracingContextFromHeaders ,
2831} from '@sentry/utils' ;
2932
3033import { DEBUG_BUILD } from './debug-build' ;
@@ -186,46 +189,50 @@ function makeWrappedDocumentRequestFunction(remixVersion?: number) {
186189 context : EntryContext ,
187190 loadContext ?: Record < string , unknown > ,
188191 ) : Promise < Response > {
189- let res : Response ;
190- // eslint-disable-next-line deprecation/deprecation
191- const activeTransaction = getActiveTransaction ( ) ;
192+ const activeSpan = getActiveSpan ( ) ;
193+ const rootSpan = activeSpan && getRootSpan ( activeSpan ) ;
192194
193- try {
194- // eslint-disable-next-line deprecation/deprecation
195- const span = activeTransaction ?. startChild ( {
196- op : 'function.remix.document_request' ,
197- origin : 'auto.function.remix' ,
198- name : spanToJSON ( activeTransaction ) . description ,
195+ const name = rootSpan ? spanToJSON ( rootSpan ) . description : undefined ;
196+
197+ return startSpan (
198+ {
199+ // If we don't have a root span, `onlyIfParent` will lead to the span not being created anyhow
200+ // So we don't need to care too much about the fallback name, it's just for typing purposes....
201+ name : name || '<unknown>' ,
202+ onlyIfParent : true ,
199203 attributes : {
200204 method : request . method ,
201205 url : request . url ,
206+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.function.remix' ,
207+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : 'function.remix.document_request' ,
202208 } ,
203- } ) ;
204-
205- res = await origDocumentRequestFunction . call (
206- this ,
207- request ,
208- responseStatusCode ,
209- responseHeaders ,
210- context ,
211- loadContext ,
212- ) ;
213-
214- span ?. end ( ) ;
215- } catch ( err ) {
216- const isRemixV1 = ! FUTURE_FLAGS ?. v2_errorBoundary && remixVersion !== 2 ;
217-
218- // This exists to capture the server-side rendering errors on Remix v1
219- // On Remix v2, we capture SSR errors at `handleError`
220- // We also skip primitives here, as we can't dedupe them, and also we don't expect any primitive SSR errors.
221- if ( isRemixV1 && ! isPrimitive ( err ) ) {
222- await captureRemixServerException ( err , 'documentRequest' , request ) ;
223- }
224-
225- throw err ;
226- }
227-
228- return res ;
209+ } ,
210+ ( ) => {
211+ return handleCallbackErrors (
212+ ( ) => {
213+ return origDocumentRequestFunction . call (
214+ this ,
215+ request ,
216+ responseStatusCode ,
217+ responseHeaders ,
218+ context ,
219+ loadContext ,
220+ ) ;
221+ } ,
222+ err => {
223+ const isRemixV1 = ! FUTURE_FLAGS ?. v2_errorBoundary && remixVersion !== 2 ;
224+
225+ // This exists to capture the server-side rendering errors on Remix v1
226+ // On Remix v2, we capture SSR errors at `handleError`
227+ // We also skip primitives here, as we can't dedupe them, and also we don't expect any primitive SSR errors.
228+ if ( isRemixV1 && ! isPrimitive ( err ) ) {
229+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
230+ captureRemixServerException ( err , 'documentRequest' , request ) ;
231+ }
232+ } ,
233+ ) ;
234+ } ,
235+ ) ;
229236 } ;
230237 } ;
231238}
@@ -242,47 +249,32 @@ function makeWrappedDataFunction(
242249 return origFn . call ( this , args ) ;
243250 }
244251
245- let res : Response | AppData ;
246- // eslint-disable-next-line deprecation/deprecation
247- const activeTransaction = getActiveTransaction ( ) ;
248- const currentScope = getCurrentScope ( ) ;
249-
250- try {
251- // eslint-disable-next-line deprecation/deprecation
252- const span = activeTransaction ?. startChild ( {
252+ return startSpan (
253+ {
253254 op : `function.remix.${ name } ` ,
254255 origin : 'auto.ui.remix' ,
255256 name : id ,
256257 attributes : {
257258 name,
258259 } ,
259- } ) ;
260-
261- if ( span ) {
262- // Assign data function to hub to be able to see `db` transactions (if any) as children.
263- // eslint-disable-next-line deprecation/deprecation
264- currentScope . setSpan ( span ) ;
265- }
266-
267- res = await origFn . call ( this , args ) ;
268-
269- // eslint-disable-next-line deprecation/deprecation
270- currentScope . setSpan ( activeTransaction ) ;
271- span ?. end ( ) ;
272- } catch ( err ) {
273- const isRemixV2 = FUTURE_FLAGS ?. v2_errorBoundary || remixVersion === 2 ;
274-
275- // On Remix v2, we capture all unexpected errors (except the `Route Error Response`s / Thrown Responses) in `handleError` function.
276- // This is both for consistency and also avoid duplicates such as primitives like `string` or `number` being captured twice.
277- // Remix v1 does not have a `handleError` function, so we capture all errors here.
278- if ( isRemixV2 ? isResponse ( err ) : true ) {
279- await captureRemixServerException ( err , name , args . request ) ;
280- }
281-
282- throw err ;
283- }
284-
285- return res ;
260+ } ,
261+ ( ) => {
262+ return handleCallbackErrors (
263+ ( ) => origFn . call ( this , args ) ,
264+ err => {
265+ const isRemixV2 = FUTURE_FLAGS ?. v2_errorBoundary || remixVersion === 2 ;
266+
267+ // On Remix v2, we capture all unexpected errors (except the `Route Error Response`s / Thrown Responses) in `handleError` function.
268+ // This is both for consistency and also avoid duplicates such as primitives like `string` or `number` being captured twice.
269+ // Remix v1 does not have a `handleError` function, so we capture all errors here.
270+ if ( isRemixV2 ? isResponse ( err ) : true ) {
271+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
272+ captureRemixServerException ( err , name , args . request ) ;
273+ }
274+ } ,
275+ ) ;
276+ } ,
277+ ) ;
286278 } ;
287279}
288280
@@ -382,47 +374,44 @@ export function createRoutes(manifest: ServerRouteManifest, parentId?: string):
382374}
383375
384376/**
385- * Starts a new transaction for the given request to be used by different `RequestHandler` wrappers.
377+ * Starts a new active span for the given request to be used by different `RequestHandler` wrappers.
386378 */
387- export function startRequestHandlerTransaction (
388- hub : Hub ,
389- name : string ,
390- source : TransactionSource ,
391- request : {
392- headers : {
393- 'sentry-trace' : string ;
394- baggage : string ;
395- } ;
379+ export function startRequestHandlerSpan < T > (
380+ {
381+ name,
382+ source,
383+ sentryTrace,
384+ baggage,
385+ method,
386+ } : {
387+ name : string ;
388+ source : TransactionSource ;
389+ sentryTrace : string ;
390+ baggage : string ;
396391 method : string ;
397392 } ,
398- ) : Transaction {
399- // eslint-disable-next-line deprecation/deprecation
400- const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders (
401- request . headers [ 'sentry-trace' ] ,
402- request . headers . baggage ,
403- ) ;
404- // eslint-disable-next-line deprecation/deprecation
405- hub . getScope ( ) . setPropagationContext ( propagationContext ) ;
406-
407- // TODO: Refactor this to `startSpan()`
408- // eslint-disable-next-line deprecation/deprecation
409- const transaction = hub . startTransaction ( {
410- name,
411- op : 'http.server' ,
412- attributes : {
413- [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.http.remix' ,
414- [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : source ,
415- method : request . method ,
393+ callback : ( span : Span ) => T ,
394+ ) : T {
395+ return continueTrace (
396+ {
397+ sentryTrace,
398+ baggage,
416399 } ,
417- ...traceparentData ,
418- metadata : {
419- dynamicSamplingContext : traceparentData && ! dynamicSamplingContext ? { } : dynamicSamplingContext ,
400+ ( ) => {
401+ return startSpan (
402+ {
403+ name,
404+ attributes : {
405+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.http.remix' ,
406+ [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : source ,
407+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : 'http.server' ,
408+ method,
409+ } ,
410+ } ,
411+ callback ,
412+ ) ;
420413 } ,
421- } ) ;
422-
423- // eslint-disable-next-line deprecation/deprecation
424- hub . getScope ( ) . setSpan ( transaction ) ;
425- return transaction ;
414+ ) ;
426415}
427416
428417/**
@@ -445,8 +434,6 @@ function wrapRequestHandler(origRequestHandler: RequestHandler, build: ServerBui
445434 }
446435
447436 return withIsolationScope ( async isolationScope => {
448- // eslint-disable-next-line deprecation/deprecation
449- const hub = getCurrentHub ( ) ;
450437 const options = getClient ( ) ?. getOptions ( ) ;
451438
452439 let normalizedRequest : Record < string , unknown > = request ;
@@ -473,23 +460,24 @@ function wrapRequestHandler(origRequestHandler: RequestHandler, build: ServerBui
473460 return origRequestHandler . call ( this , request , loadContext ) ;
474461 }
475462
476- const transaction = startRequestHandlerTransaction ( hub , name , source , {
477- headers : {
478- 'sentry-trace' : request . headers . get ( 'sentry-trace' ) || '' ,
463+ return startRequestHandlerSpan (
464+ {
465+ name,
466+ source,
467+ sentryTrace : request . headers . get ( 'sentry-trace' ) || '' ,
479468 baggage : request . headers . get ( 'baggage' ) || '' ,
469+ method : request . method ,
480470 } ,
481- method : request . method ,
482- } ) ;
483-
484- const res = ( await origRequestHandler . call ( this , request , loadContext ) ) as Response ;
471+ async span => {
472+ const res = ( await origRequestHandler . call ( this , request , loadContext ) ) as Response ;
485473
486- if ( isResponse ( res ) ) {
487- setHttpStatus ( transaction , res . status ) ;
488- }
489-
490- transaction . end ( ) ;
474+ if ( isResponse ( res ) ) {
475+ setHttpStatus ( span , res . status ) ;
476+ }
491477
492- return res ;
478+ return res ;
479+ } ,
480+ ) ;
493481 } ) ;
494482 } ;
495483}
0 commit comments