@@ -5,7 +5,8 @@ import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@
55import * as domain from 'domain' ;
66import { IncomingMessage , ServerResponse } from 'http' ;
77
8- import { autoEndTransactionOnResponseEnd } from './utils/responseEnd' ;
8+ import { platformSupportsStreaming } from '../../utils/platformSupportsStreaming' ;
9+ import { autoEndTransactionOnResponseEnd , flushQueue } from './utils/responseEnd' ;
910
1011declare module 'http' {
1112 interface IncomingMessage {
@@ -77,43 +78,62 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
7778 return async function ( this : unknown , ...args : Parameters < F > ) : Promise < ReturnType < F > > {
7879 return domain . create ( ) . bind ( async ( ) => {
7980 let requestTransaction : Transaction | undefined = getTransactionFromRequest ( req ) ;
80-
81- if ( requestTransaction === undefined ) {
82- const sentryTraceHeader = req . headers [ 'sentry-trace' ] ;
83- const rawBaggageString = req . headers && req . headers . baggage ;
84- const traceparentData =
85- typeof sentryTraceHeader === 'string' ? extractTraceparentData ( sentryTraceHeader ) : undefined ;
86-
87- const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext ( rawBaggageString ) ;
88-
89- const newTransaction = startTransaction (
90- {
91- op : 'http.server' ,
92- name : options . requestedRouteName ,
93- ...traceparentData ,
94- status : 'ok' ,
95- metadata : {
96- source : 'route' ,
97- dynamicSamplingContext : traceparentData && ! dynamicSamplingContext ? { } : dynamicSamplingContext ,
81+ let dataFetcherSpan ;
82+
83+ const sentryTraceHeader = req . headers [ 'sentry-trace' ] ;
84+ const rawBaggageString = req . headers && req . headers . baggage ;
85+ const traceparentData =
86+ typeof sentryTraceHeader === 'string' ? extractTraceparentData ( sentryTraceHeader ) : undefined ;
87+
88+ const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext ( rawBaggageString ) ;
89+
90+ if ( platformSupportsStreaming ( ) ) {
91+ if ( requestTransaction === undefined ) {
92+ const newTransaction = startTransaction (
93+ {
94+ op : 'http.server' ,
95+ name : options . requestedRouteName ,
96+ ...traceparentData ,
97+ status : 'ok' ,
98+ metadata : {
99+ request : req ,
100+ source : 'route' ,
101+ dynamicSamplingContext : traceparentData && ! dynamicSamplingContext ? { } : dynamicSamplingContext ,
102+ } ,
98103 } ,
99- } ,
100- { request : req } ,
101- ) ;
104+ { request : req } ,
105+ ) ;
102106
103- requestTransaction = newTransaction ;
104- autoEndTransactionOnResponseEnd ( newTransaction , res ) ;
107+ requestTransaction = newTransaction ;
105108
106- // Link the transaction and the request together, so that when we would normally only have access to one, it's
107- // still possible to grab the other.
108- setTransactionOnRequest ( newTransaction , req ) ;
109- newTransaction . setMetadata ( { request : req } ) ;
110- }
109+ if ( platformSupportsStreaming ( ) ) {
110+ // On platforms that don't support streaming, doing things after res.end() is unreliable.
111+ autoEndTransactionOnResponseEnd ( newTransaction , res ) ;
112+ }
111113
112- const dataFetcherSpan = requestTransaction . startChild ( {
113- op : 'function.nextjs' ,
114- description : `${ options . dataFetchingMethodName } (${ options . dataFetcherRouteName } )` ,
115- status : 'ok' ,
116- } ) ;
114+ // Link the transaction and the request together, so that when we would normally only have access to one, it's
115+ // still possible to grab the other.
116+ setTransactionOnRequest ( newTransaction , req ) ;
117+ }
118+
119+ dataFetcherSpan = requestTransaction . startChild ( {
120+ op : 'function.nextjs' ,
121+ description : `${ options . dataFetchingMethodName } (${ options . dataFetcherRouteName } )` ,
122+ status : 'ok' ,
123+ } ) ;
124+ } else {
125+ dataFetcherSpan = startTransaction ( {
126+ op : 'function.nextjs' ,
127+ name : `${ options . dataFetchingMethodName } (${ options . dataFetcherRouteName } )` ,
128+ ...traceparentData ,
129+ status : 'ok' ,
130+ metadata : {
131+ request : req ,
132+ source : 'route' ,
133+ dynamicSamplingContext : traceparentData && ! dynamicSamplingContext ? { } : dynamicSamplingContext ,
134+ } ,
135+ } ) ;
136+ }
117137
118138 const currentScope = getCurrentHub ( ) . getScope ( ) ;
119139 if ( currentScope ) {
@@ -127,15 +147,13 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
127147 // Since we finish the span before the error can bubble up and trigger the handlers in `registerErrorInstrumentation`
128148 // that set the transaction status, we need to manually set the status of the span & transaction
129149 dataFetcherSpan . setStatus ( 'internal_error' ) ;
130-
131- const transaction = dataFetcherSpan . transaction ;
132- if ( transaction ) {
133- transaction . setStatus ( 'internal_error' ) ;
134- }
135-
150+ requestTransaction ?. setStatus ( 'internal_error' ) ;
136151 throw e ;
137152 } finally {
138153 dataFetcherSpan . finish ( ) ;
154+ if ( ! platformSupportsStreaming ( ) ) {
155+ await flushQueue ( ) ;
156+ }
139157 }
140158 } ) ( ) ;
141159 } ;
0 commit comments