Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : import 'dart:math' as math;
5 :
6 : import '../../../dartastic_opentelemetry.dart';
7 :
8 : /// A sampler that samples traces based on a probability defined by the ratio of
9 : /// traces that should be sampled. The ratio must be in the range [0.0, 1.0].
10 : ///
11 : /// Uses the lowest 8 bytes of the trace ID to make a sampling decision.
12 : class TraceIdRatioSampler implements Sampler {
13 : /// The sampling ratio, a value between 0.0 and 1.0 that determines
14 : /// the probability that a trace will be sampled.
15 : final double ratio;
16 :
17 3 : @override
18 6 : String get description => 'TraceIdRatioSampler{$ratio}';
19 :
20 : /// Creates a TraceIdRatioSampler with the given ratio.
21 : /// [ratio] must be in the range [0.0, 1.0].
22 4 : TraceIdRatioSampler(this.ratio) {
23 16 : if (ratio < 0.0 || ratio > 1.0) {
24 1 : throw ArgumentError('ratio must be in range [0.0, 1.0]');
25 : }
26 : }
27 :
28 : /// Converts a trace ID to a value between 0.0 and 1.0 for sampling decisions.
29 : /// Uses the lowest 8 bytes (16 hex characters) of the trace ID as per the specification.
30 : ///
31 : /// @param traceId The trace ID as a hex string
32 : /// @return A value between 0.0 and 1.0 based on the trace ID
33 3 : double _traceIdToValue(String traceId) {
34 : // Get the last 16 hex chars (8 bytes) of trace ID
35 12 : final lastBytes = traceId.substring(math.max(0, traceId.length - 16));
36 :
37 : // Parse hex string to integer with BigInt to avoid precision issues
38 3 : final value = BigInt.parse(lastBytes, radix: 16);
39 :
40 : // Maximum possible value for 8 bytes (64 bits) is 2^64 - 1
41 3 : final maxValue = BigInt.parse('ffffffffffffffff', radix: 16);
42 :
43 : // Convert to a double between 0.0 and 1.0
44 9 : return value.toDouble() / maxValue.toDouble();
45 : }
46 :
47 4 : @override
48 : SamplingResult shouldSample({
49 : required Context parentContext,
50 : required String traceId,
51 : required String name,
52 : required SpanKind spanKind,
53 : required Attributes? attributes,
54 : required List<SpanLink>? links,
55 : }) {
56 5 : final parentTraceState = parentContext.spanContext?.traceState;
57 :
58 : // If ratio is 0, never sample
59 8 : if (ratio == 0.0) {
60 1 : return SamplingResult(
61 : decision: SamplingDecision.drop,
62 : source: SamplingDecisionSource.tracerConfig,
63 : traceState: parentTraceState,
64 : );
65 : }
66 :
67 : // If ratio is 1, always sample
68 8 : if (ratio == 1.0) {
69 2 : return SamplingResult(
70 : decision: SamplingDecision.recordAndSample,
71 : source: SamplingDecisionSource.tracerConfig,
72 : traceState: parentTraceState,
73 : );
74 : }
75 :
76 : // Convert trace ID to a value between 0.0 and 1.0
77 3 : final value = _traceIdToValue(traceId);
78 :
79 : // If the value is less than our ratio, we should sample
80 6 : final shouldSample = value < ratio;
81 :
82 3 : return SamplingResult(
83 : decision: shouldSample
84 : ? SamplingDecision.recordAndSample
85 : : SamplingDecision.drop,
86 : source: SamplingDecisionSource.tracerConfig,
87 : traceState: parentTraceState,
88 : );
89 : }
90 : }
|