sentry-ruby 5.4.2 → 5.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/Gemfile +13 -14
- data/README.md +11 -8
- data/Rakefile +8 -1
- data/lib/sentry/background_worker.rb +8 -1
- data/lib/sentry/backpressure_monitor.rb +75 -0
- data/lib/sentry/backtrace.rb +1 -1
- data/lib/sentry/baggage.rb +70 -0
- data/lib/sentry/breadcrumb.rb +8 -2
- data/lib/sentry/check_in_event.rb +60 -0
- data/lib/sentry/client.rb +77 -19
- data/lib/sentry/configuration.rb +177 -29
- data/lib/sentry/cron/configuration.rb +23 -0
- data/lib/sentry/cron/monitor_check_ins.rb +75 -0
- data/lib/sentry/cron/monitor_config.rb +53 -0
- data/lib/sentry/cron/monitor_schedule.rb +42 -0
- data/lib/sentry/envelope.rb +2 -5
- data/lib/sentry/event.rb +7 -29
- data/lib/sentry/hub.rb +100 -4
- data/lib/sentry/integrable.rb +6 -0
- data/lib/sentry/interfaces/request.rb +6 -16
- data/lib/sentry/interfaces/single_exception.rb +13 -3
- data/lib/sentry/net/http.rb +37 -46
- data/lib/sentry/profiler.rb +233 -0
- data/lib/sentry/propagation_context.rb +134 -0
- data/lib/sentry/puma.rb +32 -0
- data/lib/sentry/rack/capture_exceptions.rb +4 -5
- data/lib/sentry/rake.rb +1 -14
- data/lib/sentry/redis.rb +41 -23
- data/lib/sentry/release_detector.rb +1 -1
- data/lib/sentry/scope.rb +81 -16
- data/lib/sentry/session.rb +5 -7
- data/lib/sentry/span.rb +57 -10
- data/lib/sentry/test_helper.rb +19 -11
- data/lib/sentry/transaction.rb +183 -30
- data/lib/sentry/transaction_event.rb +51 -0
- data/lib/sentry/transport/configuration.rb +74 -1
- data/lib/sentry/transport/http_transport.rb +68 -37
- data/lib/sentry/transport/spotlight_transport.rb +50 -0
- data/lib/sentry/transport.rb +39 -24
- data/lib/sentry/utils/argument_checking_helper.rb +9 -3
- data/lib/sentry/utils/encoding_helper.rb +22 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +116 -41
- metadata +14 -3
- data/CODE_OF_CONDUCT.md +0 -74
| @@ -14,11 +14,19 @@ module Sentry | |
| 14 14 | 
             
                RATE_LIMIT_HEADER = "x-sentry-rate-limits"
         | 
| 15 15 | 
             
                USER_AGENT = "sentry-ruby/#{Sentry::VERSION}"
         | 
| 16 16 |  | 
| 17 | 
            +
                # The list of errors ::Net::HTTP is known to raise
         | 
| 18 | 
            +
                # See https://github.com/ruby/ruby/blob/b0c639f249165d759596f9579fa985cb30533de6/lib/bundler/fetcher.rb#L281-L286
         | 
| 19 | 
            +
                HTTP_ERRORS = [
         | 
| 20 | 
            +
                  Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
         | 
| 21 | 
            +
                  Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
         | 
| 22 | 
            +
                  Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
         | 
| 23 | 
            +
                  Zlib::BufError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED
         | 
| 24 | 
            +
                ].freeze
         | 
| 25 | 
            +
             | 
| 26 | 
            +
             | 
| 17 27 | 
             
                def initialize(*args)
         | 
| 18 28 | 
             
                  super
         | 
| 19 | 
            -
                  @ | 
| 20 | 
            -
             | 
| 21 | 
            -
                  log_debug("Sentry HTTP Transport will connect to #{@dsn.server}")
         | 
| 29 | 
            +
                  log_debug("Sentry HTTP Transport will connect to #{@dsn.server}") if @dsn
         | 
| 22 30 | 
             
                end
         | 
| 23 31 |  | 
| 24 32 | 
             
                def send_data(data)
         | 
| @@ -32,34 +40,76 @@ module Sentry | |
| 32 40 | 
             
                  headers = {
         | 
| 33 41 | 
             
                    'Content-Type' => CONTENT_TYPE,
         | 
| 34 42 | 
             
                    'Content-Encoding' => encoding,
         | 
| 35 | 
            -
                    'X-Sentry-Auth' => generate_auth_header,
         | 
| 36 43 | 
             
                    'User-Agent' => USER_AGENT
         | 
| 37 44 | 
             
                  }
         | 
| 38 45 |  | 
| 46 | 
            +
                  auth_header = generate_auth_header
         | 
| 47 | 
            +
                  headers['X-Sentry-Auth'] = auth_header if auth_header
         | 
| 48 | 
            +
             | 
| 39 49 | 
             
                  response = conn.start do |http|
         | 
