sentry-ruby-core 4.4.2 → 5.0.0

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.
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