@@ -4,6 +4,7 @@ import type {
4
4
SpanAttributeValue ,
5
5
SpanAttributes ,
6
6
SpanContextData ,
7
+ SpanEnvelope ,
7
8
SpanJSON ,
8
9
SpanOrigin ,
9
10
SpanStatus ,
@@ -16,6 +17,7 @@ import { dropUndefinedKeys, logger, timestampInSeconds, uuid4 } from '@sentry/ut
16
17
import { getClient , getCurrentScope } from '../currentScopes' ;
17
18
import { DEBUG_BUILD } from '../debug-build' ;
18
19
20
+ import { createSpanEnvelope } from '../envelope' ;
19
21
import { getMetricSummaryJsonForSpan } from '../metrics/metric-summary' ;
20
22
import {
21
23
SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME ,
@@ -58,6 +60,12 @@ export class SentrySpan implements Span {
58
60
/** The timed events added to this span. */
59
61
protected _events : TimedEvent [ ] ;
60
62
63
+ /** if true, treat span as a standalone span (not part of a transaction) */
64
+ private _isStandaloneSpan ?: boolean ;
65
+
66
+ /** send standalone span as segment span */
67
+ private _isSegmentSpan ?: boolean ;
68
+
61
69
/**
62
70
* You should never call the constructor manually, always use `Sentry.startSpan()`
63
71
* or other span methods.
@@ -96,6 +104,9 @@ export class SentrySpan implements Span {
96
104
if ( this . _endTime ) {
97
105
this . _onSpanEnded ( ) ;
98
106
}
107
+
108
+ this . _isStandaloneSpan = spanContext . isStandalone ;
109
+ this . _isSegmentSpan = spanContext . isStandalone && spanContext . isSegment ;
99
110
}
100
111
101
112
/** @inheritdoc */
@@ -188,6 +199,8 @@ export class SentrySpan implements Span {
188
199
profile_id : this . _attributes [ SEMANTIC_ATTRIBUTE_PROFILE_ID ] as string | undefined ,
189
200
exclusive_time : this . _attributes [ SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME ] as number | undefined ,
190
201
measurements : timedEventsToMeasurements ( this . _events ) ,
202
+ is_segment : ! this . _isStandaloneSpan ? undefined : this . _isSegmentSpan ,
203
+ segment_id : this . _isStandaloneSpan && this . _isSegmentSpan ? this . _spanId : undefined ,
191
204
} ) ;
192
205
}
193
206
@@ -227,13 +240,20 @@ export class SentrySpan implements Span {
227
240
client . emit ( 'spanEnd' , this ) ;
228
241
}
229
242
230
- // If this is a root span, send it when it is endedf
231
- if ( this === getRootSpan ( this ) ) {
232
- const transactionEvent = this . _convertSpanToTransaction ( ) ;
233
- if ( transactionEvent ) {
234
- const scope = getCapturedScopesOnSpan ( this ) . scope || getCurrentScope ( ) ;
235
- scope . captureEvent ( transactionEvent ) ;
236
- }
243
+ // If this is not a root span, we're done, otherwise, we send it when it is ended
244
+ if ( this !== getRootSpan ( this ) ) {
245
+ return ;
246
+ }
247
+
248
+ if ( this . _isStandaloneSpan ) {
249
+ sendSpanEnvelope ( createSpanEnvelope ( [ this ] ) ) ;
250
+ return ;
251
+ }
252
+
253
+ const transactionEvent = this . _convertSpanToTransaction ( ) ;
254
+ if ( transactionEvent ) {
255
+ const scope = getCapturedScopesOnSpan ( this ) . scope || getCurrentScope ( ) ;
256
+ scope . captureEvent ( transactionEvent ) ;
237
257
}
238
258
}
239
259
@@ -318,3 +338,17 @@ function isSpanTimeInput(value: undefined | SpanAttributes | SpanTimeInput): val
318
338
function isFullFinishedSpan ( input : Partial < SpanJSON > ) : input is SpanJSON {
319
339
return ! ! input . start_timestamp && ! ! input . timestamp && ! ! input . span_id && ! ! input . trace_id ;
320
340
}
341
+
342
+ function sendSpanEnvelope ( envelope : SpanEnvelope ) : void {
343
+ const client = getClient ( ) ;
344
+ if ( ! client ) {
345
+ return ;
346
+ }
347
+
348
+ const transport = client . getTransport ( ) ;
349
+ if ( transport ) {
350
+ transport . send ( envelope ) . then ( null , reason => {
351
+ DEBUG_BUILD && logger . error ( 'Error while sending span:' , reason ) ;
352
+ } ) ;
353
+ }
354
+ }
0 commit comments