File: sentry.rb

package info (click to toggle)
ruby-httpx 1.7.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,816 kB
  • sloc: ruby: 12,209; makefile: 4
file content (121 lines) | stat: -rw-r--r-- 3,211 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# 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

        config = ::Sentry.configuration
        url = request.uri.to_s

        return unless config.propagate_traces && config.trace_propagation_targets.any? { |target| url.match?(target) }

        trace = sentry_span.to_sentry_trace
        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 res.is_a?(HTTPX::ErrorResponse)
          { error: res.error.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]}")
        if res.is_a?(HTTPX::ErrorResponse)
          sentry_span.set_data(:error, res.error.message)
        else
          sentry_span.set_data(:status, res.status)
        end
        sentry_span.set_timestamp(::Sentry.utc_now.to_f)
      end

      def extract_request_info(req)
        uri = req.uri

        result = {
          method: req.verb,
        }

        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 RequestMethods
      def __sentry_enable_trace!
        return if @__sentry_enable_trace

        Tracer.call(self)
        @__sentry_enable_trace = true
      end
    end

    module ConnectionMethods
      def send(request)
        request.__sentry_enable_trace!

        super
      end
    end
  end
end

Sentry.register_patch(:httpx) do
  sentry_session = HTTPX.plugin(HTTPX::Plugins::Sentry)

  HTTPX.send(:remove_const, :Session)
  HTTPX.send(:const_set, :Session, sentry_session.class)
end