sentry-raven 2.12.1 → 3.0.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.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. data/.craft.yml +14 -0
  3. data/.github/workflows/test.yml +77 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop.yml +44 -9
  6. data/.scripts/bump-version.sh +9 -0
  7. data/Gemfile +17 -25
  8. data/README.md +4 -3
  9. data/changelog.md +38 -0
  10. data/lib/raven/backtrace.rb +7 -5
  11. data/lib/raven/base.rb +5 -3
  12. data/lib/raven/breadcrumbs.rb +1 -1
  13. data/lib/raven/breadcrumbs/activesupport.rb +10 -10
  14. data/lib/raven/breadcrumbs/logger.rb +3 -3
  15. data/lib/raven/cli.rb +2 -2
  16. data/lib/raven/client.rb +12 -6
  17. data/lib/raven/configuration.rb +15 -5
  18. data/lib/raven/event.rb +5 -6
  19. data/lib/raven/instance.rb +6 -3
  20. data/lib/raven/integrations/delayed_job.rb +14 -15
  21. data/lib/raven/integrations/rack-timeout.rb +2 -3
  22. data/lib/raven/integrations/rack.rb +4 -3
  23. data/lib/raven/integrations/rails.rb +1 -0
  24. data/lib/raven/integrations/rails/active_job.rb +10 -7
  25. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  26. data/lib/raven/interface.rb +2 -2
  27. data/lib/raven/interfaces/stack_trace.rb +1 -1
  28. data/lib/raven/linecache.rb +5 -2
  29. data/lib/raven/logger.rb +3 -2
  30. data/lib/raven/processor/cookies.rb +16 -6
  31. data/lib/raven/processor/post_data.rb +2 -0
  32. data/lib/raven/processor/removecircularreferences.rb +1 -0
  33. data/lib/raven/processor/sanitizedata.rb +65 -17
  34. data/lib/raven/processor/utf8conversion.rb +2 -0
  35. data/lib/raven/transports.rb +4 -0
  36. data/lib/raven/transports/http.rb +5 -5
  37. data/lib/raven/utils/exception_cause_chain.rb +1 -0
  38. data/lib/raven/utils/real_ip.rb +1 -1
  39. data/lib/raven/version.rb +2 -2
  40. data/sentry-raven.gemspec +2 -2
  41. metadata +7 -12
  42. data/.travis.yml +0 -47
@@ -4,13 +4,13 @@ module Raven
4
4
  module BreadcrumbLogger
5
5
  LEVELS = {
6
6
  ::Logger::DEBUG => 'debug',
7
- ::Logger::INFO => 'info',
8
- ::Logger::WARN => 'warn',
7
+ ::Logger::INFO => 'info',
8
+ ::Logger::WARN => 'warn',
9
9
  ::Logger::ERROR => 'error',
10
10
  ::Logger::FATAL => 'fatal'
11
11
  }.freeze
12
12
 
13
- EXC_FORMAT = /^([a-zA-Z0-9]+)\:\s(.*)$/
13
+ EXC_FORMAT = /^([a-zA-Z0-9]+)\:\s(.*)$/.freeze
14
14
 
15
15
  def self.parse_exception(message)
16
16
  lines = message.split(/\n\s*/)
@@ -29,8 +29,8 @@ module Raven
29
29
 
30
30
  begin
31
31
  1 / 0
32
- rescue ZeroDivisionError => exception
33
- evt = instance.capture_exception(exception)
32
+ rescue ZeroDivisionError => e
33
+ evt = instance.capture_exception(e)
34
34
  end
35
35
 
36
36
  if evt && !(evt.is_a? Thread)
@@ -1,14 +1,17 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'base64'
3
4
  require 'json'
4
5
  require 'zlib'
5
6
 
7
+ require "raven/transports"
8
+
6
9
  module Raven
7
10
  # Encodes events and sends them to the Sentry server.
8
11
  class Client
