11import { getMainCarrier , Hub } from '@sentry/hub' ;
22import { SpanContext } from '@sentry/types' ;
3- import { isInstanceOf } from '@sentry/utils' ;
43
54import { Span } from './span' ;
65
7- /**
8- * Checks whether given value is instance of Span
9- * @param span value to check
10- */
11- function isSpanInstance ( span : unknown ) : span is Span {
12- return isInstanceOf ( span , Span ) ;
13- }
14-
156/** Returns all trace headers that are currently on the top scope. */
167function traceHeaders ( ) : { [ key : string ] : string } {
178 // @ts -ignore
@@ -33,36 +24,42 @@ function traceHeaders(): { [key: string]: string } {
3324 * and attach a `SpanRecorder`. If it's of type `SpanContext` and there is already a `Span` on the Scope,
3425 * the created Span will have a reference to it and become it's child. Otherwise it'll crete a new `Span`.
3526 *
36- * @param span Already constructed span which should be started or properties with which the span should be created
27+ * @param spanContext Already constructed span or properties with which the span should be created
3728 */
38- function startSpan ( spanOrSpanContext ?: Span | SpanContext , forceNoChild : boolean = false ) : Span {
29+ function startSpan ( spanContext ?: SpanContext ) : Span {
3930 // @ts -ignore
40- const that = this as Hub ;
41- const scope = that . getScope ( ) ;
42- const client = that . getClient ( ) ;
31+ const hub = this as Hub ;
32+ const scope = hub . getScope ( ) ;
33+ const client = hub . getClient ( ) ;
4334 let span ;
4435
45- if ( ! isSpanInstance ( spanOrSpanContext ) && ! forceNoChild ) {
46- if ( scope ) {
47- const parentSpan = scope . getSpan ( ) as Span ;
48- if ( parentSpan ) {
49- span = parentSpan . child ( spanOrSpanContext ) ;
50- }
36+ // This flag determines if we already added the span as a child to the span that currently lives on the scope
37+ // If we do not have this, we will add it later on twice to the span recorder and therefore have too many spans
38+ let addedAsChild = false ;
39+
40+ if ( scope ) {
41+ const parentSpan = scope . getSpan ( ) as Span ;
42+ if ( parentSpan ) {
43+ span = parentSpan . child ( spanContext ) ;
44+ addedAsChild = true ;
5145 }
5246 }
5347
54- if ( ! isSpanInstance ( span ) ) {
55- span = new Span ( spanOrSpanContext , that ) ;
48+ if ( ! span ) {
49+ span = new Span ( spanContext , hub ) ;
5650 }
5751
58- if ( span . sampled === undefined && span . transaction !== undefined ) {
52+ // We only roll the dice on sampling for "root" spans (transactions) because the childs inherit this state
53+ if ( span . sampled === undefined && span . isRootSpan ( ) ) {
5954 const sampleRate = ( client && client . getOptions ( ) . tracesSampleRate ) || 0 ;
6055 span . sampled = Math . random ( ) < sampleRate ;
6156 }
6257
63- if ( span . sampled ) {
58+ // We only want to create a span list if we sampled the transaction
59+ // in case we will discard the span anyway because sampled == false, we safe memory and do not store child spans
60+ if ( span . sampled && ! addedAsChild ) {
6461 const experimentsOptions = ( client && client . getOptions ( ) . _experiments ) || { } ;
65- span . initFinishedSpans ( experimentsOptions . maxSpans as number ) ;
62+ span . initSpanRecorder ( experimentsOptions . maxSpans as number ) ;
6663 }
6764
6865 return span ;
0 commit comments