Line data Source code
1 : // Licensed under the Apache License, Version 2.0
2 : // Copyright 2025, Michael Bushe, All rights reserved.
3 :
4 : import 'dart:math';
5 : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
6 : import 'sampler.dart';
7 :
8 : /// A sampler that randomly samples traces based on a probability.
9 : ///
10 : /// This sampler makes a random decision for each span, with the specified
11 : /// probability of sampling. Unlike the TraceIdRatioSampler, which uses the
12 : /// trace ID to make a consistent decision for all spans in a trace, this
13 : /// sampler uses a fresh random number for each decision.
14 : ///
15 : /// Note that this means the same trace ID might get different sampling
16 : /// decisions if evaluated multiple times, which could lead to inconsistent
17 : /// sampling within a trace. For consistent sampling across a trace, use
18 : /// TraceIdRatioSampler or ParentBasedSampler.
19 : class ProbabilitySampler implements Sampler {
20 : /// The probability of sampling a span, in the range [0.0, 1.0].
21 : final double probability;
22 :
23 : /// Random number generator for sampling decisions.
24 : late final Random _random;
25 :
26 : /// Gets a description of this sampler.
27 : ///
28 : /// @return A description including the sampling probability
29 0 : @override
30 0 : String get description => 'ProbabilitySampler{$probability}';
31 :
32 : /// Creates a probability sampler with the given probability.
33 : ///
34 : /// @param probability The probability of sampling a span, in the range [0.0, 1.0]
35 : /// @param seed Optional seed for the random number generator (mainly for testing)
36 : /// @throws ArgumentError if probability is not in the range [0.0, 1.0]
37 1 : ProbabilitySampler(this.probability, {int? seed}) {
38 : // Initialize random with secure random if no seed provided
39 3 : _random = seed != null ? Random(seed) : Random.secure();
40 4 : if (probability < 0.0 || probability > 1.0) {
41 0 : throw ArgumentError('probability must be in range [0.0, 1.0]');
42 : }
43 : }
44 :
45 : /// Makes a sampling decision based on random probability.
46 : ///
47 : /// This method generates a random number between 0 and 1, and compares
48 : /// it to the configured probability to make a sampling decision.
49 : ///
50 : /// @param parentContext Ignored
51 : /// @param traceId Ignored
52 : /// @param name Ignored
53 : /// @param spanKind Ignored
54 : /// @param attributes Ignored
55 : /// @param links Ignored
56 : /// @return A sampling result based on the random probability
57 1 : @override
58 : SamplingResult shouldSample({
59 : required Context parentContext,
60 : required String traceId,
61 : required String name,
62 : required SpanKind spanKind,
63 : required Attributes? attributes,
64 : required List<SpanLink>? links,
65 : }) {
66 : // Short circuit for always/never sample
67 2 : if (probability >= 1.0) {
68 : return const SamplingResult(
69 : decision: SamplingDecision.recordAndSample,
70 : source: SamplingDecisionSource.tracerConfig,
71 : );
72 : }
73 2 : if (probability <= 0.0) {
74 : return const SamplingResult(
75 : decision: SamplingDecision.drop,
76 : source: SamplingDecisionSource.tracerConfig,
77 : );
78 : }
79 :
80 4 : final decision = _random.nextDouble() < probability;
81 :
82 1 : return SamplingResult(
83 : decision:
84 : decision ? SamplingDecision.recordAndSample : SamplingDecision.drop,
85 : source: SamplingDecisionSource.tracerConfig,
86 : );
87 : }
88 : }
|