1- import { captureException , flush , Handlers , withScope } from '@sentry/node' ;
2- import { addExceptionMechanism } from '@sentry/utils' ;
1+ import { captureException , flush , getCurrentHub , Handlers , startTransaction , withScope } from '@sentry/node' ;
2+ import { extractTraceparentData , getActiveTransaction , hasTracingEnabled } from '@sentry/tracing' ;
3+ import { addExceptionMechanism , isString , logger , stripUrlQueryAndFragment } from '@sentry/utils' ;
34import { NextApiHandler } from 'next' ;
45
6+ import { addRequestDataToEvent , NextRequest } from './instrumentServer' ;
7+
58const { parseRequest } = Handlers ;
69
710// purely for clarity
@@ -12,9 +15,43 @@ export const withSentry = (handler: NextApiHandler): WrappedNextApiHandler => {
1215 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
1316 return async ( req , res ) => {
1417 try {
15- // TODO: Start Transaction
18+ const currentScope = getCurrentHub ( ) . getScope ( ) ;
19+
20+ if ( currentScope ) {
21+ currentScope . addEventProcessor ( event => addRequestDataToEvent ( event , req as NextRequest ) ) ;
22+
23+ // We only want to record page and API requests
24+ if ( hasTracingEnabled ( ) ) {
25+ // If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision)
26+ let traceparentData ;
27+ if ( req . headers && isString ( req . headers [ 'sentry-trace' ] ) ) {
28+ traceparentData = extractTraceparentData ( req . headers [ 'sentry-trace' ] as string ) ;
29+ logger . log ( `[Tracing] Continuing trace ${ traceparentData ?. traceId } .` ) ;
30+ }
31+
32+ const url = `${ req . url } ` ;
33+ // pull off query string, if any
34+ const reqPath = stripUrlQueryAndFragment ( url ) ;
35+
36+ // requests for pages will only ever be GET requests, so don't bother to include the method in the transaction
37+ // name; requests to API routes could be GET, POST, PUT, etc, so do include it there
38+ const namePrefix = `${ ( req . method || 'GET' ) . toUpperCase ( ) } ` ;
39+
40+ const transaction = startTransaction (
41+ {
42+ name : `${ namePrefix } ${ reqPath } ` ,
43+ op : 'http.server' ,
44+ metadata : { requestPath : reqPath } ,
45+ ...traceparentData ,
46+ } ,
47+ // extra context passed to the `tracesSampler`
48+ { request : req } ,
49+ ) ;
50+ currentScope . setSpan ( transaction ) ;
51+ }
52+ }
53+
1654 return await handler ( req , res ) ; // Call Handler
17- // TODO: Finish Transaction
1855 } catch ( e ) {
1956 withScope ( scope => {
2057 scope . addEventProcessor ( event => {
@@ -27,6 +64,17 @@ export const withSentry = (handler: NextApiHandler): WrappedNextApiHandler => {
2764 } ) ;
2865 throw e ;
2966 } finally {
67+ const transaction = getActiveTransaction ( ) ;
68+ if ( transaction ) {
69+ transaction . setHttpStatus ( res . statusCode ) ;
70+
71+ // we'll collect this data in a more targeted way in the event processor we added above,
72+ // `addRequestDataToEvent`
73+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
74+ delete transaction . metadata . requestPath ;
75+
76+ transaction . finish ( ) ;
77+ }
3078 await flush ( 2000 ) ;
3179 }
3280 } ;
0 commit comments