9
- PROTOCOL_VERSION = '5'.freeze
10
- USER_AGENT = "raven-ruby/#{Raven::VERSION}".freeze
11
- CONTENT_TYPE = 'application/json'.freeze
12
+ PROTOCOL_VERSION = '5'
13
+ USER_AGENT = "raven-ruby/#{Raven::VERSION}"
14
+ CONTENT_TYPE = 'application/json'
12
15
 
13
16
  attr_accessor :configuration
14
17
 
@@ -35,7 +38,8 @@ module Raven
35
38
  return
36
39
  end
37
40
 
38
- configuration.logger.info "Sending event #{event[:event_id]} to Sentry"
41
+ event_id = event[:event_id] || event['event_id']
42
+ configuration.logger.info "Sending event #{event_id} to Sentry"
39
43
 
40
44
  content_type, encoded_data = encode(event)
41
45
 
@@ -92,7 +96,7 @@ module Raven
92
96
  end
93
97
 
94
98
  def get_log_message(event)
95
- (event && event[:message]) || get_message_from_exception(event) || '<no message value>'
99
+ (event && event[:message]) || (event && event['message']) || get_message_from_exception(event) || '<no message value>'
96
100
  end
97
101
 
98
102
  def generate_auth_header
@@ -119,7 +123,9 @@ module Raven
119
123
  configuration.logger.warn "Not sending event due to previous failure(s)."
120
124
  end
121
125
  configuration.logger.warn("Failed to submit event: #{get_log_message(event)}")
122
- configuration.transport_failure_callback.call(event) if configuration.transport_failure_callback
126
+
127
+ # configuration.transport_failure_callback can be false & nil
128
+ configuration.transport_failure_callback.call(event) if configuration.transport_failure_callback # rubocop:disable Style/SafeNavigation
123
129
  end
124
130
  end
125
131
 
@@ -236,6 +236,7 @@ module Raven
236
236
 
237
237
  def server=(value)
238
238
  return if value.nil?
239
+
239
240
  uri = URI.parse(value)
240
241
  uri_path = uri.path.split('/')
241
242
 
@@ -253,13 +254,14 @@ module Raven
253
254
 
254
255
  # For anyone who wants to read the base server string
255
256
  @server = "#{scheme}://#{host}"
256
- @server << ":#{port}" unless port == { 'http' => 80, 'https' => 443 }[scheme]
257
- @server << path
257
+ @server += ":#{port}" unless port == { 'http' => 80, 'https' => 443 }[scheme]
258
+ @server += path
258
259
  end
259
260
  alias dsn= server=
260
261
 
261
262
  def encoding=(encoding)
262
263
  raise(Error, 'Unsupported encoding') unless %w(gzip json).include? encoding
264
+
263
265
  @encoding = encoding
264
266
  end
265
267
 
@@ -267,6 +269,7 @@ module Raven
267
269
  unless value == false || value.respond_to?(:call)
268
270
  raise(ArgumentError, "async must be callable (or false to disable)")
269
271
  end
272
+
270
273
  @async = value
271
274
  end
272
275
 
@@ -274,6 +277,7 @@ module Raven
274
277
  unless value == false || value.respond_to?(:call)
275
278
  raise(ArgumentError, "transport_failure_callback must be callable (or false to disable)")
276
279
  end
280
+
277
281
  @transport_failure_callback = value
278
282
  end
279
283
 
@@ -281,6 +285,7 @@ module Raven
281
285
  unless value == false || value.respond_to?(:call)
282
286
  raise ArgumentError, "should_capture must be callable (or false to disable)"
283
287
  end
288
+
284
289
  @should_capture = value
285
290
  end
286
291
 
@@ -288,6 +293,7 @@ module Raven
288
293
  unless value == false || value.respond_to?(:call)
289
294
  raise ArgumentError, "before_send must be callable (or false to disable)"
290
295
  end
296
+
291
297
  @before_send = value
292
298
  end
293
299
 
@@ -351,8 +357,8 @@ module Raven
351
357
  detect_release_from_git ||
352
358
  detect_release_from_capistrano ||
353
359
  detect_release_from_heroku
