Skip to content
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

Unify Net::HTTP and Faraday implementations #2360

Merged
merged 3 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sentry-ruby/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ gem "puma"
gem "timecop"
gem "stackprof" unless RUBY_PLATFORM == "java"

gem "graphql", ">= 2.2.6" if RUBY_VERSION.to_f >= 2.7
gem "graphql", ">= 2.2.6", "< 2.3.11" if RUBY_VERSION.to_f >= 2.7

gem "benchmark-ips"
gem "benchmark_driver"
Expand Down
50 changes: 8 additions & 42 deletions sentry-ruby/lib/sentry/faraday.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
module Sentry
module Faraday
OP_NAME = "http.client"
SPAN_ORIGIN = "auto.http.faraday"
BREADCRUMB_CATEGORY = "http"

module Connection
# Since there's no way to preconfigure Faraday connections and add our instrumentation
Expand All @@ -25,14 +23,13 @@
end

class Instrumenter
attr_reader :configuration
SPAN_ORIGIN = "auto.http.faraday"
BREADCRUMB_CATEGORY = "http"

def initialize
@configuration = Sentry.configuration
end
include Utils::HttpTracing

def instrument(op_name, env, &block)
return unless Sentry.initialized?
return block.call unless Sentry.initialized?

Check warning on line 32 in sentry-ruby/lib/sentry/faraday.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/faraday.rb#L32

Added line #L32 was not covered by tests

Sentry.with_child_span(op: op_name, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |sentry_span|
request_info = extract_request_info(env)
Expand All @@ -42,17 +39,14 @@
end

res = block.call
response_status = res.status

Check warning on line 42 in sentry-ruby/lib/sentry/faraday.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/faraday.rb#L42

Added line #L42 was not covered by tests

if record_sentry_breadcrumb?
record_sentry_breadcrumb(request_info, res)
record_sentry_breadcrumb(request_info, response_status)

Check warning on line 45 in sentry-ruby/lib/sentry/faraday.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/faraday.rb#L45

Added line #L45 was not covered by tests
end

if sentry_span
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, res.status)
set_span_info(sentry_span, request_info, response_status)

Check warning on line 49 in sentry-ruby/lib/sentry/faraday.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/faraday.rb#L49

Added line #L49 was not covered by tests
end

res
Expand All @@ -65,41 +59,13 @@
url = env[:url].scheme + "://" + env[:url].host + env[:url].path
result = { method: env[:method].to_s.upcase, url: url }

if configuration.send_default_pii
if Sentry.configuration.send_default_pii

Check warning on line 62 in sentry-ruby/lib/sentry/faraday.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/faraday.rb#L62

Added line #L62 was not covered by tests
result[:query] = env[:url].query
result[:body] = env[:body]
end

result
end

def record_sentry_breadcrumb(request_info, res)
crumb = Sentry::Breadcrumb.new(
level: :info,
category: BREADCRUMB_CATEGORY,
type: :info,
data: {
status: res.status,
**request_info
}
)

Sentry.add_breadcrumb(crumb)
end

def record_sentry_breadcrumb?
configuration.breadcrumbs_logger.include?(:http_logger)
end

def propagate_trace?(url)
url &&
configuration.propagate_traces &&
configuration.trace_propagation_targets.any? { |target| url.match?(target) }
end

def set_propagation_headers(headers)
Sentry.get_trace_propagation_headers&.each { |k, v| headers[k] = v }
end
end
end
end
Expand Down
52 changes: 15 additions & 37 deletions sentry-ruby/lib/sentry/net/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

require "net/http"
require "resolv"
require "sentry/utils/http_tracing"

module Sentry
# @api private
module Net
module HTTP
include Utils::HttpTracing

OP_NAME = "http.client"
SPAN_ORIGIN = "auto.http.net_http"
BREADCRUMB_CATEGORY = "net.http"
Expand All @@ -21,8 +24,7 @@
# req['connection'] ||= 'close'
# return request(req, body, &block) # <- request will be called for the second time from the first call
# }
# end
# # .....
# end # .....
# end
# ```
#
Expand All @@ -34,44 +36,26 @@
Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |sentry_span|
request_info = extract_request_info(req)

if propagate_trace?(request_info[:url], Sentry.configuration)
if propagate_trace?(request_info[:url])

Check warning on line 39 in sentry-ruby/lib/sentry/net/http.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/net/http.rb#L39

Added line #L39 was not covered by tests
set_propagation_headers(req)
end

super.tap do |res|
record_sentry_breadcrumb(request_info, res)
res = super
response_status = res.code.to_i

Check warning on line 44 in sentry-ruby/lib/sentry/net/http.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/net/http.rb#L43-L44

