sentry-ruby-core 4.4.2 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +2 -0
  4. data/Gemfile +8 -5
  5. data/LICENSE.txt +1 -1
  6. data/README.md +29 -175
  7. data/bin/console +5 -1
  8. data/lib/sentry/background_worker.rb +33 -3
  9. data/lib/sentry/backtrace.rb +1 -3
  10. data/lib/sentry/breadcrumb/sentry_logger.rb +3 -1
  11. data/lib/sentry/breadcrumb.rb +28 -2
  12. data/lib/sentry/breadcrumb_buffer.rb +16 -0
  13. data/lib/sentry/client.rb +65 -7
  14. data/lib/sentry/configuration.rb +155 -112
  15. data/lib/sentry/core_ext/object/deep_dup.rb +4 -0
  16. data/lib/sentry/core_ext/object/duplicable.rb +2 -0
  17. data/lib/sentry/dsn.rb +6 -1
  18. data/lib/sentry/envelope.rb +26 -0
  19. data/lib/sentry/event.rb +65 -23
  20. data/lib/sentry/exceptions.rb +2 -0
  21. data/lib/sentry/hub.rb +31 -5
  22. data/lib/sentry/integrable.rb +2 -0
  23. data/lib/sentry/interface.rb +3 -10
  24. data/lib/sentry/interfaces/exception.rb +13 -3
  25. data/lib/sentry/interfaces/request.rb +49 -19
  26. data/lib/sentry/interfaces/single_exception.rb +31 -0
  27. data/lib/sentry/interfaces/stacktrace.rb +14 -0
  28. data/lib/sentry/interfaces/stacktrace_builder.rb +39 -10
  29. data/lib/sentry/interfaces/threads.rb +12 -2
  30. data/lib/sentry/linecache.rb +3 -0
  31. data/lib/sentry/net/http.rb +71 -47
  32. data/lib/sentry/rack/capture_exceptions.rb +2 -0
  33. data/lib/sentry/rack.rb +2 -1
  34. data/lib/sentry/rake.rb +33 -9
  35. data/lib/sentry/release_detector.rb +39 -0
  36. data/lib/sentry/scope.rb +76 -6
  37. data/lib/sentry/span.rb +84 -8
  38. data/lib/sentry/transaction.rb +48 -10
  39. data/lib/sentry/transaction_event.rb +19 -6
  40. data/lib/sentry/transport/configuration.rb +4 -2
  41. data/lib/sentry/transport/dummy_transport.rb +2 -0
  42. data/lib/sentry/transport/http_transport.rb +57 -38
  43. data/lib/sentry/transport.rb +80 -19
  44. data/lib/sentry/utils/argument_checking_helper.rb +2 -0
  45. data/lib/sentry/utils/custom_inspection.rb +14 -0
  46. data/lib/sentry/utils/exception_cause_chain.rb +10 -10
  47. data/lib/sentry/utils/logging_helper.rb +6 -4
  48. data/lib/sentry/utils/real_ip.rb +9 -1
  49. data/lib/sentry/utils/request_id.rb +2 -0
  50. data/lib/sentry/version.rb +3 -1
  51. data/lib/sentry-ruby.rb +184 -49
  52. data/sentry-ruby-core.gemspec +2 -3
  53. data/sentry-ruby.gemspec +2 -3
  54. metadata +9 -22
  55. data/.craft.yml +0 -28
  56. data/lib/sentry/benchmarks/benchmark_transport.rb +0 -14
  57. data/lib/sentry/rack/deprecations.rb +0 -19
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class Transaction < Span
3
5
  SENTRY_TRACE_REGEXP = Regexp.new(
@@ -12,7 +14,22 @@ module Sentry
12
14
 
13
15
  include LoggingHelper
14
16
 
15
- attr_reader :name, :parent_sampled, :hub, :configuration, :logger
17
+ # The name of the transaction.
18
+ # @return [String]
19
+ attr_reader :name
20
+
21
+ # The sampling decision of the parent transaction, which will be considered when making the current transaction's sampling decision.
22
+ # @return [String]
23
+ attr_reader :parent_sampled
24
+
25
+ # @deprecated Use Sentry.get_current_hub instead.
26
+ attr_reader :hub
27
+
28
+ # @deprecated Use Sentry.configuration instead.
29
+ attr_reader :configuration
30
+
31
+ # @deprecated Use Sentry.logger instead.
32
+ attr_reader :logger
16
33
 
17
34
  def initialize(name: nil, parent_sampled: nil, hub:, **options)
18
35
  super(**options)
@@ -21,11 +38,23 @@ module Sentry
21
38
  @parent_sampled = parent_sampled
22
39
  @transaction = self
23
40
  @hub = hub
24
- @configuration = hub.configuration
25
- @logger = configuration.logger
41
+ @configuration = hub.configuration # to be removed
42
+ @tracing_enabled = hub.configuration.tracing_enabled?
43
+ @traces_sampler = hub.configuration.traces_sampler
44
+ @traces_sample_rate = hub.configuration.traces_sample_rate
45
+ @logger = hub.configuration.logger
26
46
  init_span_recorder
27
47
  end
28
48
 
49
+ # Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
50
+ #
51
+ # The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
52
+ #
53
+ # The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
54
+ # @param sentry_trace [String] the trace string from the previous transaction.
55
+ # @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
56
+ # @param options [Hash] the options you want to use to initialize a Transaction instance.
57
+ # @return [Transaction, nil]
29
58
  def self.from_sentry_trace(sentry_trace, hub: Sentry.get_current_hub, **options)
30
59
  return unless hub.configuration.tracing_enabled?
31
60
  return unless sentry_trace
@@ -44,12 +73,14 @@ module Sentry
44
73
  new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: parent_sampled, hub: hub, **options)