354
- rescue => ex
355
- logger.error "Error detecting release: #{ex.message}"
360
+ rescue => e
361
+ logger.error "Error detecting release: #{e.message}"
356
362
  end
357
363
 
358
364
  def excluded_exception?(incoming_exception)
@@ -418,18 +424,21 @@ module Raven
418
424
 
419
425
  def capture_in_current_environment?
420
426
  return true unless environments.any? && !environments.include?(current_environment)
427
+
421
428
  @errors << "Not configured to send/capture in environment '#{current_environment}'"
422
429
  false
423
430
  end
424
431
 
425
432
  def capture_allowed_by_callback?(message_or_exc)
426
- return true if !should_capture || message_or_exc.nil? || should_capture.call(*[message_or_exc])
433
+ return true if !should_capture || message_or_exc.nil? || should_capture.call(message_or_exc)
434
+
427
435
  @errors << "should_capture returned false"
428
436
  false
429
437
  end
430
438
 
431
439
  def valid?
432
440
  return true if %w(server host path public_key project_id).all? { |k| public_send(k) }
441
+
433
442
  if server
434
443
  %w(server host path public_key project_id).map do |key|
435
444
  @errors << "No #{key} specified" unless public_send(key)
@@ -442,6 +451,7 @@ module Raven
442
451
 
443
452
  def sample_allowed?
444
453
  return true if sample_rate == 1.0
454
+
445
455
  if Random::DEFAULT.rand >= sample_rate
446
456
  @errors << "Excluded by random sample"
447
457
  false
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'socket'
3
4
  require 'securerandom'
4
5
 
@@ -76,7 +77,7 @@ module Raven
76
77
  end
77
78
 
78
79
  def message
79
- @interfaces[:logentry] && @interfaces[:logentry].unformatted_message
80
+ @interfaces[:logentry]&.unformatted_message
80
81
  end
81
82
 
82
83
  def message=(args)
@@ -96,12 +97,13 @@ module Raven
96
97
  end
97
98
 
98
99
  def level=(new_level) # needed to meet the Sentry spec
99
- @level = new_level == "warn" || new_level == :warn ? :warning : new_level
100
+ @level = new_level.to_s == "warn" ? :warning : new_level
100
101
  end
101
102
 
102
103
  def interface(name, value = nil, &block)
103
104
  int = Interface.registered[name]
104
105
  raise(Error, "Unknown interface: #{name}") unless int
106
+
105
107
  @interfaces[int.sentry_alias] = int.new(value, &block) if value || block
106
108
  @interfaces[int.sentry_alias]
107
109
  end
@@ -228,10 +230,7 @@ module Raven
228
230
  end
229
231
 
230
232
  def async_json_processors
231
- [
232
- Raven::Processor::RemoveCircularReferences,
233
- Raven::Processor::UTF8Conversion
234
- ].map { |v| v.new(self) }
233
+ configuration.processors.map { |v| v.new(self) }
235
234
  end
236
235
 
237
236
  def list_gem_specs
@@ -51,6 +51,7 @@ module Raven
51
51
  # Tell the log that the client is good to go
52
52
  def report_status
53
53
  return if configuration.silence_ready
54
+
54
55
  if configuration.capture_allowed?
55
56
  logger.info "Raven #{VERSION} ready to catch errors"
56
57
  else
@@ -111,15 +112,15 @@ module Raven
111
112
  message_or_exc = obj.is_a?(String) ? "message" : "exception"
112
113
  options[:configuration] = configuration
113
114
  options[:context] = context
114
- if (evt = Event.send("from_" + message_or_exc, obj, options))
115
+ if evt = Event.send("from_" + message_or_exc, obj, options)
115
116
  yield evt if block_given?
116
117
  if configuration.async?
117
118
  begin
118
119
  # We have to convert to a JSON-like hash, because background job
119
120
  # processors (esp ActiveJob) may not like weird types in the event hash
120
121
  configuration.async.call(evt.to_json_compatible)
121
- rescue => ex
122
- logger.error("async event sending failed: #{ex.message}")
122
+ rescue => e
123
+ logger.error("async event sending failed: #{e.message}")
123
124
  send_event(evt, make_hint(obj))
