-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathdynamicSamplingContext.ts
125 lines (101 loc) · 4.04 KB
/
dynamicSamplingContext.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import type { Client, DynamicSamplingContext, Span } from '@sentry/types';
import {
addNonEnumerableProperty,
baggageHeaderToDynamicSamplingContext,
dropUndefinedKeys,
dynamicSamplingContextToSentryBaggageHeader,
} from '@sentry/utils';
import { DEFAULT_ENVIRONMENT } from '../constants';
import { getClient } from '../currentScopes';
import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '../semanticAttributes';
import { hasTracingEnabled } from '../utils/hasTracingEnabled';
import { getRootSpan, spanIsSampled, spanToJSON } from '../utils/spanUtils';
/**
* If you change this value, also update the terser plugin config to
* avoid minification of the object property!
*/
const FROZEN_DSC_FIELD = '_frozenDsc';
type SpanWithMaybeDsc = Span & {
[FROZEN_DSC_FIELD]?: Partial<DynamicSamplingContext> | undefined;
};
/**
* Freeze the given DSC on the given span.
*/
export function freezeDscOnSpan(span: Span, dsc: Partial<DynamicSamplingContext>): void {
const spanWithMaybeDsc = span as SpanWithMaybeDsc;
addNonEnumerableProperty(spanWithMaybeDsc, FROZEN_DSC_FIELD, dsc);
}
/**
* Creates a dynamic sampling context from a client.
*
* Dispatches the `createDsc` lifecycle hook as a side effect.
*/
export function getDynamicSamplingContextFromClient(trace_id: string, client: Client): DynamicSamplingContext {
const options = client.getOptions();
const { publicKey: public_key } = client.getDsn() || {};
const dsc = dropUndefinedKeys({
environment: options.environment || DEFAULT_ENVIRONMENT,
release: options.release,
public_key,
trace_id,
}) as DynamicSamplingContext;
client.emit('createDsc', dsc);
return dsc;
}
/**
* Creates a dynamic sampling context from a span (and client and scope)
*
* @param span the span from which a few values like the root span name and sample rate are extracted.
*
* @returns a dynamic sampling context
*/
export function getDynamicSamplingContextFromSpan(span: Span): Readonly<Partial<DynamicSamplingContext>> {
const client = getClient();
if (!client) {
return {};
}
const dsc = getDynamicSamplingContextFromClient(spanToJSON(span).trace_id || '', client);
const rootSpan = getRootSpan(span);
// For core implementation, we freeze the DSC onto the span as a non-enumerable property
const frozenDsc = (rootSpan as SpanWithMaybeDsc)[FROZEN_DSC_FIELD];
if (frozenDsc) {
return frozenDsc;
}
// For OpenTelemetry, we freeze the DSC on the trace state
const traceState = rootSpan.spanContext().traceState;
const traceStateDsc = traceState && traceState.get('sentry.dsc');
// If the span has a DSC, we want it to take precedence
const dscOnTraceState = traceStateDsc && baggageHeaderToDynamicSamplingContext(traceStateDsc);
if (dscOnTraceState) {
return dscOnTraceState;
}
// Else, we generate it from the span
const jsonSpan = spanToJSON(rootSpan);
const attributes = jsonSpan.data || {};
const maybeSampleRate = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
if (maybeSampleRate != null) {
dsc.sample_rate = `${maybeSampleRate}`;
}
// We don't want to have a transaction name in the DSC if the source is "url" because URLs might contain PII
const source = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
// after JSON conversion, txn.name becomes jsonSpan.description
const name = jsonSpan.description;
if (source !== 'url' && name) {
dsc.transaction = name;
}
// How can we even land here with hasTracingEnabled() returning false?
// Otel creates a Non-recording span in Tracing Without Performance mode when handling incoming requests
// So we end up with an active span that is not sampled (neither positively nor negatively)
if (hasTracingEnabled()) {
dsc.sampled = String(spanIsSampled(rootSpan));
}
client.emit('createDsc', dsc, rootSpan);
return dsc;
}
/**
* Convert a Span to a baggage header.
*/
export function spanToBaggageHeader(span: Span): string | undefined {
const dsc = getDynamicSamplingContextFromSpan(span);
return dynamicSamplingContextToSentryBaggageHeader(dsc);
}