45
74
  end
46
75
 
76
+ # @return [Hash]
47
77
  def to_hash
48
78
  hash = super
49
79
  hash.merge!(name: @name, sampled: @sampled, parent_sampled: @parent_sampled)
50
80
  hash
51
81
  end
52
82
 
83
+ # @return [Transaction]
53
84
  def deep_dup
54
85
  copy = super
55
86
  copy.init_span_recorder(@span_recorder.max_length)
@@ -63,23 +94,24 @@ module Sentry
63
94
  copy
64
95
  end
65
96
 
97
+ # Sets initial sampling decision of the transaction.
98
+ # @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided).
99
+ # @return [void]
66
100
  def set_initial_sample_decision(sampling_context:)
67
- unless configuration.tracing_enabled?
101
+ unless @tracing_enabled
68
102
  @sampled = false
69
103
  return
70
104
  end
71
105
 
72
106
  return unless @sampled.nil?
73
107
 
74
- traces_sampler = configuration.traces_sampler
75
-
76
108
  sample_rate =
77
- if traces_sampler.is_a?(Proc)
78
- traces_sampler.call(sampling_context)
109
+ if @traces_sampler.is_a?(Proc)
110
+ @traces_sampler.call(sampling_context)
79
111
  elsif !sampling_context[:parent_sampled].nil?
80
112
  sampling_context[:parent_sampled]
81
113
  else
82
- configuration.traces_sample_rate
114
+ @traces_sample_rate
83
115
  end
84
116
 
85
117
  transaction_description = generate_transaction_description
@@ -111,6 +143,9 @@ module Sentry
111
143
  end
112
144
  end
113
145
 
146
+ # Finishes the transaction's recording and send it to Sentry.
147
+ # @param hub [Hub] the hub that'll send this transaction. (Deprecated)
148
+ # @return [TransactionEvent]
114
149
  def finish(hub: nil)
115
150
  if hub