124
125
  end
125
126
  else
@@ -184,6 +185,7 @@ module Raven
184
185
  def tags_context(options = nil)
185
186
  context.tags.merge!(options || {})
186
187
  yield if block_given?
188
+ context.tags
187
189
  ensure
188
190
  context.tags.delete_if { |k, _| options.keys.include? k } if block_given?
189
191
  end
@@ -198,6 +200,7 @@ module Raven
198
200
  def extra_context(options = nil)
199
201
  context.extra.merge!(options || {})
200
202
  yield if block_given?
203
+ context.extra
201
204
  ensure
202
205
  context.extra.delete_if { |k, _| options.keys.include? k } if block_given?
203
206
  end
@@ -8,19 +8,18 @@ module Delayed
8
8
  begin
9
9
  # Forward the call to the next callback in the callback chain
10
10
  block.call(job, *args)
11
-
12
- rescue Exception => exception
11
+ rescue Exception => e
13
12
  # Log error to Sentry
14
13
  extra = {
15
14
  :delayed_job => {
16
- :id => job.id,
17
- :priority => job.priority,
18
- :attempts => job.attempts,
19
- :run_at => job.run_at,
20
- :locked_at => job.locked_at,
21
- :locked_by => job.locked_by,
22
- :queue => job.queue,
23
- :created_at => job.created_at
15
+ :id => job.id.to_s,
16
+ :priority => job.priority,
17
+ :attempts => job.attempts,
18
+ :run_at => job.run_at,
19
+ :locked_at => job.locked_at,
20
+ :locked_by => job.locked_by,
21
+ :queue => job.queue,
22
+ :created_at => job.created_at
24
23
  }
25
24
  }
26
25
  # last_error can be nil
@@ -32,16 +31,16 @@ module Delayed
32
31
  if job.respond_to?('payload_object') && job.payload_object.respond_to?('job_data')
33
32
  extra[:active_job] = job.payload_object.job_data
34
33
  end
