1
- import type { Attributes , Context , Span } from '@opentelemetry/api' ;
1
+ import type { Attributes , Context , Span , TraceState as TraceStateInterface } from '@opentelemetry/api' ;
2
2
import { SpanKind } from '@opentelemetry/api' ;
3
3
import { isSpanContextValid , trace } from '@opentelemetry/api' ;
4
4
import { TraceState } from '@opentelemetry/core' ;
@@ -40,16 +40,8 @@ export class SentrySampler implements Sampler {
40
40
const parentSpan = trace . getSpan ( context ) ;
41
41
const parentContext = parentSpan ?. spanContext ( ) ;
42
42
43
- let traceState = parentContext ?. traceState || new TraceState ( ) ;
44
-
45
- // We always keep the URL on the trace state, so we can access it in the propagator
46
- const url = spanAttributes [ SEMATTRS_HTTP_URL ] ;
47
- if ( url && typeof url === 'string' ) {
48
- traceState = traceState . set ( SENTRY_TRACE_STATE_URL , url ) ;
49
- }
50
-
51
43
if ( ! hasTracingEnabled ( options ) ) {
52
- return { decision : SamplingDecision . NOT_RECORD , traceState } ;
44
+ return sentrySamplerNoDecision ( { context , spanAttributes } ) ;
53
45
}
54
46
55
47
// If we have a http.client span that has no local parent, we never want to sample it
@@ -59,7 +51,7 @@ export class SentrySampler implements Sampler {
59
51
spanAttributes [ SEMATTRS_HTTP_METHOD ] &&
60
52
( ! parentSpan || parentContext ?. isRemote )
61
53
) {
62
- return { decision : SamplingDecision . NOT_RECORD , traceState } ;
54
+ return sentrySamplerNoDecision ( { context , spanAttributes } ) ;
63
55
}
64
56
65
57
const parentSampled = parentSpan ? getParentSampled ( parentSpan , traceId , spanName ) : undefined ;
@@ -76,7 +68,7 @@ export class SentrySampler implements Sampler {
76
68
mutableSamplingDecision ,
77
69
) ;
78
70
if ( ! mutableSamplingDecision . decision ) {
79
- return { decision : SamplingDecision . NOT_RECORD , traceState : traceState } ;
71
+ return sentrySamplerNoDecision ( { context , spanAttributes } ) ;
80
72
}
81
73
82
74
const [ sampled , sampleRate ] = sampleSpan ( options , {
@@ -96,25 +88,22 @@ export class SentrySampler implements Sampler {
96
88
const method = `${ spanAttributes [ SEMATTRS_HTTP_METHOD ] } ` . toUpperCase ( ) ;
97
89
if ( method === 'OPTIONS' || method === 'HEAD' ) {
98
90
DEBUG_BUILD && logger . log ( `[Tracing] Not sampling span because HTTP method is '${ method } ' for ${ spanName } ` ) ;
91
+
99
92
return {
100
- decision : SamplingDecision . NOT_RECORD ,
93
+ ... sentrySamplerNotSampled ( { context , spanAttributes } ) ,
101
94
attributes,
102
- traceState : traceState . set ( SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING , '1' ) ,
103
95
} ;
104
96
}
105
97
106
98
if ( ! sampled ) {
107
99
return {
108
- decision : SamplingDecision . NOT_RECORD ,
100
+ ... sentrySamplerNotSampled ( { context , spanAttributes } ) ,
109
101
attributes,
110
- traceState : traceState . set ( SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING , '1' ) ,
111
102
} ;
112
103
}
113
-
114
104
return {
115
- decision : SamplingDecision . RECORD_AND_SAMPLED ,
105
+ ... sentrySamplerSampled ( { context , spanAttributes } ) ,
116
106
attributes,
117
- traceState,
118
107
} ;
119
108
}
120
109
@@ -152,3 +141,55 @@ function getParentSampled(parentSpan: Span, traceId: string, spanName: string):
152
141
153
142
return undefined ;
154
143
}
144
+
145
+ /**
146
+ * Returns a SamplingResult that indicates that a span was not sampled, but no definite decision was made yet.
147
+ * This indicates to downstream SDKs that they may make their own decision.
148
+ */
149
+ export function sentrySamplerNoDecision ( {
150
+ context,
151
+ spanAttributes,
152
+ } : { context : Context ; spanAttributes : SpanAttributes } ) : SamplingResult {
153
+ const traceState = getBaseTraceState ( context , spanAttributes ) ;
154
+
155
+ return { decision : SamplingDecision . NOT_RECORD , traceState } ;
156
+ }
157
+
158
+ /**
159
+ * Returns a SamplingResult that indicates that a span was not sampled.
160
+ */
161
+ export function sentrySamplerNotSampled ( {
162
+ context,
163
+ spanAttributes,
164
+ } : { context : Context ; spanAttributes : SpanAttributes } ) : SamplingResult {
165
+ const traceState = getBaseTraceState ( context , spanAttributes ) . set ( SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING , '1' ) ;
166
+
167
+ return { decision : SamplingDecision . NOT_RECORD , traceState } ;
168
+ }
169
+
170
+ /**
171
+ * Returns a SamplingResult that indicates that a span was sampled.
172
+ */
173
+ export function sentrySamplerSampled ( {
174
+ context,
175
+ spanAttributes,
176
+ } : { context : Context ; spanAttributes : SpanAttributes } ) : SamplingResult {
177
+ const traceState = getBaseTraceState ( context , spanAttributes ) ;
178
+
179
+ return { decision : SamplingDecision . RECORD_AND_SAMPLED , traceState } ;
180
+ }
181
+
182
+ function getBaseTraceState ( context : Context , spanAttributes : SpanAttributes ) : TraceStateInterface {
183
+ const parentSpan = trace . getSpan ( context ) ;
184
+ const parentContext = parentSpan ?. spanContext ( ) ;
185
+
186
+ let traceState = parentContext ?. traceState || new TraceState ( ) ;
187
+
188
+ // We always keep the URL on the trace state, so we can access it in the propagator
189
+ const url = spanAttributes [ SEMATTRS_HTTP_URL ] ;
190
+ if ( url && typeof url === 'string' ) {
191
+ traceState = traceState . set ( SENTRY_TRACE_STATE_URL , url ) ;
192
+ }
193
+
194
+ return traceState ;
195
+ }
0 commit comments