Skip to content

Commit

Permalink
Add new trace_propagation_targets configuration (#2079)
Browse files Browse the repository at this point in the history
  • Loading branch information
sl0thentr0py authored Aug 2, 2023
1 parent 94a4895 commit bc0f8ce
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 7 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
### Features

- Make `:value` in `SingleExceptionInterface` writable, so that it can be modified in `before_send` under `event.exception.values[n].value` [#2072](https://github.com/getsentry/sentry-ruby/pull/2072)
- Add new `config.trace_propagation_targets` option to set targets for which headers are propagated in outgoing HTTP requests [#2079](https://github.com/getsentry/sentry-ruby/pull/2079)

```rb
# takes an array of strings or regexps
config.trace_propagation_targets = [/.*/] # default is to all targets
config.trace_propagation_targets = [/example.com/, 'foobar.org/api/v2']
```

## 5.10.0

Expand Down
8 changes: 8 additions & 0 deletions sentry-ruby/lib/sentry/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ def capture_exception_frame_locals=(value)
# @return [Boolean]
attr_accessor :auto_session_tracking

# Allowlist of outgoing request targets to which sentry-trace and baggage headers are attached.
# Default is all (/.*/)
# @return [Array<String, Regexp>]
attr_accessor :trace_propagation_targets

# The instrumenter to use, :sentry or :otel
# @return [Symbol]
attr_reader :instrumenter
Expand Down Expand Up @@ -290,6 +295,8 @@ def capture_exception_frame_locals=(value)

INSTRUMENTERS = [:sentry, :otel]

PROPAGATION_TARGETS_MATCH_ALL = /.*/.freeze

class << self
# Post initialization callbacks are called at the end of initialization process
# allowing extending the configuration of sentry-ruby by multiple extensions
Expand Down Expand Up @@ -332,6 +339,7 @@ def initialize
self.dsn = ENV['SENTRY_DSN']
self.server_name = server_name_from_env
self.instrumenter = :sentry
self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]

self.before_send = nil
self.before_send_transaction = nil
Expand Down
17 changes: 10 additions & 7 deletions sentry-ruby/lib/sentry/net/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ def request(req, body = nil, &block)
return super if from_sentry_sdk?

Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |sentry_span|
set_sentry_trace_header(req, sentry_span)
request_info = extract_request_info(req)
set_sentry_trace_header(req, sentry_span, request_info)

super.tap do |res|
record_sentry_breadcrumb(req, res)
record_sentry_breadcrumb(request_info, res)

if sentry_span
request_info = extract_request_info(req)
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
sentry_span.set_data('url', request_info[:url])
sentry_span.set_data('http.method', request_info[:method])
Expand All @@ -49,10 +49,11 @@ def request(req, body = nil, &block)

private

def set_sentry_trace_header(req, sentry_span)
def set_sentry_trace_header(req, sentry_span, request_info)
return unless sentry_span

client = Sentry.get_current_client
return unless propagate_trace?(request_info[:url], client.configuration.trace_propagation_targets)

trace = client.generate_sentry_trace(sentry_span)
req[SENTRY_TRACE_HEADER_NAME] = trace if trace
Expand All @@ -61,11 +62,9 @@ def set_sentry_trace_header(req, sentry_span)
req[BAGGAGE_HEADER_NAME] = baggage if baggage && !baggage.empty?
end

def record_sentry_breadcrumb(req, res)
def record_sentry_breadcrumb(request_info, res)
return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)

request_info = extract_request_info(req)

crumb = Sentry::Breadcrumb.new(
level: :info,
category: BREADCRUMB_CATEGORY,
Expand Down Expand Up @@ -96,6 +95,10 @@ def extract_request_info(req)

result
end

def propagate_trace?(url, trace_propagation_targets)
url && trace_propagation_targets.any? { |target| url.match?(target) }
end
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions sentry-ruby/spec/sentry/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,17 @@ class SentryConfigurationSample < Sentry::Configuration
end
end

describe "#trace_propagation_targets" do
it "returns match all by default" do
expect(subject.trace_propagation_targets).to eq([/.*/])
end

it "accepts array of strings or regexps" do
subject.trace_propagation_targets = ["example.com", /foobar.org\/api\/v2/]
expect(subject.trace_propagation_targets).to eq(["example.com", /foobar.org\/api\/v2/])
end
end

describe "#instrumenter" do
it "returns :sentry by default" do
expect(subject.instrumenter).to eq(:sentry)
Expand Down
70 changes: 70 additions & 0 deletions sentry-ruby/spec/sentry/net/http_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,76 @@
end
end

context "with custom trace_propagation_targets" do
before do
Sentry.configuration.trace_propagation_targets = ["example.com", /foobar.org\/api\/v2/]
end

it "doesn't add sentry headers to outgoing requests to different target" do
stub_normal_response

uri = URI("http://google.com/path")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)

transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)

http.request(request)

expect(request.key?("sentry-trace")).to eq(false)
expect(request.key?("baggage")).to eq(false)
end

it "doesn't add sentry headers to outgoing requests to different target path" do
stub_normal_response

uri = URI("http://foobar.org/api/v1/path")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)

transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)

http.request(request)

expect(request.key?("sentry-trace")).to eq(false)
expect(request.key?("baggage")).to eq(false)
end

it "adds sentry headers to outgoing requests matching string" 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)

transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)

http.request(request)

expect(request.key?("sentry-trace")).to eq(true)
expect(request.key?("baggage")).to eq(true)
end

it "adds sentry headers to outgoing requests matching regexp" do
stub_normal_response

uri = URI("http://foobar.org/api/v2/path")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)

transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)

http.request(request)

expect(request.key?("sentry-trace")).to eq(true)
expect(request.key?("baggage")).to eq(true)
end
end

it "doesn't record span for the SDK's request" do
stub_sentry_response

Expand Down

0 comments on commit bc0f8ce

Please sign in to comment.