-
-
Notifications
You must be signed in to change notification settings - Fork 515
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
Changes from all commits
77a2f49
183da90
a993e7d
d32e03e
a1c4b47
d1c8698
8443c3e
853a881
1512068
dc382f4
311e737
ba322a8
9a780c1
a0098f8
2da997f
72f631e
241ed08
20ff23a
c2088a3
db7da2e
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 |
---|---|---|
|
@@ -6,7 +6,8 @@ VERSION="3.4.5" | |
|
||
# E2E testing | ||
SENTRY_DSN="http://user:[email protected]/project/42" | ||
SENTRY_DSN_JS="http://user:[email protected]/project/43" | ||
# SENTRY_DSN_JS="http://user:[email protected]/project/43" | ||
SENTRY_DSN_JS="" | ||
|
||
SENTRY_E2E_RAILS_APP_PORT=4000 | ||
SENTRY_E2E_SVELTE_APP_PORT=4001 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
require "securerandom" | ||
require "sentry/baggage" | ||
require "sentry/utils/uuid" | ||
require "sentry/utils/sample_rand" | ||
|
||
module Sentry | ||
class PropagationContext | ||
|
@@ -33,13 +34,58 @@ class PropagationContext | |
# Please use the #get_baggage method for interfacing outside this class. | ||
# @return [Baggage, nil] | ||
attr_reader :baggage | ||
# The propagated random value used for sampling decisions. | ||
# @return [Float, nil] | ||
attr_reader :sample_rand | ||
|
||
# Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header. | ||
# | ||
# @param sentry_trace [String] the sentry-trace header value from the previous transaction. | ||
# @return [Array, nil] | ||
def self.extract_sentry_trace(sentry_trace) | ||
match = SENTRY_TRACE_REGEXP.match(sentry_trace) | ||
return if match.nil? | ||
|
||
trace_id, parent_span_id, sampled_flag = match[1..3] | ||
parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0" | ||
|
||
[trace_id, parent_span_id, parent_sampled] | ||
end | ||
|
||
def self.extract_sample_rand_from_baggage(baggage, trace_id = nil) | ||
return unless baggage&.items | ||
|
||
sample_rand_str = baggage.items["sample_rand"] | ||
return unless sample_rand_str | ||
|
||
generator = Utils::SampleRand.new(trace_id: trace_id) | ||
generator.generate_from_value(sample_rand_str) | ||
end | ||
|
||
def self.generate_sample_rand(baggage, trace_id, parent_sampled) | ||
generator = Utils::SampleRand.new(trace_id: trace_id) | ||
|
||
if baggage&.items && !parent_sampled.nil? | ||
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 | ||
else | ||
generator.generate_from_trace_id | ||
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: Sample Rate Handling BugIn |
||
end | ||
|
||
def initialize(scope, env = nil) | ||
@scope = scope | ||
@parent_span_id = nil | ||
@parent_sampled = nil | ||
@baggage = nil | ||
@incoming_trace = false | ||
@sample_rand = nil | ||
|
||
if env | ||
sentry_trace_header = env["HTTP_SENTRY_TRACE"] || env[SENTRY_TRACE_HEADER_NAME] | ||
|
@@ -61,6 +107,8 @@ def initialize(scope, env = nil) | |
Baggage.new({}) | ||
end | ||
|
||
@sample_rand = self.class.extract_sample_rand_from_baggage(@baggage, @trace_id) | ||
|
||
@baggage.freeze! | ||
@incoming_trace = true | ||
end | ||
|
@@ -69,20 +117,7 @@ def initialize(scope, env = nil) | |
|
||
@trace_id ||= Utils.uuid | ||
@span_id = Utils.uuid.slice(0, 16) | ||
end | ||
|
||
# Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header. | ||
# | ||
# @param sentry_trace [String] the sentry-trace header value from the previous transaction. | ||
# @return [Array, nil] | ||
def self.extract_sentry_trace(sentry_trace) | ||
match = SENTRY_TRACE_REGEXP.match(sentry_trace) | ||
return nil if match.nil? | ||
|
||
trace_id, parent_span_id, sampled_flag = match[1..3] | ||
parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0" | ||
|
||
[trace_id, parent_span_id, parent_sampled] | ||
@sample_rand ||= self.class.generate_sample_rand(@baggage, @trace_id, @parent_sampled) | ||
end | ||
|
||
# Returns the trace context that can be used to embed in an Event. | ||
|
@@ -123,6 +158,7 @@ def populate_head_baggage | |
|
||
items = { | ||
"trace_id" => trace_id, | ||
"sample_rand" => Utils::SampleRand.format(@sample_rand), | ||
"environment" => configuration.environment, | ||
"release" => configuration.release, | ||
"public_key" => configuration.dsn&.public_key | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# 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 | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def format_random(value) | ||
truncated = (value * PRECISION).floor / PRECISION | ||
("%.#{FORMAT_PRECISION}f" % truncated).to_f | ||
end | ||
|
||
def parse_value(sample_rand_value) | ||
Float(sample_rand_value) | ||
rescue ArgumentError | ||
nil | ||
end | ||
end | ||
end | ||
end |
Uh oh!
There was an error while loading. Please reload this page.