|
2 | 2 |
|
3 | 3 | module Sentry |
4 | 4 | module Utils |
5 | | - module SampleRand |
6 | | - def self.generate_from_trace_id(trace_id) |
7 | | - (random_from_trace_id(trace_id) * 1_000_000).floor / 1_000_000.0 |
| 5 | + class SampleRand |
| 6 | + PRECISION = 1_000_000.0 |
| 7 | + FORMAT_PRECISION = 6 |
| 8 | + |
| 9 | + attr_reader :trace_id |
| 10 | + |
| 11 | + def self.valid?(value) |
| 12 | + return false unless value |
| 13 | + value >= 0.0 && value < 1.0 |
| 14 | + end |
| 15 | + |
| 16 | + def self.format(value) |
| 17 | + return unless value |
| 18 | + |
| 19 | + truncated = (value * PRECISION).floor / PRECISION |
| 20 | + "%.#{FORMAT_PRECISION}f" % truncated |
| 21 | + end |
| 22 | + |
| 23 | + def initialize(trace_id: nil) |
| 24 | + @trace_id = trace_id |
8 | 25 | end |
9 | 26 |
|
10 | | - def self.generate_from_sampling_decision(sampled, sample_rate, trace_id = nil) |
11 | | - if sample_rate.nil? || sample_rate <= 0.0 || sample_rate > 1.0 |
12 | | - trace_id ? generate_from_trace_id(trace_id) : format(Random.rand(1.0)).to_f |
| 27 | + def generate_from_trace_id |
| 28 | + (random_from_trace_id * PRECISION).floor / PRECISION |
| 29 | + end |
| 30 | + |
| 31 | + def generate_from_sampling_decision(sampled, sample_rate) |
| 32 | + if invalid_sample_rate?(sample_rate) |
| 33 | + fallback_generation |
13 | 34 | else |
14 | | - random = random_from_trace_id(trace_id) |
15 | | - |
16 | | - if sampled |
17 | | - format(random * sample_rate) |
18 | | - elsif sample_rate == 1.0 |
19 | | - format(random) |
20 | | - else |
21 | | - format(sample_rate + random * (1.0 - sample_rate)) |
22 | | - end.to_f |
| 35 | + generate_based_on_sampling(sampled, sample_rate) |
23 | 36 | end |
24 | 37 | end |
25 | 38 |
|
26 | | - def self.random_from_trace_id(trace_id) |
27 | | - if trace_id |
28 | | - Random.new(trace_id[0, 16].to_i(16)) |
| 39 | + def generate_from_value(sample_rand_value) |
| 40 | + parsed_value = parse_value(sample_rand_value) |
| 41 | + |
| 42 | + if self.class.valid?(parsed_value) |
| 43 | + parsed_value |
| 44 | + else |
| 45 | + fallback_generation |
| 46 | + end |
| 47 | + end |
| 48 | + |
| 49 | + private |
| 50 | + |
| 51 | + def random_from_trace_id |
| 52 | + if @trace_id |
| 53 | + Random.new(@trace_id[0, 16].to_i(16)) |
29 | 54 | else |
30 | 55 | Random.new |
31 | 56 | end.rand(1.0) |
32 | 57 | end |
33 | 58 |
|
34 | | - def self.valid?(sample_rand) |
35 | | - return false unless sample_rand |
36 | | - return false if sample_rand.is_a?(String) && sample_rand.empty? |
| 59 | + def invalid_sample_rate?(sample_rate) |
| 60 | + sample_rate.nil? || sample_rate <= 0.0 || sample_rate > 1.0 |
| 61 | + end |
37 | 62 |
|
38 | | - value = sample_rand.is_a?(String) ? sample_rand.to_f : sample_rand |
39 | | - value >= 0.0 && value < 1.0 |
| 63 | + def fallback_generation |
| 64 | + if @trace_id |
| 65 | + (random_from_trace_id * PRECISION).floor / PRECISION |
| 66 | + else |
| 67 | + format_random(Random.rand(1.0)) |
| 68 | + end |
| 69 | + end |
| 70 | + |
| 71 | + def generate_based_on_sampling(sampled, sample_rate) |
| 72 | + random = random_from_trace_id |
| 73 | + |
| 74 | + result = if sampled |
| 75 | + random * sample_rate |
| 76 | + elsif sample_rate == 1.0 |
| 77 | + random |
| 78 | + else |
| 79 | + sample_rate + random * (1.0 - sample_rate) |
| 80 | + end |
| 81 | + |
| 82 | + format_random(result) |
40 | 83 | end |
41 | 84 |
|
42 | | - def self.format(sample_rand) |
43 | | - truncated = (sample_rand * 1_000_000).floor / 1_000_000.0 |
44 | | - "%.6f" % truncated |
| 85 | + def format_random(value) |
| 86 | + truncated = (value * PRECISION).floor / PRECISION |
| 87 | + ("%.#{FORMAT_PRECISION}f" % truncated).to_f |
| 88 | + end |
| 89 | + |
| 90 | + def parse_value(sample_rand_value) |
| 91 | + return unless sample_rand_value |
| 92 | + return if sample_rand_value.is_a?(String) && sample_rand_value.empty? |
| 93 | + |
| 94 | + sample_rand_value.is_a?(String) ? sample_rand_value.to_f : sample_rand_value |
45 | 95 | end |
46 | 96 | end |
47 | 97 | end |
|
0 commit comments