@@ -113,37 +113,48 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
113
113
const previousActiveSpan = getActiveSpan ( ) ;
114
114
const span = _startIdleSpan ( startSpanOptions ) ;
115
115
116
- function _endSpan ( timestamp : number = timestampInSeconds ( ) ) : void {
117
- // Ensure we end with the last span timestamp, if possible
118
- const spans = getSpanDescendants ( span ) . filter ( child => child !== span ) ;
116
+ // We patch span.end to ensure we can run some things before the span is ended
117
+ // eslint-disable-next-line @typescript-eslint/unbound-method
118
+ span . end = new Proxy ( span . end , {
119
+ apply ( target , thisArg , args : Parameters < Span [ 'end' ] > ) {
120
+ if ( beforeSpanEnd ) {
121
+ beforeSpanEnd ( span ) ;
122
+ }
119
123
120
- // If we have no spans, we just end, nothing else to do here
121
- if ( ! spans . length ) {
122
- span . end ( timestamp ) ;
123
- return ;
124
- }
124
+ onIdleSpanEnded ( ) ;
125
125
126
- const childEndTimestamps = spans
127
- . map ( span => spanToJSON ( span ) . timestamp )
128
- . filter ( timestamp => ! ! timestamp ) as number [ ] ;
129
- const latestSpanEndTimestamp = childEndTimestamps . length ? Math . max ( ...childEndTimestamps ) : undefined ;
130
-
131
- const spanEndTimestamp = spanTimeInputToSeconds ( timestamp ) ;
132
- // In reality this should always exist here, but type-wise it may be undefined...
133
- const spanStartTimestamp = spanToJSON ( span ) . start_timestamp ;
134
-
135
- // The final endTimestamp should:
136
- // * Never be before the span start timestamp
137
- // * Be the latestSpanEndTimestamp, if there is one, and it is smaller than the passed span end timestamp
138
- // * Otherwise be the passed end timestamp
139
- // Final timestamp can never be after finalTimeout
140
- const endTimestamp = Math . min (
141
- spanStartTimestamp ? spanStartTimestamp + finalTimeout / 1000 : Infinity ,
142
- Math . max ( spanStartTimestamp || - Infinity , Math . min ( spanEndTimestamp , latestSpanEndTimestamp || Infinity ) ) ,
143
- ) ;
144
-
145
- span . end ( endTimestamp ) ;
146
- }
126
+ const timestamp = args [ 0 ] || timestampInSeconds ( ) ;
127
+
128
+ // Ensure we end with the last span timestamp, if possible
129
+ const spans = getSpanDescendants ( span ) . filter ( child => child !== span ) ;
130
+
131
+ // If we have no spans, we just end, nothing else to do here
132
+ if ( ! spans . length ) {
133
+ return Reflect . apply ( target , thisArg , args ) ;
134
+ }
135
+
136
+ const childEndTimestamps = spans
137
+ . map ( span => spanToJSON ( span ) . timestamp )
138
+ . filter ( timestamp => ! ! timestamp ) as number [ ] ;
139
+ const latestSpanEndTimestamp = childEndTimestamps . length ? Math . max ( ...childEndTimestamps ) : undefined ;
140
+
141
+ const spanEndTimestamp = spanTimeInputToSeconds ( timestamp ) ;
142
+ // In reality this should always exist here, but type-wise it may be undefined...
143
+ const spanStartTimestamp = spanToJSON ( span ) . start_timestamp ;
144
+
145
+ // The final endTimestamp should:
146
+ // * Never be before the span start timestamp
147
+ // * Be the latestSpanEndTimestamp, if there is one, and it is smaller than the passed span end timestamp
148
+ // * Otherwise be the passed end timestamp
149
+ // Final timestamp can never be after finalTimeout
150
+ const endTimestamp = Math . min (
151
+ spanStartTimestamp ? spanStartTimestamp + finalTimeout / 1000 : Infinity ,
152
+ Math . max ( spanStartTimestamp || - Infinity , Math . min ( spanEndTimestamp , latestSpanEndTimestamp || Infinity ) ) ,
153
+ ) ;
154
+
155
+ return Reflect . apply ( target , thisArg , [ endTimestamp ] ) ;
156
+ } ,
157
+ } ) ;
147
158
148
159
/**
149
160
* Cancels the existing idle timeout, if there is one.
@@ -173,7 +184,7 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
173
184
_idleTimeoutID = setTimeout ( ( ) => {
174
185
if ( ! _finished && activities . size === 0 && _autoFinishAllowed ) {
175
186
_finishReason = FINISH_REASON_IDLE_TIMEOUT ;
176
- _endSpan ( endTimestamp ) ;
187
+ span . end ( endTimestamp ) ;
177
188
}
178
189
} , idleTimeout ) ;
179
190
}
@@ -186,7 +197,7 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
186
197
_idleTimeoutID = setTimeout ( ( ) => {
187
198
if ( ! _finished && _autoFinishAllowed ) {
188
199
_finishReason = FINISH_REASON_HEARTBEAT_FAILED ;
189
- _endSpan ( endTimestamp ) ;
200
+ span . end ( endTimestamp ) ;
190
201
}
191
202
} , childSpanTimeout ) ;
192
203
}
@@ -227,10 +238,6 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
227
238
_finished = true ;
228
239
activities . clear ( ) ;
229
240
230
- if ( beforeSpanEnd ) {
231
- beforeSpanEnd ( span ) ;
232
- }
233
-
234
241
_setSpanForScope ( scope , previousActiveSpan ) ;
235
242
236
243
const spanJSON = spanToJSON ( span ) ;
@@ -312,10 +319,6 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
312
319
}
313
320
314
321
_popActivity ( endedSpan . spanContext ( ) . spanId ) ;
315
-
316
- if ( endedSpan === span ) {
317
- onIdleSpanEnded ( ) ;
318
- }
319
322
} ) ;
320
323
321
324
client . on ( 'idleSpanEnableAutoFinish' , spanToAllowAutoFinish => {
@@ -338,7 +341,7 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
338
341
if ( ! _finished ) {
339
342
span . setStatus ( { code : SPAN_STATUS_ERROR , message : 'deadline_exceeded' } ) ;
340
343
_finishReason = FINISH_REASON_FINAL_TIMEOUT ;
341
- _endSpan ( ) ;
344
+ span . end ( ) ;
342
345
}
343
346
} , finalTimeout ) ;
344
347
0 commit comments