-
-
Notifications
You must be signed in to change notification settings - Fork 516
Propagated sampling rates #2671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
42f2b62
df46b6e
4965f37
5d3b601
a7ddeda
0fdbceb
763c8ad
04fe927
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
require "sentry/baggage" | ||
require "sentry/profiler" | ||
require "sentry/utils/sample_rand" | ||
require "sentry/propagation_context" | ||
|
||
module Sentry | ||
|
@@ -57,12 +58,17 @@ class Transaction < Span | |
# @return [Profiler] | ||
attr_reader :profiler | ||
|
||
# Sample rand value generated from trace_id | ||
# @return [String] | ||
attr_reader :sample_rand | ||
|
||
def initialize( | ||
hub:, | ||
name: nil, | ||
source: :custom, | ||
parent_sampled: nil, | ||
baggage: nil, | ||
sample_rand: nil, | ||
**options | ||
) | ||
super(transaction: self, **options) | ||
|
@@ -82,12 +88,18 @@ def initialize( | |
@effective_sample_rate = nil | ||
@contexts = {} | ||
@measurements = {} | ||
@sample_rand = sample_rand | ||
|
||
unless @hub.profiler_running? | ||
@profiler = @configuration.profiler_class.new(@configuration) | ||
end | ||
|
||
init_span_recorder | ||
|
||
unless @sample_rand | ||
generator = Utils::SampleRand.new(trace_id: @trace_id) | ||
@sample_rand = generator.generate_from_trace_id | ||
end | ||
end | ||
|
||
# @deprecated use Sentry.continue_trace instead. | ||
|
@@ -123,12 +135,15 @@ def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_h | |
|
||
baggage.freeze! | ||
|
||
sample_rand = extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled) | ||
|
||
new( | ||
trace_id: trace_id, | ||
parent_span_id: parent_span_id, | ||
parent_sampled: parent_sampled, | ||
hub: hub, | ||
baggage: baggage, | ||
sample_rand: sample_rand, | ||
**options | ||
) | ||
end | ||
|
@@ -139,6 +154,29 @@ def self.extract_sentry_trace(sentry_trace) | |
PropagationContext.extract_sentry_trace(sentry_trace) | ||
end | ||
|
||
def self.extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we not define these methods again, but add params to the ones in so expose
|
||
generator = Utils::SampleRand.new(trace_id: trace_id) | ||
|
||
unless baggage&.items | ||
return generator.generate_from_trace_id | ||
end | ||
|
||
sample_rand_str = baggage.items["sample_rand"] | ||
|
||
if sample_rand_str | ||
return generator.generate_from_value(sample_rand_str) | ||
end | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
sample_rate_str = baggage.items["sample_rate"] | ||
sample_rate = sample_rate_str&.to_f | ||
|
||
if sample_rate && parent_sampled != nil | ||
generator.generate_from_sampling_decision(parent_sampled, sample_rate) | ||
else | ||
generator.generate_from_trace_id | ||
end | ||
end | ||
|
||
# @return [Hash] | ||
def to_hash | ||
hash = super | ||
|
@@ -153,6 +191,13 @@ def to_hash | |
hash | ||
end | ||
|
||
def parent_sample_rate | ||
return unless @baggage&.items | ||
|
||
sample_rate_str = @baggage.items["sample_rate"] | ||
sample_rate_str&.to_f | ||
end | ||
|
||
# @return [Transaction] | ||
def deep_dup | ||
copy = super | ||
|
@@ -225,7 +270,7 @@ def set_initial_sample_decision(sampling_context:) | |
@effective_sample_rate /= 2**factor | ||
end | ||
|
||
@sampled = Random.rand < @effective_sample_rate | ||
@sampled = @sample_rand < @effective_sample_rate | ||
end | ||
|
||
if @sampled | ||
|
@@ -331,6 +376,7 @@ def populate_head_baggage | |
items = { | ||
"trace_id" => trace_id, | ||
"sample_rate" => effective_sample_rate&.to_s, | ||
"sample_rand" => Utils::SampleRand.format(@sample_rand), | ||
"sampled" => sampled&.to_s, | ||
"environment" => @environment, | ||
"release" => @release, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# frozen_string_literal: true | ||
|
||
module Sentry | ||
module Utils | ||
class SampleRand | ||
PRECISION = 1_000_000.0 | ||
FORMAT_PRECISION = 6 | ||
|
||
attr_reader :trace_id | ||
|
||
def self.valid?(value) | ||
return false unless value | ||
value >= 0.0 && value < 1.0 | ||
end | ||
|
||
def self.format(value) | ||
return unless value | ||
|
||
truncated = (value * PRECISION).floor / PRECISION | ||
"%.#{FORMAT_PRECISION}f" % truncated | ||
end | ||
|
||
def initialize(trace_id: nil) | ||
@trace_id = trace_id | ||
end | ||
|
||
def generate_from_trace_id | ||
(random_from_trace_id * PRECISION).floor / PRECISION | ||
end | ||
|
||
def generate_from_sampling_decision(sampled, sample_rate) | ||
if invalid_sample_rate?(sample_rate) | ||
fallback_generation | ||
else | ||
generate_based_on_sampling(sampled, sample_rate) | ||
end | ||
end | ||
|
||
def generate_from_value(sample_rand_value) | ||
parsed_value = parse_value(sample_rand_value) | ||
|
||
if self.class.valid?(parsed_value) | ||
parsed_value | ||
else | ||
fallback_generation | ||
end | ||
solnic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
private | ||
|
||
def random_from_trace_id | ||
if @trace_id | ||
Random.new(@trace_id[0, 16].to_i(16)) | ||
else | ||
Random.new | ||
end.rand(1.0) | ||
end | ||
|
||
def invalid_sample_rate?(sample_rate) | ||
sample_rate.nil? || sample_rate <= 0.0 || sample_rate > 1.0 | ||
end | ||
|
||
def fallback_generation | ||
if @trace_id | ||
(random_from_trace_id * PRECISION).floor / PRECISION | ||
else | ||
format_random(Random.rand(1.0)) | ||
end | ||
end | ||
|
||
def generate_based_on_sampling(sampled, sample_rate) | ||
random = random_from_trace_id | ||
|
||
result = if sampled | ||
random * sample_rate | ||
elsif sample_rate == 1.0 | ||
random | ||
else | ||
sample_rate + random * (1.0 - sample_rate) | ||
end | ||
|
||
format_random(result) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: String Validation Error in
|
||
|
||
def format_random(value) | ||
truncated = (value * PRECISION).floor / PRECISION | ||
("%.#{FORMAT_PRECISION}f" % truncated).to_f | ||
end | ||
|
||
def parse_value(sample_rand_value) | ||
return unless sample_rand_value | ||
return if sample_rand_value.is_a?(String) && sample_rand_value.empty? | ||
|
||
sample_rand_value.is_a?(String) ? sample_rand_value.to_f : sample_rand_value | ||
end | ||
end | ||
end | ||
end |
Uh oh!
There was an error while loading. Please reload this page.