Added lines #L43 - L44 were not covered by tests

if sentry_span
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, res.code.to_i)
end
if record_sentry_breadcrumb?
record_sentry_breadcrumb(request_info, response_status)

Check warning on line 47 in sentry-ruby/lib/sentry/net/http.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/net/http.rb#L46-L47

Added lines #L46 - L47 were not covered by tests
end
end
end

private
if sentry_span
set_span_info(sentry_span, request_info, response_status)

Check warning on line 51 in sentry-ruby/lib/sentry/net/http.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/net/http.rb#L50-L51

Added lines #L50 - L51 were not covered by tests
end

def set_propagation_headers(req)
Sentry.get_trace_propagation_headers&.each { |k, v| req[k] = v }
res

Check warning on line 54 in sentry-ruby/lib/sentry/net/http.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/net/http.rb#L54

Added line #L54 was not covered by tests
end
end

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

crumb = Sentry::Breadcrumb.new(
level: :info,
category: BREADCRUMB_CATEGORY,
type: :info,
data: {
status: res.code.to_i,
**request_info
}
)
Sentry.add_breadcrumb(crumb)
end
private

def from_sentry_sdk?
dsn = Sentry.configuration.dsn
Expand All @@ -94,12 +78,6 @@

result
end

def propagate_trace?(url, configuration)
url &&
configuration.propagate_traces &&
configuration.trace_propagation_targets.any? { |target| url.match?(target) }
end
end
end
end
Expand Down
41 changes: 41 additions & 0 deletions sentry-ruby/lib/sentry/utils/http_tracing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

module Sentry
module Utils
module HttpTracing
def set_span_info(sentry_span, request_info, response_status)
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, response_status)

Check warning on line 11 in sentry-ruby/lib/sentry/utils/http_tracing.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/utils/http_tracing.rb#L7-L11

Added lines #L7 - L11 were not covered by tests
end

def set_propagation_headers(req)
Sentry.get_trace_propagation_headers&.each { |k, v| req[k] = v }

Check warning on line 15 in sentry-ruby/lib/sentry/utils/http_tracing.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/utils/http_tracing.rb#L15

Added line #L15 was not covered by tests
end

def record_sentry_breadcrumb(request_info, response_status)
crumb = Sentry::Breadcrumb.new(

Check warning on line 19 in sentry-ruby/lib/sentry/utils/http_tracing.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/utils/http_tracing.rb#L19

Added line #L19 was not covered by tests
level: :info,
category: self.class::BREADCRUMB_CATEGORY,
type: :info,
data: { status: response_status, **request_info }
)

Sentry.add_breadcrumb(crumb)

Check warning on line 26 in sentry-ruby/lib/sentry/utils/http_tracing.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/utils/http_tracing.rb#L26

Added line #L26 was not covered by tests
end

def record_sentry_breadcrumb?
Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)

Check warning on line 30 in sentry-ruby/lib/sentry/utils/http_tracing.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/utils/http_tracing.rb#L30

Added line #L30 was not covered by tests
end

def propagate_trace?(url)
url &&

Check warning on line 34 in sentry-ruby/lib/sentry/utils/http_tracing.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/utils/http_tracing.rb#L34

Added line #L34 was not covered by tests
Sentry.initialized? &&
Sentry.configuration.propagate_traces &&
solnic marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sl0thentr0py given that direct access to the configuration is not thread-safe, we could have a method for checking configuration values that would perform this additonal initialize? check, something like:

Sentry.configuration(:propagate_traces)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feel free to to add a helper but the semantics of that (truthy or falsey?) are not always clean.

Sentry.configuration.trace_propagation_targets.any? { |target| url.match?(target) }

Check warning on line 37 in sentry-ruby/lib/sentry/utils/http_tracing.rb

View check run for this annotation

Codecov / codecov/patch

sentry-ruby/lib/sentry/utils/http_tracing.rb#L37

Added line #L37 was not covered by tests
end
end
end
end
22 changes: 22 additions & 0 deletions sentry-ruby/spec/sentry/faraday_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,26 @@
expect(transaction.span_recorder.spans.map(&:origin)).not_to include("auto.http.faraday")
end
end

context "when Sentry is not initialized" do
let(:http) do
Faraday.new(url) do |f|
f.adapter Faraday::Adapter::Test do |stub|
stub.get("/test") do
[200, { "Content-Type" => "text/html" }, "<h1>hello world</h1>"]
end
end
end
end

let(:url) { "http://example.com" }

it "skips instrumentation" do
allow(Sentry).to receive(:initialized?).and_return(false)

response = http.get("/test")

expect(response.status).to eq(200)
end
end
end
Loading