35
- ::Raven.capture_exception(exception,
36
- :logger => 'delayed_job',
37
- :tags => {
34
+ ::Raven.capture_exception(e,
35
+ :logger => 'delayed_job',
36
+ :tags => {
38
37
  :delayed_job_queue => job.queue,
39
- :delayed_job_id => job.id
38
+ :delayed_job_id => job.id.to_s
40
39
  },
41
40
  :extra => extra)
42
41
 
43
42
  # Make sure we propagate the failure!
44
- raise exception
43
+ raise e
45
44
  ensure
46
45
  ::Raven::Context.clear!
47
46
  ::Raven::BreadcrumbBuffer.clear!
@@ -14,6 +14,5 @@ module RackTimeoutExtensions
14
14
  end
15
15
  end
16
16
 
17
- # Include is private in Ruby 1.9
18
- Rack::Timeout::Error.__send__(:include, RackTimeoutExtensions)
19
- Rack::Timeout::RequestTimeoutException.__send__(:include, RackTimeoutExtensions)
17
+ Rack::Timeout::Error.include(RackTimeoutExtensions)
18
+ Rack::Timeout::RequestTimeoutException.include(RackTimeoutExtensions)
@@ -92,8 +92,8 @@ module Raven
92
92
  request.body.rewind
93
93
  data
94
94
  end
95
- rescue IOError => ex
96
- ex.message
95
+ rescue IOError => e
96
+ e.message
97
97
  end
98
98
 
99
99
  def format_headers_for_sentry(env_hash)
@@ -112,8 +112,9 @@ module Raven
112
112
  next if key == 'HTTP_COOKIE' # Cookies don't go here, they go somewhere else
113
113
 
114
114
  next unless key.start_with?('HTTP_') || %w(CONTENT_TYPE CONTENT_LENGTH).include?(key)
115
+
115
116
  # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
116
- key = key.gsub("HTTP_", "")
117
+ key = key.sub(/^HTTP_/, "")
117
118
  key = key.split('_').map(&:capitalize).join('-')
118
119
  memo[key] = value
119
120
  rescue StandardError => e
@@ -5,6 +5,7 @@ module Raven
5
5
  require 'raven/integrations/rails/overrides/streaming_reporter'
6
6
  require 'raven/integrations/rails/controller_methods'
7
7
  require 'raven/integrations/rails/controller_transaction'
8
+ require 'raven/integrations/rack'
8
9
 
9
10
  initializer "raven.use_rack_middleware" do |app|
10
11
  app.config.middleware.insert 0, Raven::Rack
@@ -9,19 +9,22 @@ module Raven
9
9
  def self.included(base)
10
10
  base.class_eval do
11
11
  around_perform do |job, block|
12
- capture_and_reraise_with_sentry(job, block)
12
+ if already_supported_by_specific_integration?(job)
13
+ block.call
14
+ else
15
+ capture_and_reraise_with_sentry(job, block)
16
+ end
13
17
  end
14
18
  end
15
19
  end
16
20
 
17
21
  def capture_and_reraise_with_sentry(job, block)
18
22
  block.call
19
- rescue Exception => exception # rubocop:disable Lint/RescueException
20
- return if rescue_with_handler(exception)
21
- unless already_supported_by_specific_integration?(job)
22
- Raven.capture_exception(exception, :extra => raven_context(job))
23
- end
24
- raise exception
23
+ rescue Exception => e # rubocop:disable Lint/RescueException
24
+ return if rescue_with_handler(e)
25
+
26
+ Raven.capture_exception(e, :extra => raven_context(job))
27
+ raise e
25
28
  ensure
26
29
  Context.clear!
27
30
  BreadcrumbBuffer.clear!
@@ -6,7 +6,7 @@ module Raven
6
6
  begin
7
7
  env = env_or_request.respond_to?(:env) ? env_or_request.env : env_or_request
8
8
  Raven::Rack.capture_exception(exception, env)
9
- rescue # rubocop:disable Lint/HandleExceptions
9
+ rescue
10
10
  end
11
11
  super
12
12
  end
@@ -21,7 +21,7 @@ module Raven
21
21
  begin
22
22
  env = env_or_request.respond_to?(:env) ? env_or_request.env : env_or_request
23
23
  Raven::Rack.capture_exception(exception, env)
24
- rescue # rubocop:disable Lint/HandleExceptions
24
+ rescue
25
25
  end
26
26
  render_exception_without_raven(env_or_request, exception)
27
27
  end
@@ -1,9 +1,9 @@
1
1
  module Raven
2
2
  class Interface
3
3
  def initialize(attributes = nil)
4
- attributes.each do |attr, value|
4
+ attributes&.each do |attr, value|
5
5
  public_send "#{attr}=", value
6
- end if attributes
6
+ end
7
7
 
8
8
  yield self if block_given?
9
9
  end
@@ -58,7 +58,7 @@ module Raven
58
58
  end
59
59
 
60
60
  def project_root
61
- @project_root ||= Raven.configuration.project_root && Raven.configuration.project_root.to_s
61
+ @project_root ||= Raven.configuration.project_root&.to_s
62
62
  end
63
63
 
64
64
  def longest_load_path
@@ -10,6 +10,7 @@ module Raven
10
10
  # line should be the line requested by lineno. See specs for more information.
11
11
  def get_file_context(filename, lineno, context)
12
12
  return nil, nil, nil unless valid_path?(filename)
13
+
13
14
  lines = Array.new(2 * context + 1) do |i|
14
15
  getline(filename, lineno - context + i)
15
16
  end
@@ -26,15 +27,17 @@ module Raven
26
27
  def getlines(path)
27
28
  @cache[path] ||= begin
28
29
  IO.readlines(path)
29
- rescue
30
- nil
30
+ rescue
31
+ nil
31
32
  end
32
33
  end
33
34
 
34
35
  def getline(path, n)
35
36
  return nil if n < 1
37
+
36
38
  lines = getlines(path)
37
39
  return nil if lines.nil?
40
+
38
41
  lines[n - 1]
39
42
  end
40
43
  end