| 40 | 
            -
                    request = ::Net::HTTP::Post.new( | 
| 50 | 
            +
                    request = ::Net::HTTP::Post.new(endpoint, headers)
         | 
| 41 51 | 
             
                    request.body = data
         | 
| 42 52 | 
             
                    http.request(request)
         | 
| 43 53 | 
             
                  end
         | 
| 44 54 |  | 
| 45 55 | 
             
                  if response.code.match?(/\A2\d{2}/)
         | 
| 46 | 
            -
                    if has_rate_limited_header?(response)
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                     | 
| 56 | 
            +
                    handle_rate_limited_response(response) if has_rate_limited_header?(response)
         | 
| 57 | 
            +
                  elsif response.code == "429"
         | 
| 58 | 
            +
                    log_debug("the server responded with status 429")
         | 
| 59 | 
            +
                    handle_rate_limited_response(response)
         | 
| 49 60 | 
             
                  else
         | 
| 50 61 | 
             
                    error_info = "the server responded with status #{response.code}"
         | 
| 62 | 
            +
                    error_info += "\nbody: #{response.body}"
         | 
| 63 | 
            +
                    error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    raise Sentry::ExternalError, error_info
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                rescue SocketError, *HTTP_ERRORS => e
         | 
| 68 | 
            +
                  on_error if respond_to?(:on_error)
         | 
| 69 | 
            +
                  raise Sentry::ExternalError.new(e&.message)
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                def endpoint
         | 
| 73 | 
            +
                  @dsn.envelope_endpoint
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                def generate_auth_header
         | 
| 77 | 
            +
                  return nil unless @dsn
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  now = Sentry.utc_now.to_i
         | 
| 80 | 
            +
                  fields = {
         | 
| 81 | 
            +
                    'sentry_version' => PROTOCOL_VERSION,
         | 
| 82 | 
            +
                    'sentry_client' => USER_AGENT,
         | 
| 83 | 
            +
                    'sentry_timestamp' => now,
         | 
| 84 | 
            +
                    'sentry_key' => @dsn.public_key
         | 
| 85 | 
            +
                  }
         | 
| 86 | 
            +
                  fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
         | 
| 87 | 
            +
                  'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
         | 
| 88 | 
            +
                end
         | 
| 51 89 |  | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 90 | 
            +
                def conn
         | 
| 91 | 
            +
                  server = URI(@dsn.server)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  # connection respects proxy setting from @transport_configuration, or environment variables (HTTP_PROXY, HTTPS_PROXY, NO_PROXY)
         | 
| 94 | 
            +
                  # Net::HTTP will automatically read the env vars.
         | 
| 95 | 
            +
                  # See https://ruby-doc.org/3.2.2/stdlibs/net/Net/HTTP.html#class-Net::HTTP-label-Proxies
         | 
| 96 | 
            +
                  connection =
         | 
| 97 | 
            +
                    if proxy = normalize_proxy(@transport_configuration.proxy)
         | 
| 98 | 
            +
                      ::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
         | 
| 54 99 | 
             
                    else
         | 
| 55 | 
            -
                       | 
| 56 | 
            -
                      error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
         | 
| 100 | 
            +
                      ::Net::HTTP.new(server.hostname, server.port)
         | 
| 57 101 | 
             
                    end
         | 
| 58 102 |  | 
| 59 | 
            -
             | 
| 103 | 
            +
                  connection.use_ssl = server.scheme == "https"
         | 
| 104 | 
            +
                  connection.read_timeout = @transport_configuration.timeout
         | 
| 105 | 
            +
                  connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
         | 
| 106 | 
            +
                  connection.open_timeout = @transport_configuration.open_timeout
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  ssl_configuration.each do |key, value|
         | 
| 109 | 
            +
                    connection.send("#{key}=", value)
         | 
| 60 110 | 
             
                  end
         | 
| 61 | 
            -
             | 
| 62 | 
            -
                   | 
| 111 | 
            +
             | 
| 112 | 
            +
                  connection
         | 
| 63 113 | 
             
                end
         | 
| 64 114 |  | 
| 65 115 | 
             
                private
         | 
| @@ -126,28 +176,9 @@ module Sentry | |
| 126 176 | 
             
                  @transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
         | 
| 127 177 | 
             
                end
         | 
| 128 178 |  | 
| 129 | 
            -
                 | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
                  connection =
         | 
| 133 | 
            -
                    if proxy = normalize_proxy(@transport_configuration.proxy)
         | 
| 134 | 
            -
                      ::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
         | 
| 135 | 
            -
                    else
         | 
| 136 | 
            -
                      ::Net::HTTP.new(server.hostname, server.port, nil)
         | 
| 137 | 
            -
                    end
         | 
| 138 | 
            -
             | 
| 139 | 
            -
                  connection.use_ssl = server.scheme == "https"
         | 
| 140 | 
            -
                  connection.read_timeout = @transport_configuration.timeout
         | 
| 141 | 
            -
                  connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
         | 
| 142 | 
            -
                  connection.open_timeout = @transport_configuration.open_timeout
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                  ssl_configuration.each do |key, value|
         | 
| 145 | 
            -
                    connection.send("#{key}=", value)
         | 
| 146 | 
            -
                  end
         | 
| 147 | 
            -
             | 
| 148 | 
            -
                  connection
         | 
| 149 | 
            -
                end
         | 
| 150 | 
            -
             | 
| 179 | 
            +
                # @param proxy [String, URI, Hash] Proxy config value passed into `config.transport`.
         | 
| 180 | 
            +
                #   Accepts either a URI formatted string, URI, or a hash with the `uri`, `user`, and `password` keys.
         | 
| 181 | 
            +
                # @return [Hash] Normalized proxy config that will be passed into `Net::HTTP`
         | 
| 151 182 | 
             
                def normalize_proxy(proxy)
         | 
| 152 183 | 
             
                  return proxy unless proxy
         | 
| 153 184 |  | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "net/http"
         | 
| 4 | 
            +
            require "zlib"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Sentry
         | 
| 7 | 
            +
              # Designed to just report events to Spotlight in development.
         | 
| 8 | 
            +
              class SpotlightTransport < HTTPTransport
         | 
| 9 | 
            +
                DEFAULT_SIDECAR_URL = "http://localhost:8969/stream"
         | 
| 10 | 
            +
                MAX_FAILED_REQUESTS = 3
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def initialize(configuration)
         | 
| 13 | 
            +
                  super
         | 
| 14 | 
            +
                  @sidecar_url = configuration.spotlight.is_a?(String) ? configuration.spotlight : DEFAULT_SIDECAR_URL
         | 
| 15 | 
            +
                  @failed = 0
         | 
| 16 | 
            +
                  @logged = false
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  log_debug("[Spotlight] initialized for url #{@sidecar_url}")
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def endpoint
         | 
| 22 | 
            +
                  "/stream"
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def send_data(data)
         | 
| 26 | 
            +
                  if @failed >= MAX_FAILED_REQUESTS
         | 
| 27 | 
            +
                    unless @logged
         | 
| 28 | 
            +
                      log_debug("[Spotlight] disabling because of too many request failures")
         | 
| 29 | 
            +
                      @logged = true
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    return
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  super
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def on_error
         | 
| 39 | 
            +
                  @failed += 1
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                # Similar to HTTPTransport connection, but does not support Proxy and SSL
         | 
| 43 | 
            +
                def conn
         | 
| 44 | 
            +
                  sidecar = URI(@sidecar_url)
         | 
| 45 | 
            +
                  connection = ::Net::HTTP.new(sidecar.hostname, sidecar.port, nil)
         | 
| 46 | 
            +
                  connection.use_ssl = false
         | 
| 47 | 
            +
                  connection
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
    
        data/lib/sentry/transport.rb
    CHANGED
    
    | @@ -18,7 +18,9 @@ module Sentry | |
| 18 18 | 
             
                  :network_error,
         | 
| 19 19 | 
             
                  :sample_rate,
         | 
| 20 20 | 
             
                  :before_send,
         | 
| 21 | 
            -
                  :event_processor
         | 
| 21 | 
            +
                  :event_processor,
         | 
| 22 | 
            +
                  :insufficient_data,
         | 
| 23 | 
            +
                  :backpressure
         | 
| 22 24 | 
             
                ]
         | 
