diff --git a/sentry-ruby/lib/sentry/hub.rb b/sentry-ruby/lib/sentry/hub.rb index 0efd00b3c..c9f95eefe 100644 --- a/sentry-ruby/lib/sentry/hub.rb +++ b/sentry-ruby/lib/sentry/hub.rb @@ -244,8 +244,6 @@ def get_baggage end def get_trace_propagation_headers - return nil unless configuration.propagate_traces - headers = {} traceparent = get_traceparent diff --git a/sentry-ruby/lib/sentry/net/http.rb b/sentry-ruby/lib/sentry/net/http.rb index 04eedb290..4b0345f52 100644 --- a/sentry-ruby/lib/sentry/net/http.rb +++ b/sentry-ruby/lib/sentry/net/http.rb @@ -32,7 +32,7 @@ def request(req, body = nil, &block) Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |sentry_span| request_info = extract_request_info(req) - if propagate_trace?(request_info[:url], Sentry.configuration.trace_propagation_targets) + if propagate_trace?(request_info[:url], Sentry.configuration) set_propagation_headers(req) end @@ -90,8 +90,10 @@ def extract_request_info(req) result end - def propagate_trace?(url, trace_propagation_targets) - url && trace_propagation_targets.any? { |target| url.match?(target) } + def propagate_trace?(url, configuration) + url && + configuration.propagate_traces && + configuration.trace_propagation_targets.any? { |target| url.match?(target) } end end end diff --git a/sentry-ruby/spec/sentry/event_spec.rb b/sentry-ruby/spec/sentry/event_spec.rb index cfa19b259..10c4b448c 100644 --- a/sentry-ruby/spec/sentry/event_spec.rb +++ b/sentry-ruby/spec/sentry/event_spec.rb @@ -30,6 +30,7 @@ expect(event.environment).to eq("test") expect(event.release).to eq("721e41770371db95eee98ca2707686226b993eda") expect(event.sdk).to eq("name" => "sentry.ruby", "version" => Sentry::VERSION) + expect(event.dynamic_sampling_context).to eq(nil) end end diff --git a/sentry-ruby/spec/sentry/net/http_spec.rb b/sentry-ruby/spec/sentry/net/http_spec.rb index 21eb96f7b..1e96f7feb 100644 --- a/sentry-ruby/spec/sentry/net/http_spec.rb +++ b/sentry-ruby/spec/sentry/net/http_spec.rb @@ -373,7 +373,20 @@ def verify_spans(transaction) perform_basic_setup end - it "doesn't affect the HTTP lib anything" do + it "adds sentry-trace and baggage headers for tracing without performance" do + stub_normal_response + + uri = URI("http://example.com/path") + http = Net::HTTP.new(uri.host, uri.port) + request = Net::HTTP::Get.new(uri.request_uri) + response = http.request(request) + + expect(request["sentry-trace"]).to eq(Sentry.get_traceparent) + expect(request["baggage"]).to eq(Sentry.get_baggage) + expect(response.code).to eq("200") + end + + it "doesn't create transaction or breadcrumbs" do stub_normal_response response = Net::HTTP.get_response(URI("http://example.com/path")) diff --git a/sentry-ruby/spec/sentry/propagation_context_spec.rb b/sentry-ruby/spec/sentry/propagation_context_spec.rb new file mode 100644 index 000000000..ce2c337b2 --- /dev/null +++ b/sentry-ruby/spec/sentry/propagation_context_spec.rb @@ -0,0 +1,63 @@ +require "spec_helper" + +RSpec.describe Sentry::PropagationContext do + before do + perform_basic_setup + end + + let(:scope) { Sentry.get_current_scope } + let(:subject) { described_class.new(scope) } + + describe "#initialize" do + it "generates correct attributes" do + expect(subject.trace_id.length).to eq(32) + expect(subject.span_id.length).to eq(16) + expect(subject.parent_span_id).to be_nil + end + end + + describe "#get_trace_context" do + it "generates correct trace context" do + expect(subject.get_trace_context).to eq({ + trace_id: subject.trace_id, + span_id: subject.span_id, + parent_span_id: subject.parent_span_id + }) + end + end + + describe "#get_traceparent" do + it "generates correct traceparent" do + expect(subject.get_traceparent).to eq("#{subject.trace_id}-#{subject.span_id}") + end + end + + describe "#get_baggage" do + before do + perform_basic_setup do |config| + config.environment = "test" + config.release = "foobar" + config.traces_sample_rate = 0.5 + end + end + + it "populates head baggage" do + baggage = subject.get_baggage + + expect(baggage.mutable).to eq(false) + expect(baggage.items).to eq({ + "trace_id" => subject.trace_id, + "sample_rate" => "0.5", + "environment" => "test", + "release" => "foobar", + "public_key" => Sentry.configuration.dsn.public_key + }) + end + end + + describe "#get_dynamic_sampling_context" do + it "generates DSC from baggage" do + expect(subject.get_dynamic_sampling_context).to eq(subject.get_baggage.dynamic_sampling_context) + end + end +end diff --git a/sentry-ruby/spec/sentry/scope_spec.rb b/sentry-ruby/spec/sentry/scope_spec.rb index 1e6d92426..3573e3a6e 100644 --- a/sentry-ruby/spec/sentry/scope_spec.rb +++ b/sentry-ruby/spec/sentry/scope_spec.rb @@ -24,6 +24,7 @@ expect(subject.fingerprint).to eq([]) expect(subject.transaction_names).to eq([]) expect(subject.transaction_sources).to eq([]) + expect(subject.propagation_context).to be_a(Sentry::PropagationContext) end it "allows setting breadcrumb buffer's size limit" do @@ -276,7 +277,7 @@ end end - it "sets trace context if there's a span" do + it "sets trace context from span if there's a span" do transaction = Sentry::Transaction.new(op: "foo", hub: hub) subject.set_span(transaction) @@ -286,6 +287,12 @@ expect(event.contexts.dig(:trace, :op)).to eq("foo") end + it "sets trace context and dynamic_sampling_context from propagation context if there's no span" do + subject.apply_to_event(event) + expect(event.contexts[:trace]).to eq(subject.propagation_context.get_trace_context) + expect(event.dynamic_sampling_context).to eq(subject.propagation_context.get_dynamic_sampling_context) + end + context "with Rack", rack: true do let(:env) do Rack::MockRequest.env_for("/test", {}) diff --git a/sentry-ruby/spec/sentry_spec.rb b/sentry-ruby/spec/sentry_spec.rb index 72fb18970..beb931131 100644 --- a/sentry-ruby/spec/sentry_spec.rb +++ b/sentry-ruby/spec/sentry_spec.rb @@ -667,6 +667,55 @@ end end + describe ".get_traceparent" do + it "returns a valid traceparent header from scope propagation context" do + traceparent = described_class.get_traceparent + propagation_context = described_class.get_current_scope.propagation_context + + expect(traceparent).to match(Sentry::Transaction::SENTRY_TRACE_REGEXP) + expect(traceparent).to eq("#{propagation_context.trace_id}-#{propagation_context.span_id}") + end + + it "returns a valid traceparent header from scope current span" do + transaction = Sentry::Transaction.new(op: "foo", hub: Sentry.get_current_hub, sampled: true) + span = transaction.start_child(op: "parent") + described_class.get_current_scope.set_span(span) + + traceparent = described_class.get_traceparent + + expect(traceparent).to match(Sentry::Transaction::SENTRY_TRACE_REGEXP) + expect(traceparent).to eq("#{span.trace_id}-#{span.span_id}-1") + end + end + + describe ".get_baggage" do + it "returns a valid baggage header from scope propagation context" do + baggage = described_class.get_baggage + propagation_context = described_class.get_current_scope.propagation_context + + expect(baggage).to eq("sentry-trace_id=#{propagation_context.trace_id},sentry-environment=development,sentry-public_key=12345") + end + + it "returns a valid baggage header from scope current span" do + transaction = Sentry::Transaction.new(op: "foo", hub: Sentry.get_current_hub, sampled: true) + span = transaction.start_child(op: "parent") + described_class.get_current_scope.set_span(span) + + baggage = described_class.get_baggage + + expect(baggage).to eq("sentry-trace_id=#{span.trace_id},sentry-sampled=true,sentry-environment=development,sentry-public_key=12345") + end + end + + describe ".get_trace_propagation_headers" do + it "returns a Hash of sentry-trace and baggage" do + expect(described_class.get_trace_propagation_headers).to eq({ + "sentry-trace" => described_class.get_traceparent, + "baggage" => described_class.get_baggage + }) + end + end + describe 'release detection' do let(:fake_root) { "/tmp/sentry/" }