implementation of the sentry plugin integration

This commit is contained in:
HoneyryderChuck 2022-05-27 13:08:16 +01:00
parent 281ac03cda
commit 7383347147
3 changed files with 112 additions and 5 deletions

View File

@ -16,6 +16,7 @@ group :test do
gem "minitest"
gem "minitest-proveit"
gem "ruby-ntlm"
gem "sentry-ruby" if RUBY_VERSION >= "2.4"
gem "spy"
gem "webmock"
gem "websocket-driver"

View File

@ -4,8 +4,10 @@ require "logger"
require "stringio"
require "test_helper"
require "support/http_helpers"
require "sentry-ruby"
require "httpx/adapters/sentry"
begin
require "httpx/adapters/sentry"
rescue LoadError
end
class SentryTest < Minitest::Test
include HTTPHelpers
@ -50,6 +52,21 @@ class SentryTest < Minitest::Test
end
end
def test_sentry_post_request
before_pii = Sentry.configuration.send_default_pii
begin
Sentry.configuration.send_default_pii = true
transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)
response = HTTPX.post(build_uri("/post"), form: { foo: "bar" })
verify_status(response, 200)
verify_spans(transaction, response, verb: "POST")
ensure
Sentry.configuration.send_default_pii = before_pii
end
end
def test_sentry_multiple_requests
transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)
@ -62,7 +79,7 @@ class SentryTest < Minitest::Test
private
def verify_spans(transaction, *responses)
def verify_spans(transaction, *responses, verb: nil, description: nil)
assert transaction.span_recorder.spans.count == responses.size + 1
assert transaction.span_recorder.spans[0] == transaction
@ -74,7 +91,7 @@ class SentryTest < Minitest::Test
assert !request_span.start_timestamp.nil?
assert !request_span.timestamp.nil?
assert request_span.start_timestamp != request_span.timestamp
assert request_span.description == response.uri
assert request_span.description == (description || "#{verb || "GET"} #{response.uri}")
assert request_span.data == { status: response.status }
end
end
@ -98,4 +115,4 @@ class SentryTest < Minitest::Test
def origin
"https://#{httpbin}"
end
end
end if RUBY_VERSION >= "2.4.0"

View File

@ -1,7 +1,96 @@
# frozen_string_literal: true
require "sentry-ruby"
module HTTPX::Plugins
module Sentry
module Tracer
module_function
def call(request)
sentry_span = start_sentry_span
return unless sentry_span
set_sentry_trace_header(request, sentry_span)
request.on(:response, &method(:finish_sentry_span).curry(3)[sentry_span, request])
end
def start_sentry_span
return unless ::Sentry.initialized? && (span = ::Sentry.get_current_scope.get_span)
return if span.sampled == false
span.start_child(op: "httpx.client", start_timestamp: ::Sentry.utc_now.to_f)
end
def set_sentry_trace_header(request, sentry_span)
return unless sentry_span
trace = ::Sentry.get_current_client.generate_sentry_trace(sentry_span)
request.headers[::Sentry::SENTRY_TRACE_HEADER_NAME] = trace if trace
end
def finish_sentry_span(span, request, response)
return unless ::Sentry.initialized?
record_sentry_breadcrumb(request, response)
record_sentry_span(request, response, span)
end
def record_sentry_breadcrumb(req, res)
return unless ::Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
request_info = extract_request_info(req)
data = if response.is_a?(HTTPX::ErrorResponse)
{ error: res.message, **request_info }
else
{ status: res.status, **request_info }
end
crumb = ::Sentry::Breadcrumb.new(
level: :info,
category: "httpx",
type: :info,
data: data
)
::Sentry.add_breadcrumb(crumb)
end
def record_sentry_span(req, res, sentry_span)
return unless sentry_span
request_info = extract_request_info(req)
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
sentry_span.set_data(:status, res.status)
sentry_span.set_timestamp(::Sentry.utc_now.to_f)
end
def extract_request_info(req)
uri = req.uri
result = {
method: req.verb.to_s.upcase,
}
if ::Sentry.configuration.send_default_pii
uri += "?#{req.query}" unless req.query.empty?
result[:body] = req.body.to_s unless req.body.empty? || req.body.unbounded_body?
end
result[:url] = uri.to_s
result
end
end
module ConnectionMethods
def send(request)
Tracer.call(request)
super
end
end
end
end