| 23 25 |  | 
| 24 26 | 
             
                include LoggingHelper
         | 
| @@ -73,7 +75,7 @@ module Sentry | |
| 73 75 | 
             
                    result, oversized = item.serialize
         | 
| 74 76 |  | 
| 75 77 | 
             
                    if oversized
         | 
| 76 | 
            -
                       | 
| 78 | 
            +
                      log_debug("Envelope item [#{item.type}] is still oversized after size reduction: {#{item.size_breakdown}}")
         | 
| 77 79 |  | 
| 78 80 | 
             
                      next
         | 
| 79 81 | 
             
                    end
         | 
| @@ -118,16 +120,8 @@ module Sentry | |
| 118 120 | 
             
                  !!delay && delay > Time.now
         | 
| 119 121 | 
             
                end
         | 
| 120 122 |  | 
| 121 | 
            -
                def  | 
| 122 | 
            -
                   | 
| 123 | 
            -
                  fields = {
         | 
| 124 | 
            -
                    'sentry_version' => PROTOCOL_VERSION,
         | 
| 125 | 
            -
                    'sentry_client' => USER_AGENT,
         | 
| 126 | 
            -
                    'sentry_timestamp' => now,
         | 
| 127 | 
            -
                    'sentry_key' => @dsn.public_key
         | 
| 128 | 
            -
                  }
         | 
| 129 | 
            -
                  fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
         | 
| 130 | 
            -
                  'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
         | 
| 123 | 
            +
                def any_rate_limited?
         | 
| 124 | 
            +
                  @rate_limits.values.any? { |t| t && t > Time.now }
         | 
| 131 125 | 
             
                end
         | 
| 132 126 |  | 
| 133 127 | 
             
                def envelope_from_event(event)
         | 
| @@ -136,20 +130,31 @@ module Sentry | |
| 136 130 | 
             
                  event_id = event_payload[:event_id] || event_payload["event_id"]
         | 
| 137 131 | 
             
                  item_type = event_payload[:type] || event_payload["type"]
         | 
| 138 132 |  | 
| 139 | 
            -
                   | 
| 140 | 
            -
                     | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
             | 
| 146 | 
            -
                  )
         | 