116
151
  log_warn(
@@ -129,7 +164,10 @@ module Sentry
129
164
  @name = UNLABELD_NAME
130
165
  end
131
166
 
132
- return unless @sampled || @parent_sampled
167
+ unless @sampled || @parent_sampled
168
+ hub.current_client.transport.record_lost_event(:sample_rate, 'transaction')
169
+ return
170
+ end
133
171
 
134
172
  event = hub.current_client.event_from_transaction(self)
135
173
  hub.capture_event(event)
@@ -4,24 +4,37 @@ module Sentry
4
4
  class TransactionEvent < Event
5
5
  TYPE = "transaction"
6
6
 
7
- ATTRIBUTES = %i(
7
+ SERIALIZEABLE_ATTRIBUTES = %i(
8
8
  event_id level timestamp start_timestamp
9
9
  release environment server_name modules
10
10
  user tags contexts extra
11
11
  transaction platform sdk type
12
12
  )
13
13
 
14
- attr_accessor(*ATTRIBUTES)
14
+ WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp start_timestamp level)
15
+
16
+ attr_writer(*WRITER_ATTRIBUTES)
17
+ attr_reader(*SERIALIZEABLE_ATTRIBUTES)
18
+
19
+ # @return [<Array[Span]>]
15
20
  attr_accessor :spans
16
21
 
17
- def start_timestamp=(time)
18
- @start_timestamp = time.is_a?(Time) ? time.to_f : time
22
+ # @param configuration [Configuration]
23
+ # @param integration_meta [Hash, nil]
24
+ # @param message [String, nil]
25
+ def initialize(configuration:, integration_meta: nil, message: nil)
26
+ super
27
+ @type = TYPE
19
28
  end
20
29
 
21
- def type
22
- TYPE
30
+ # Sets the event's start_timestamp.
31
+ # @param time [Time, Float]
32
+ # @return [void]
33
+ def start_timestamp=(time)
34
+ @start_timestamp = time.is_a?(Time) ? time.to_f : time
23
35
  end
24
36
 
37
+ # @return [Hash]
25
38
  def to_hash
26
39
  data = super
27
40
  data[:spans] = @spans.map(&:to_hash) if @spans
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class Transport
3
5
  class Configuration
4
- attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :http_adapter, :faraday_builder,
5
- :transport_class, :encoding
6
+ attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :encoding
7
+ attr_reader :transport_class
6
8
 
7
9
  def initialize
8
10
  @ssl_verification = true
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class DummyTransport < Transport
3
5
  attr_accessor :events
@@ -1,5 +1,7 @@
1
- require 'faraday'
2
- require 'zlib'
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "zlib"
3
5
 
4
6
  module Sentry
5
7
  class HTTPTransport < Transport
@@ -10,12 +12,12 @@ module Sentry
10
12
  DEFAULT_DELAY = 60
11
13
  RETRY_AFTER_HEADER = "retry-after"
12
14
  RATE_LIMIT_HEADER = "x-sentry-rate-limits"
15
+ USER_AGENT = "sentry-ruby/#{Sentry::VERSION}"
13
16
 
14
- attr_reader :conn, :adapter
17
+ attr_reader :conn
15
18
 
16
19
  def initialize(*args)
17
20
  super
18
- @adapter = @transport_configuration.http_adapter || Faraday.default_adapter
19
21
  @conn = set_conn
20
22
  @endpoint = @dsn.envelope_endpoint
21
23
  end
@@ -28,29 +30,37 @@ module Sentry
28
30
  encoding = GZIP_ENCODING
29
31
  end
30
32
 
31
- response = conn.post @endpoint do |req|
32
- req.headers['Content-Type'] = CONTENT_TYPE
33
- req.headers['Content-Encoding'] = encoding
34
- req.headers['X-Sentry-Auth'] = generate_auth_header
35
- req.body = data
33
+ headers = {
34
+ 'Content-Type' => CONTENT_TYPE,
35
+ 'Content-Encoding' => encoding,
36
+ 'X-Sentry-Auth' => generate_auth_header,
37
+ 'User-Agent' => USER_AGENT
38
+ }
39
+
40
+ response = conn.start do |http|
41
+ request = ::Net::HTTP::Post.new(@endpoint, headers)
42
+ request.body = data
43
+ http.request(request)
36
44
  end
37
45
 
38
- if has_rate_limited_header?(response.headers)
39
- handle_rate_limited_response(response.headers)
40
- end
41
- rescue Faraday::Error => e
42
- error_info = e.message
46
+ if response.code.match?(/\A2\d{2}/)
47
+ if has_rate_limited_header?(response)
48
+ handle_rate_limited_response(response)
49
+ end
50
+ else
51
+ error_info = "the server responded with status #{response.code}"
43
52
 
44
- if e.response
45
- if e.response[:status] == 429
46
- handle_rate_limited_response(e.response[:headers])
53
+ if response.code == "429"
54
+ handle_rate_limited_response(response)
47
55
  else
48
- error_info += "\nbody: #{e.response[:body]}"
49
- error_info += " Error in headers is: #{e.response[:headers]['x-sentry-error']}" if e.response[:headers]['x-sentry-error']
56
+ error_info += "\nbody: #{response.body}"
57
+ error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
50
58
  end
51
- end
52
59
 
53
- raise Sentry::ExternalError, error_info
60
+ raise Sentry::ExternalError, error_info
61
+ end
62
+ rescue SocketError => e
63
+ raise Sentry::ExternalError.new(e.message)
54
64
  end
55
65
 
56
66
  private
@@ -118,31 +128,40 @@ module Sentry
118
128
  end
119
129
 
120
130
  def set_conn
121
- server = @dsn.server
131
+ server = URI(@dsn.server)
122
132
 
123
133
  log_debug("Sentry HTTP Transport connecting to #{server}")
124
134
 
125
- Faraday.new(server, :ssl => ssl_configuration, :proxy => @transport_configuration.proxy) do |builder|
126
- @transport_configuration.faraday_builder&.call(builder)
127
- builder.response :raise_error
128
- builder.options.merge! faraday_opts
129
- builder.headers[:user_agent] = "sentry-ruby/#{Sentry::VERSION}"
130
- builder.adapter(*adapter)
131
- end
132
- end
135
+ use_ssl = server.scheme == "https"
136
+ port = use_ssl ? 443 : 80
133
137
 
134
- # TODO: deprecate and replace where possible w/Faraday Builder
135
- def faraday_opts
136
- [:timeout, :open_timeout].each_with_object({}) do |opt, memo|
137
- memo[opt] = @transport_configuration.public_send(opt) if @transport_configuration.public_send(opt)
138
+ connection =
139
+ if proxy = @transport_configuration.proxy
140
+ ::Net::HTTP.new(server.hostname, port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
141
+ else
142
+ ::Net::HTTP.new(server.hostname, port, nil)
143
+ end
144
+
145
+ connection.use_ssl = use_ssl
146
+ connection.read_timeout = @transport_configuration.timeout
147
+ connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
148
+ connection.open_timeout = @transport_configuration.open_timeout
149
+
150
+ ssl_configuration.each do |key, value|
151
+ connection.send("#{key}=", value)
138
152
  end
153
+
154
+ connection
139
155
  end
140
156
 
141
157
  def ssl_configuration
142
- (@transport_configuration.ssl || {}).merge(
143
- :verify => @transport_configuration.ssl_verification,
144
- :ca_file => @transport_configuration.ssl_ca_file
145
- )
158
+ configuration = {
159
+ verify: @transport_configuration.ssl_verification,
160
+ ca_file: @transport_configuration.ssl_ca_file
161
+ }.merge(@transport_configuration.ssl || {})
162
+
163
+ configuration[:verify_mode] = configuration.delete(:verify) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
164
+ configuration
146
165
  end
147
166
  end
148
167
  end
@@ -1,22 +1,44 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
  require "base64"
5
+ require "sentry/envelope"
3
6
 
4
7
  module Sentry
5
8
  class Transport
6
9
  PROTOCOL_VERSION = '7'
7
10
  USER_AGENT = "sentry-ruby/#{Sentry::VERSION}"
11
+ CLIENT_REPORT_INTERVAL = 30
12
+
13
+ # https://develop.sentry.dev/sdk/client-reports/#envelope-item-payload
14
+ CLIENT_REPORT_REASONS = [
15
+ :ratelimit_backoff,
16
+ :queue_overflow,
17
+ :cache_overflow, # NA
18
+ :network_error,
19
+ :sample_rate,
20
+ :before_send,
21
+ :event_processor
22
+ ]
8
23
 
9
24
  include LoggingHelper
10
25
 
11
- attr_accessor :configuration
12
- attr_reader :logger, :rate_limits
26
+ attr_reader :rate_limits, :discarded_events, :last_client_report_sent
27
+
28
+ # @deprecated Use Sentry.logger to retrieve the current logger instead.
29
+ attr_reader :logger
13
30
 
14
31
  def initialize(configuration)
15
- @configuration = configuration
16
32
  @logger = configuration.logger
17
33
  @transport_configuration = configuration.transport
18
34
  @dsn = configuration.dsn
19
35
  @rate_limits = {}
36
+ @send_client_reports = configuration.send_client_reports
37
+
38
+ if @send_client_reports
39
+ @discarded_events = Hash.new(0)
40
+ @last_client_report_sent = Time.now
41
+ end
20
42
  end
21
43
 
22
44
  def send_data(data, options = {})
@@ -27,14 +49,9 @@ module Sentry
27
49
  event_hash = event.to_hash
28
50
  item_type = get_item_type(event_hash)
29
51
 
30
- unless configuration.sending_allowed?
31
- log_debug("Envelope [#{item_type}] not sent: #{configuration.error_messages}")
32
-
33
- return
34
- end
35
-
36
52
  if is_rate_limited?(item_type)
37
53
  log_info("Envelope [#{item_type}] not sent: rate limiting")
54
+ record_lost_event(:ratelimit_backoff, item_type)
38
55
 
39
56
  return
40
57
  end
@@ -91,20 +108,38 @@ module Sentry
91
108
 
92
109
  def encode(event)
93
110
  # Convert to hash
94
- event_hash = event.to_hash
111
+ event_payload = event.to_hash
112
+ event_id = event_payload[:event_id] || event_payload["event_id"]
113
+ item_type = get_item_type(event_payload)
114
+
115
+ envelope = Envelope.new(
116
+ {
117
+ event_id: event_id,
118
+ dsn: @dsn.to_s,
119
+ sdk: Sentry.sdk_meta,
120
+ sent_at: Sentry.utc_now.iso8601
121
+ }
122
+ )
123
+
124
+ envelope.add_item(
125
+ { type: item_type, content_type: 'application/json' },
126
+ event_payload
127
+ )
128
+
129
+ client_report_headers, client_report_payload = fetch_pending_client_report
130
+ envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
95
131
 
96
- event_id = event_hash[:event_id] || event_hash["event_id"]
97
- item_type = get_item_type(event_hash)
132
+ log_info("Sending envelope [#{item_type}] #{event_id} to Sentry")
98
133
 
99
- envelope = <<~ENVELOPE
100
- {"event_id":"#{event_id}","dsn":"#{configuration.dsn.to_s}","sdk":#{Sentry.sdk_meta.to_json},"sent_at":"#{Sentry.utc_now.iso8601}"}
101
- {"type":"#{item_type}","content_type":"application/json"}
102
- #{JSON.generate(event_hash)}
103
- ENVELOPE
134
+ envelope.to_s
135
+ end
104
136
 
105
- log_info("Sending envelope [#{item_type}] #{event_id} to Sentry")
137
+ def record_lost_event(reason, item_type)
138
+ return unless @send_client_reports
139
+ return unless CLIENT_REPORT_REASONS.include?(reason)
106
140
 
107
- envelope
141
+ item_type ||= 'event'
142
+ @discarded_events[[reason, item_type]] += 1
108
143
  end
109
144
 
110
145
  private
@@ -112,6 +147,32 @@ module Sentry
112
147
  def get_item_type(event_hash)
113
148
  event_hash[:type] || event_hash["type"] || "event"
114
149
  end
150
+
151
+ def fetch_pending_client_report
152
+ return nil unless @send_client_reports
153
+ return nil if @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
154
+ return nil if @discarded_events.empty?
155
+
156
+ discarded_events_hash = @discarded_events.map do |key, val|
157
+ reason, type = key
158
+
159
+ # 'event' has to be mapped to 'error'
160
+ category = type == 'transaction' ? 'transaction' : 'error'
161
+
162
+ { reason: reason, category: category, quantity: val }
163
+ end
164
+
165
+ item_header = { type: 'client_report' }
166
+ item_payload = {
167
+ timestamp: Sentry.utc_now.iso8601,
168
+ discarded_events: discarded_events_hash
169
+ }
170
+
171
+ @discarded_events = Hash.new(0)
172
+ @last_client_report_sent = Time.now
173
+
174
+ [item_header, item_payload]
175
+ end
115
176
  end
116
177
  end
117
178
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module ArgumentCheckingHelper
3
5
  private
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module CustomInspection
5
+ def inspect
6
+ attr_strings = (instance_variables - self.class::SKIP_INSPECTION_ATTRIBUTES).each_with_object([]) do |attr, result|
7
+ value = instance_variable_get(attr)
8
+ result << "#{attr}=#{value.inspect}" if value
9
+ end
10
+
11
+ "#<#{self.class.name} #{attr_strings.join(", ")}>"
12
+ end
13
+ end
14
+ end
@@ -1,19 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Utils
3
5
  module ExceptionCauseChain
4
6
  def self.exception_to_array(exception)
5
- if exception.respond_to?(:cause) && exception.cause
6
- exceptions = [exception]
7
- while exception.cause
8
- exception = exception.cause
9
- break if exceptions.any? { |e| e.object_id == exception.object_id }
7
+ exceptions = [exception]
8
+
9
+ while exception.cause
10
+ exception = exception.cause
11
+ break if exceptions.any? { |e| e.object_id == exception.object_id }
10
12
 
11
- exceptions << exception
12
- end
13
- exceptions
14
- else
15
- [exception]
13
+ exceptions << exception
16
14
  end
15
+
16
+ exceptions
17
17
  end
18
18
  end
19
19
  end
@@ -1,24 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module LoggingHelper
3
5
  def log_error(message, exception, debug: false)
4
6
  message = "#{message}: #{exception.message}"
5
7
  message += "\n#{exception.backtrace.join("\n")}" if debug
6
8
 
7
- logger.error(LOGGER_PROGNAME) do
9
+ @logger.error(LOGGER_PROGNAME) do
8
10
  message
9
11
  end
10
12
  end
11
13
 
12
14
  def log_info(message)
13
- logger.info(LOGGER_PROGNAME) { message }
15
+ @logger.info(LOGGER_PROGNAME) { message }
14
16
  end
15
17
 
16
18
  def log_debug(message)
17
- logger.debug(LOGGER_PROGNAME) { message }
19
+ @logger.debug(LOGGER_PROGNAME) { message }
18
20
  end
19
21
 
20
22
  def log_warn(message)
21
- logger.warn(LOGGER_PROGNAME) { message }
23
+ @logger.warn(LOGGER_PROGNAME) { message }
22
24
  end
23
25
  end
24
26
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ipaddr'
2
4
 
3
5
  # Based on ActionDispatch::RemoteIp. All security-related precautions from that
@@ -29,7 +31,13 @@ module Sentry
29
31
  @client_ip = client_ip
30
32
  @real_ip = real_ip
31
33
  @forwarded_for = forwarded_for
32
- @trusted_proxies = (LOCAL_ADDRESSES + Array(trusted_proxies)).map { |proxy| IPAddr.new(proxy.to_s) }.uniq
34
+ @trusted_proxies = (LOCAL_ADDRESSES + Array(trusted_proxies)).map do |proxy|
35
+ if proxy.is_a?(IPAddr)
36
+ proxy
37
+ else
38
+ IPAddr.new(proxy.to_s)
39
+ end
40
+ end.uniq
33
41
  end
34
42
 
35
43
  def calculate_ip
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Utils
3
5
  module RequestId
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
- VERSION = "4.4.2"
4
+ VERSION = "5.0.0"
3
5
  end