| 133 | 
            +
                  envelope_headers = {
         | 
| 134 | 
            +
                    event_id: event_id,
         | 
| 135 | 
            +
                    dsn: @dsn.to_s,
         | 
| 136 | 
            +
                    sdk: Sentry.sdk_meta,
         | 
| 137 | 
            +
                    sent_at: Sentry.utc_now.iso8601
         | 
| 138 | 
            +
                  }
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                  if event.is_a?(Event) && event.dynamic_sampling_context
         | 
| 141 | 
            +
                    envelope_headers[:trace] = event.dynamic_sampling_context
         | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                  envelope = Envelope.new(envelope_headers)
         | 
| 147 145 |  | 
| 148 146 | 
             
                  envelope.add_item(
         | 
| 149 147 | 
             
                    { type: item_type, content_type: 'application/json' },
         | 
| 150 148 | 
             
                    event_payload
         | 
| 151 149 | 
             
                  )
         | 
| 152 150 |  | 
| 151 | 
            +
                  if event.is_a?(TransactionEvent) && event.profile
         | 
| 152 | 
            +
                    envelope.add_item(
         | 
| 153 | 
            +
                      { type: 'profile', content_type: 'application/json' },
         | 
| 154 | 
            +
                      event.profile
         | 
| 155 | 
            +
                    )
         | 
| 156 | 
            +
                  end
         | 
| 157 | 
            +
             | 
| 153 158 | 
             
                  client_report_headers, client_report_payload = fetch_pending_client_report
         | 
| 154 159 | 
             
                  envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
         | 
| 155 160 |  | 
| @@ -163,18 +168,27 @@ module Sentry | |
| 163 168 | 
             
                  @discarded_events[[reason, item_type]] += 1
         | 
| 164 169 | 
             
                end
         | 
| 165 170 |  | 
| 171 | 
            +
                def flush
         | 
| 172 | 
            +
                  client_report_headers, client_report_payload = fetch_pending_client_report(force: true)
         | 
| 173 | 
            +
                  return unless client_report_headers
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  envelope = Envelope.new
         | 
| 176 | 
            +
                  envelope.add_item(client_report_headers, client_report_payload)
         | 
| 177 | 
            +
                  send_envelope(envelope)
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
             | 
| 166 180 | 
             
                private
         | 
| 167 181 |  | 
| 168 | 
            -
                def fetch_pending_client_report
         | 
| 182 | 
            +
                def fetch_pending_client_report(force: false)
         | 
| 169 183 | 
             
                  return nil unless @send_client_reports
         | 
| 170 | 
            -
                  return nil if @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
         | 
| 184 | 
            +
                  return nil if !force && @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
         | 
| 171 185 | 
             
                  return nil if @discarded_events.empty?
         | 
| 172 186 |  | 
| 173 187 | 
             
                  discarded_events_hash = @discarded_events.map do |key, val|
         | 
| 174 188 | 
             
                    reason, type = key
         | 
| 175 189 |  | 
| 176 190 | 
             
                    # 'event' has to be mapped to 'error'
         | 
| 177 | 
            -
                    category = type == ' | 
| 191 | 
            +
                    category = type == 'event' ? 'error' : type
         | 
| 178 192 |  | 
| 179 193 | 
             
                    { reason: reason, category: category, quantity: val }
         | 
| 180 194 | 
             
                  end
         | 
| @@ -194,7 +208,7 @@ module Sentry | |
| 194 208 | 
             
                def reject_rate_limited_items(envelope)
         | 
| 195 209 | 
             
                  envelope.items.reject! do |item|
         | 
| 196 210 | 
             
                    if is_rate_limited?(item.type)
         | 
| 197 | 
            -
                       | 
| 211 | 
            +
                      log_debug("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
         | 
| 198 212 | 
             
                      record_lost_event(:ratelimit_backoff, item.type)
         | 
| 199 213 |  | 
| 200 214 | 
             
                      true
         | 
| @@ -208,3 +222,4 @@ end | |
| 208 222 |  | 
| 209 223 | 
             
            require "sentry/transport/dummy_transport"
         | 
| 210 224 | 
             
            require "sentry/transport/http_transport"
         | 
| 225 | 
            +
            require "sentry/transport/spotlight_transport"
         | 
| @@ -4,9 +4,15 @@ module Sentry | |
| 4 4 | 
             
              module ArgumentCheckingHelper
         | 
| 5 5 | 
             
                private
         | 
| 6 6 |  | 
| 7 | 
            -
                def check_argument_type!(argument,  | 
| 8 | 
            -
                  unless argument.is_a?( | 
| 9 | 
            -
                    raise ArgumentError, "expect the argument to be a #{ | 
| 7 | 
            +
                def check_argument_type!(argument, *expected_types)
         | 
| 8 | 
            +
                  unless expected_types.any? { |t| argument.is_a?(t) }
         | 
| 9 | 
            +
                    raise ArgumentError, "expect the argument to be a #{expected_types.join(' or ')}, got #{argument.class} (#{argument.inspect})"
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def check_argument_includes!(argument, values)
         | 
| 14 | 
            +
                  unless values.include?(argument)
         | 
| 15 | 
            +
                    raise ArgumentError, "expect the argument to be one of #{values.map(&:inspect).join(' or ')}, got #{argument.inspect}"
         | 
| 10 16 | 
             
                  end
         | 
| 11 17 | 
             
                end
         | 
| 12 18 | 
             
              end
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Sentry
         | 
| 4 | 
            +
              module Utils
         | 
| 5 | 
            +
                module EncodingHelper
         | 
| 6 | 
            +
                  def self.encode_to_utf_8(value)
         | 
| 7 | 
            +
                    if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
         | 
| 8 | 
            +
                      value = value.dup.force_encoding(Encoding::UTF_8)
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    value = value.scrub unless value.valid_encoding?
         | 
| 12 | 
            +
                    value
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def self.valid_utf_8?(value)
         | 
| 16 | 
            +
                    return true unless value.respond_to?(:force_encoding)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    value.dup.force_encoding(Encoding::UTF_8).valid_encoding?
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
    
        data/lib/sentry/version.rb
    CHANGED
    
    
    
        data/lib/sentry-ruby.rb
    CHANGED
    
    | @@ -8,17 +8,21 @@ require "sentry/version" | |
| 8 8 | 
             
            require "sentry/exceptions"
         | 
| 9 9 | 
             
            require "sentry/core_ext/object/deep_dup"
         | 
| 10 10 | 
             
            require "sentry/utils/argument_checking_helper"
         | 
| 11 | 
            +
            require "sentry/utils/encoding_helper"
         | 
| 11 12 | 
             
            require "sentry/utils/logging_helper"
         | 
| 12 13 | 
             
            require "sentry/configuration"
         | 
| 13 14 | 
             
            require "sentry/logger"
         | 
| 14 15 | 
             
            require "sentry/event"
         | 
| 15 16 | 
             
            require "sentry/error_event"
         | 
| 16 17 | 
             
            require "sentry/transaction_event"
         | 
| 18 | 
            +
            require "sentry/check_in_event"
         | 
| 17 19 | 
             
            require "sentry/span"
         | 
| 18 20 | 
             
            require "sentry/transaction"
         | 
| 19 21 | 
             
            require "sentry/hub"
         | 
| 20 22 | 
             
            require "sentry/background_worker"
         | 
| 21 23 | 
             
            require "sentry/session_flusher"
         | 
| 24 | 
            +
            require "sentry/backpressure_monitor"
         | 
| 25 | 
            +
            require "sentry/cron/monitor_check_ins"
         | 
| 22 26 |  | 
| 23 27 | 
             
            [
         | 
| 24 28 | 
             
              "sentry/rake",
         | 
| @@ -39,6 +43,8 @@ module Sentry | |
| 39 43 |  | 
| 40 44 | 
             
              SENTRY_TRACE_HEADER_NAME = "sentry-trace".freeze
         | 
| 41 45 |  | 
| 46 | 
            +
              BAGGAGE_HEADER_NAME = "baggage".freeze
         | 
| 47 | 
            +
             | 
| 42 48 | 
             
              THREAD_LOCAL = :sentry_hub
         | 
| 43 49 |  | 
| 44 50 | 
             
              class << self
         | 
| @@ -60,30 +66,44 @@ module Sentry | |
| 60 66 | 
             
                end
         | 
| 61 67 |  | 
| 62 68 | 
             
                # @!attribute [rw] background_worker
         | 
| 63 | 
            -
                #   @return [BackgroundWorker | 
| 69 | 
            +
                #   @return [BackgroundWorker]
         | 
| 64 70 | 
             
                attr_accessor :background_worker
         | 
| 65 71 |  | 
| 66 72 | 
             
                # @!attribute [r] session_flusher
         | 
| 67 73 | 
             
                #   @return [SessionFlusher, nil]
         | 
| 68 74 | 
             
                attr_reader :session_flusher
         | 
| 69 75 |  | 
| 76 | 
            +
                # @!attribute [r] backpressure_monitor
         | 
| 77 | 
            +
                #   @return [BackpressureMonitor, nil]
         | 
| 78 | 
            +
                attr_reader :backpressure_monitor
         | 
| 79 | 
            +
             | 
| 70 80 | 
             
                ##### Patch Registration #####
         | 
| 71 81 |  | 
| 72 82 | 
             
                # @!visibility private
         | 
| 73 | 
            -
                def register_patch(&block)
         | 
| 74 | 
            -
                   | 
| 83 | 
            +
                def register_patch(key, patch = nil, target = nil, &block)
         | 
| 84 | 
            +
                  if patch && block
         | 
| 85 | 
            +
                    raise ArgumentError.new("Please provide either a patch and its target OR a block, but not both")
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  if block
         | 
| 89 | 
            +
                    registered_patches[key] = block
         | 
| 90 | 
            +
                  else
         | 
| 91 | 
            +
                    registered_patches[key] = proc do
         | 
| 92 | 
            +
                      target.send(:prepend, patch) unless target.ancestors.include?(patch)
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
                  end
         | 
| 75 95 | 
             
                end
         | 
| 76 96 |  | 
| 77 97 | 
             
                # @!visibility private
         | 
| 78 98 | 
             
                def apply_patches(config)
         | 
| 79 | 
            -
                  registered_patches.each do |patch|
         | 
| 80 | 
            -
                    patch.call(config)
         | 
| 99 | 
            +
                  registered_patches.each do |key, patch|
         | 
| 100 | 
            +
                    patch.call(config) if config.enabled_patches.include?(key)
         | 
| 81 101 | 
             
                  end
         | 
| 82 102 | 
             
                end
         | 
| 83 103 |  | 
| 84 104 | 
             
                # @!visibility private
         | 
| 85 105 | 
             
                def registered_patches
         | 
| 86 | 
            -
                  @registered_patches ||=  | 
| 106 | 
            +
                  @registered_patches ||= {}
         | 
| 87 107 | 
             
                end
         | 
| 88 108 |  | 
| 89 109 | 
             
                ##### Integrations #####
         | 
| @@ -202,17 +222,9 @@ module Sentry | |
| 202 222 | 
             
                  Thread.current.thread_variable_set(THREAD_LOCAL, hub)
         | 
| 203 223 | 
             
                  @main_hub = hub
         | 
| 204 224 | 
             
                  @background_worker = Sentry::BackgroundWorker.new(config)
         | 
| 205 | 
            -
             | 
| 206 | 
            -
                  @ | 
| 207 | 
            -
             | 
| 208 | 
            -
                                     else
         | 
| 209 | 
            -
                                       nil
         | 
| 210 | 
            -
                                     end
         | 
| 211 | 
            -
             | 
| 212 | 
            -
                  if config.capture_exception_frame_locals
         | 
| 213 | 
            -
                    exception_locals_tp.enable
         | 
| 214 | 
            -
                  end
         | 
| 215 | 
            -
             | 
| 225 | 
            +
                  @session_flusher = config.auto_session_tracking ? Sentry::SessionFlusher.new(config, client) : nil
         | 
| 226 | 
            +
                  @backpressure_monitor = config.enable_backpressure_handling ? Sentry::BackpressureMonitor.new(config, client) : nil
         | 
| 227 | 
            +
                  exception_locals_tp.enable if config.include_local_variables
         | 
| 216 228 | 
             
                  at_exit { close }
         | 
| 217 229 | 
             
                end
         | 
| 218 230 |  | 
| @@ -221,20 +233,27 @@ module Sentry | |
| 221 233 | 
             
                #
         | 
| 222 234 | 
             
                # @return [void]
         | 
| 223 235 | 
             
                def close
         | 
| 224 | 
            -
                  if @background_worker
         | 
| 225 | 
            -
                    @background_worker.shutdown
         | 
| 226 | 
            -
                    @background_worker = nil
         | 
| 227 | 
            -
                  end
         | 
| 228 | 
            -
             | 
| 229 236 | 
             
                  if @session_flusher
         | 
| 237 | 
            +
                    @session_flusher.flush
         | 
| 230 238 | 
             
                    @session_flusher.kill
         | 
| 231 239 | 
             
                    @session_flusher = nil
         | 
| 232 240 | 
             
                  end
         | 
| 233 241 |  | 
| 234 | 
            -
                  if  | 
| 235 | 
            -
                     | 
| 242 | 
            +
                  if @backpressure_monitor
         | 
| 243 | 
            +
                    @backpressure_monitor.kill
         | 
| 244 | 
            +
                    @backpressure_monitor = nil
         | 
| 236 245 | 
             
                  end
         | 
| 237 246 |  | 
| 247 | 
            +
                  if client = get_current_client
         | 
| 248 | 
            +
                    client.transport.flush
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                    if client.configuration.include_local_variables
         | 
| 251 | 
            +
                      exception_locals_tp.disable
         | 
| 252 | 
            +
                    end
         | 
| 253 | 
            +
                  end
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                  @background_worker.shutdown
         | 
| 256 | 
            +
             | 
| 238 257 | 
             
                  @main_hub = nil
         | 
| 239 258 | 
             
                  Thread.current.thread_variable_set(THREAD_LOCAL, nil)
         | 
| 240 259 | 
             
                end
         | 
| @@ -348,7 +367,7 @@ module Sentry | |
| 348 367 | 
             
                # @yieldparam scope [Scope]
         | 
| 349 368 | 
             
                # @return [void]
         | 
| 350 369 | 
             
                def with_scope(&block)
         | 
| 351 | 
            -
                  return unless initialized?
         | 
| 370 | 
            +
                  return yield unless initialized?
         | 
| 352 371 | 
             
                  get_current_hub.with_scope(&block)
         | 
| 353 372 | 
             
                end
         | 
| 354 373 |  | 
| @@ -417,6 +436,22 @@ module Sentry | |
| 417 436 | 
             
                  get_current_hub.capture_event(event)
         | 
| 418 437 | 
             
                end
         | 
| 419 438 |  | 
| 439 | 
            +
                # Captures a check-in and sends it to Sentry via the currently active hub.
         | 
| 440 | 
            +
                #
         | 
| 441 | 
            +
                # @param slug [String] identifier of this monitor
         | 
| 442 | 
            +
                # @param status [Symbol] status of this check-in, one of {CheckInEvent::VALID_STATUSES}
         | 
| 443 | 
            +
                #
         | 
| 444 | 
            +
                # @param [Hash] options extra check-in options
         | 
| 445 | 
            +
                # @option options [String] check_in_id for updating the status of an existing monitor
         | 
| 446 | 
            +
                # @option options [Integer] duration seconds elapsed since this monitor started
         | 
| 447 | 
            +
                # @option options [Cron::MonitorConfig] monitor_config configuration for this monitor
         | 
| 448 | 
            +
                #
         | 
| 449 | 
            +
                # @return [String, nil] The {CheckInEvent#check_in_id} to use for later updates on the same slug
         | 
| 450 | 
            +
                def capture_check_in(slug, status, **options)
         | 
| 451 | 
            +
                  return unless initialized?
         | 
| 452 | 
            +
                  get_current_hub.capture_check_in(slug, status, **options)
         | 
| 453 | 
            +
                end
         | 
| 454 | 
            +
             | 
| 420 455 | 
             
                # Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
         | 
| 421 456 | 
             
                #
         | 
| 422 457 | 
             
                # @return [Transaction, nil]
         | 
| @@ -439,22 +474,8 @@ module Sentry | |
| 439 474 | 
             
                #   end
         | 
| 440 475 | 
             
                #
         | 
| 441 476 | 
             
                def with_child_span(**attributes, &block)
         | 
| 442 | 
            -
                   | 
| 443 | 
            -
             | 
| 444 | 
            -
             | 
| 445 | 
            -
                    begin
         | 
| 446 | 
            -
                      current_span.with_child_span(**attributes) do |child_span|
         | 
| 447 | 
            -
                        get_current_scope.set_span(child_span)
         | 
| 448 | 
            -
                        result = yield(child_span)
         | 
| 449 | 
            -
                      end
         | 
| 450 | 
            -
                    ensure
         | 
| 451 | 
            -
                      get_current_scope.set_span(current_span)
         | 
| 452 | 
            -
                    end
         | 
| 453 | 
            -
             | 
| 454 | 
            -
                    result
         | 
| 455 | 
            -
                  else
         | 
| 456 | 
            -
                    yield(nil)
         | 
| 457 | 
            -
                  end
         | 
| 477 | 
            +
                  return yield(nil) unless Sentry.initialized?
         | 
| 478 | 
            +
                  get_current_hub.with_child_span(**attributes, &block)
         | 
| 458 479 | 
             
                end
         | 
| 459 480 |  | 
| 460 481 | 
             
                # Returns the id of the lastly reported Sentry::Event.
         | 
| @@ -473,6 +494,59 @@ module Sentry | |
| 473 494 | 
             
                  !!exc.instance_variable_get(CAPTURED_SIGNATURE)
         | 
| 474 495 | 
             
                end
         | 
| 475 496 |  | 
| 497 | 
            +
                # Add a global event processor [Proc].
         | 
| 498 | 
            +
                # These run before scope event processors.
         | 
| 499 | 
            +
                #
         | 
| 500 | 
            +
                # @yieldparam event [Event]
         | 
| 501 | 
            +
                # @yieldparam hint [Hash, nil]
         | 
| 502 | 
            +
                # @return [void]
         | 
| 503 | 
            +
                #
         | 
| 504 | 
            +
                # @example
         | 
| 505 | 
            +
                #   Sentry.add_global_event_processor do |event, hint|
         | 
| 506 | 
            +
                #     event.tags = { foo: 42 }
         | 
| 507 | 
            +
                #     event
         | 
| 508 | 
            +
                #   end
         | 
| 509 | 
            +
                #
         | 
| 510 | 
            +
                def add_global_event_processor(&block)
         | 
| 511 | 
            +
                  Scope.add_global_event_processor(&block)
         | 
| 512 | 
            +
                end
         | 
| 513 | 
            +
             | 
| 514 | 
            +
                # Returns the traceparent (sentry-trace) header for distributed tracing.
         | 
| 515 | 
            +
                # Can be either from the currently active span or the propagation context.
         | 
| 516 | 
            +
                #
         | 
| 517 | 
            +
                # @return [String, nil]
         | 
| 518 | 
            +
                def get_traceparent
         | 
| 519 | 
            +
                  return nil unless initialized?
         | 
| 520 | 
            +
                  get_current_hub.get_traceparent
         | 
| 521 | 
            +
                end
         | 
| 522 | 
            +
             | 
| 523 | 
            +
                # Returns the baggage header for distributed tracing.
         | 
| 524 | 
            +
                # Can be either from the currently active span or the propagation context.
         | 
| 525 | 
            +
                #
         | 
| 526 | 
            +
                # @return [String, nil]
         | 
| 527 | 
            +
                def get_baggage
         | 
| 528 | 
            +
                  return nil unless initialized?
         | 
| 529 | 
            +
                  get_current_hub.get_baggage
         | 
| 530 | 
            +
                end
         | 
| 531 | 
            +
             | 
| 532 | 
            +
                # Returns the a Hash containing sentry-trace and baggage.
         | 
| 533 | 
            +
                # Can be either from the currently active span or the propagation context.
         | 
| 534 | 
            +
                #
         | 
| 535 | 
            +
                # @return [Hash, nil]
         | 
| 536 | 
            +
                def get_trace_propagation_headers
         | 
| 537 | 
            +
                  return nil unless initialized?
         | 
| 538 | 
            +
                  get_current_hub.get_trace_propagation_headers
         | 
| 539 | 
            +
                end
         | 
| 540 | 
            +
             | 
| 541 | 
            +
                # Continue an incoming trace from a rack env like hash.
         | 
| 542 | 
            +
                #
         | 
| 543 | 
            +
                # @param env [Hash]
         | 
| 544 | 
            +
                # @return [Transaction, nil]
         | 
| 545 | 
            +
                def continue_trace(env, **options)
         | 
| 546 | 
            +
                  return nil unless initialized?
         | 
| 547 | 
            +
                  get_current_hub.continue_trace(env, **options)
         | 
| 548 | 
            +
                end
         | 
| 549 | 
            +
             | 
| 476 550 | 
             
                ##### Helpers #####
         | 
| 477 551 |  | 
| 478 552 | 
             
                # @!visibility private
         | 
| @@ -503,3 +577,4 @@ end | |
| 503 577 | 
             
            # patches
         | 
| 504 578 | 
             
            require "sentry/net/http"
         | 
| 505 579 | 
             
            require "sentry/redis"
         | 
| 580 | 
            +
            require "sentry/puma"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: sentry-ruby
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 5. | 
| 4 | 
            +
              version: 5.16.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Sentry Team
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2024-01-09 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: concurrent-ruby
         | 
| @@ -42,7 +42,6 @@ files: | |
| 42 42 | 
             
            - ".rspec"
         | 
| 43 43 | 
             
            - ".yardopts"
         | 
| 44 44 | 
             
            - CHANGELOG.md
         | 
| 45 | 
            -
            - CODE_OF_CONDUCT.md
         | 
| 46 45 | 
             
            - Gemfile
         | 
| 47 46 | 
             
            - LICENSE.txt
         | 
| 48 47 | 
             
            - Makefile
         | 
| @@ -52,14 +51,21 @@ files: | |
| 52 51 | 
             
            - bin/setup
         | 
| 53 52 | 
             
            - lib/sentry-ruby.rb
         | 
| 54 53 | 
             
            - lib/sentry/background_worker.rb
         | 
| 54 | 
            +
            - lib/sentry/backpressure_monitor.rb
         | 
| 55 55 | 
             
            - lib/sentry/backtrace.rb
         | 
| 56 | 
            +
            - lib/sentry/baggage.rb
         | 
| 56 57 | 
             
            - lib/sentry/breadcrumb.rb
         | 
| 57 58 | 
             
            - lib/sentry/breadcrumb/sentry_logger.rb
         | 
| 58 59 | 
             
            - lib/sentry/breadcrumb_buffer.rb
         | 
| 60 | 
            +
            - lib/sentry/check_in_event.rb
         | 
| 59 61 | 
             
            - lib/sentry/client.rb
         | 
| 60 62 | 
             
            - lib/sentry/configuration.rb
         | 
| 61 63 | 
             
            - lib/sentry/core_ext/object/deep_dup.rb
         | 
| 62 64 | 
             
            - lib/sentry/core_ext/object/duplicable.rb
         | 
| 65 | 
            +
            - lib/sentry/cron/configuration.rb
         | 
| 66 | 
            +
            - lib/sentry/cron/monitor_check_ins.rb
         | 
| 67 | 
            +
            - lib/sentry/cron/monitor_config.rb
         | 
| 68 | 
            +
            - lib/sentry/cron/monitor_schedule.rb
         | 
| 63 69 | 
             
            - lib/sentry/dsn.rb
         | 
| 64 70 | 
             
            - lib/sentry/envelope.rb
         | 
| 65 71 | 
             
            - lib/sentry/error_event.rb
         | 
| @@ -77,6 +83,9 @@ files: | |
| 77 83 | 
             
            - lib/sentry/linecache.rb
         | 
| 78 84 | 
             
            - lib/sentry/logger.rb
         | 
| 79 85 | 
             
            - lib/sentry/net/http.rb
         | 
| 86 | 
            +
            - lib/sentry/profiler.rb
         | 
| 87 | 
            +
            - lib/sentry/propagation_context.rb
         | 
| 88 | 
            +
            - lib/sentry/puma.rb
         | 
| 80 89 | 
             
            - lib/sentry/rack.rb
         | 
| 81 90 | 
             
            - lib/sentry/rack/capture_exceptions.rb
         | 
| 82 91 | 
             
            - lib/sentry/rake.rb
         | 
| @@ -93,8 +102,10 @@ files: | |
| 93 102 | 
             
            - lib/sentry/transport/configuration.rb
         | 
| 94 103 | 
             
            - lib/sentry/transport/dummy_transport.rb
         | 
| 95 104 | 
             
            - lib/sentry/transport/http_transport.rb
         | 
| 105 | 
            +
            - lib/sentry/transport/spotlight_transport.rb
         | 
| 96 106 | 
             
            - lib/sentry/utils/argument_checking_helper.rb
         | 
| 97 107 | 
             
            - lib/sentry/utils/custom_inspection.rb
         | 
| 108 | 
            +
            - lib/sentry/utils/encoding_helper.rb
         | 
| 98 109 | 
             
            - lib/sentry/utils/exception_cause_chain.rb
         | 
| 99 110 | 
             
            - lib/sentry/utils/logging_helper.rb
         | 
| 100 111 | 
             
            - lib/sentry/utils/real_ip.rb
         |