sentry-raven 2.9.0 → 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 (43) 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 +5 -4
  9. data/changelog.md +77 -1
  10. data/lib/raven/backtrace.rb +7 -5
  11. data/lib/raven/base.rb +5 -3
  12. data/lib/raven/breadcrumbs/activesupport.rb +10 -10
  13. data/lib/raven/breadcrumbs/logger.rb +4 -4
  14. data/lib/raven/breadcrumbs.rb +1 -1
  15. data/lib/raven/cli.rb +2 -2
  16. data/lib/raven/client.rb +28 -10
  17. data/lib/raven/configuration.rb +23 -8
  18. data/lib/raven/event.rb +5 -9
  19. data/lib/raven/instance.rb +12 -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/active_job.rb +10 -7
  24. data/lib/raven/integrations/rails/controller_transaction.rb +1 -1
  25. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  26. data/lib/raven/integrations/rails.rb +1 -0
  27. data/lib/raven/interface.rb +2 -2
  28. data/lib/raven/interfaces/stack_trace.rb +1 -1
  29. data/lib/raven/linecache.rb +5 -2
  30. data/lib/raven/logger.rb +3 -2
  31. data/lib/raven/processor/cookies.rb +16 -6
  32. data/lib/raven/processor/post_data.rb +2 -0
  33. data/lib/raven/processor/removecircularreferences.rb +1 -0
  34. data/lib/raven/processor/sanitizedata.rb +65 -17
  35. data/lib/raven/processor/utf8conversion.rb +3 -1
  36. data/lib/raven/transports/http.rb +5 -5
  37. data/lib/raven/transports.rb +4 -0
  38. data/lib/raven/utils/exception_cause_chain.rb +1 -0
  39. data/lib/raven/utils/real_ip.rb +1 -1
  40. data/lib/raven/version.rb +2 -2
  41. data/sentry-raven.gemspec +3 -3
  42. metadata +8 -13
  43. data/.travis.yml +0 -47
@@ -1,19 +1,19 @@
1
1
  module Raven
2
2
  module ActiveSupportBreadcrumbs
3
3
  class << self
4
- def add(name, started, _finished, _unique_id, data)
5
- Raven.breadcrumbs.record do |crumb|
6
- crumb.data = data
7
- crumb.category = name
8
- crumb.timestamp = started.to_i
9
- end
4
+ def add(name, started, _finished, _unique_id, data)
5
+ Raven.breadcrumbs.record do |crumb|
6
+ crumb.data = data
7
+ crumb.category = name
8
+ crumb.timestamp = started.to_i
10
9
  end
10
+ end
11
11
 
12
- def inject
13
- ActiveSupport::Notifications.subscribe(/.*/) do |name, started, finished, unique_id, data|
14
- add(name, started, finished, unique_id, data)
15
- end
12
+ def inject
13
+ ActiveSupport::Notifications.subscribe(/.*/) do |name, started, finished, unique_id, data|
14
+ add(name, started, finished, unique_id, data)
16
15
  end
16
+ end
17
17
  end
18
18
  end
19
19
  end
@@ -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*/)
@@ -36,7 +36,7 @@ module Raven
36
36
 
37
37
  # some loggers will add leading/trailing space as they (incorrectly, mind you)
38
38
  # think of logging as a shortcut to std{out,err}
39
- message = message.strip
39
+ message = message.to_s.strip
40
40
 
41
41
  last_crumb = Raven.breadcrumbs.peek
42
42
  # try to avoid dupes from logger broadcasts
@@ -64,7 +64,7 @@ module Raven
64
64
  end
65
65
 
66
66
  def empty?
67
- !members.any?
67
+ members.none?
68
68
  end
69
69
 
70
70
  def to_hash
data/lib/raven/cli.rb CHANGED
@@ -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)
data/lib/raven/client.rb CHANGED
@@ -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
 
@@ -79,8 +83,20 @@ module Raven
79
83
  end
80
84
  end
81
85
 
86
+ def get_message_from_exception(event)
87
+ (
88
+ event &&
89
+ event[:exception] &&
90
+ event[:exception][:values] &&
91
+ event[:exception][:values][0] &&
92
+ event[:exception][:values][0][:type] &&
93
+ event[:exception][:values][0][:value] &&
94
+ "#{event[:exception][:values][0][:type]}: #{event[:exception][:values][0][:value]}"
95
+ )
96
+ end
97
+
82
98
  def get_log_message(event)
83
- (event && event[:message]) || '<no message value>'
99
+ (event && event[:message]) || (event && event['message']) || get_message_from_exception(event) || '<no message value>'
84
100
  end
85
101
 
86
102
  def generate_auth_header
@@ -100,14 +116,16 @@ module Raven
100
116
  end
101
117
 
102
118
  def failed_send(e, event)
103
- @state.failure
104
119
  if e # exception was raised
105
- configuration.logger.error "Unable to record event with remote Sentry server (#{e.class} - #{e.message}):\n#{e.backtrace[0..10].join("\n")}"
120
+ @state.failure
121
+ configuration.logger.warn "Unable to record event with remote Sentry server (#{e.class} - #{e.message}):\n#{e.backtrace[0..10].join("\n")}"
106
122
  else
107
- configuration.logger.error "Not sending event due to previous failure(s)."
123
+ configuration.logger.warn "Not sending event due to previous failure(s)."
108
124
  end
109
- configuration.logger.error("Failed to submit event: #{get_log_message(event)}")
110
- configuration.transport_failure_callback.call(event) if configuration.transport_failure_callback
125
+ configuration.logger.warn("Failed to submit event: #{get_log_message(event)}")
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
111
129
  end
112
130
  end
113
131
 
@@ -1,7 +1,7 @@
1
1
  require 'uri'
2
2
 
3
3
  module Raven
4
- class Configuration # rubocop:disable Metrics/ClassLength
4
+ class Configuration
5
5
  # Directories to be recognized as part of your app. e.g. if you
6
6
  # have an `engines` dir at the root of your project, you may want
7
7
  # to set this to something like /(app|config|engines|lib)/
@@ -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
 
@@ -347,11 +353,12 @@ module Raven
347
353
  end
348
354
 
349
355
  def detect_release
350
- detect_release_from_git ||
356
+ detect_release_from_env ||
357
+ detect_release_from_git ||
351
358
  detect_release_from_capistrano ||
352
359
  detect_release_from_heroku
353
- rescue => ex
354
- logger.error "Error detecting release: #{ex.message}"
360
+ rescue => e
361
+ logger.error "Error detecting release: #{e.message}"
355
362
  end
356
363
 
357
364
  def excluded_exception?(incoming_exception)
@@ -411,20 +418,27 @@ module Raven
411
418
  Raven.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
412
419
  end
413
420
 
421
+ def detect_release_from_env
422
+ ENV['SENTRY_RELEASE']
423
+ end
424
+
414
425
  def capture_in_current_environment?
415
426
  return true unless environments.any? && !environments.include?(current_environment)
427
+
416
428
  @errors << "Not configured to send/capture in environment '#{current_environment}'"
417
429
  false
418
430
  end
419
431
 
420
432
  def capture_allowed_by_callback?(message_or_exc)
421
- 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
+
422
435
  @errors << "should_capture returned false"
423
436
  false
424
437
  end
425
438
 
426
439
  def valid?
427
440
  return true if %w(server host path public_key project_id).all? { |k| public_send(k) }
441
+
428
442
  if server
429
443
  %w(server host path public_key project_id).map do |key|
430
444
  @errors << "No #{key} specified" unless public_send(key)
@@ -437,6 +451,7 @@ module Raven
437
451
 
438
452
  def sample_allowed?
439
453
  return true if sample_rate == 1.0
454
+
440
455
  if Random::DEFAULT.rand >= sample_rate
441
456
  @errors << "Excluded by random sample"
442
457
  false
@@ -453,7 +468,7 @@ module Raven
453
468
  end
454
469
 
455
470
  def current_environment_from_env
456
- ENV['SENTRY_CURRENT_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
471
+ ENV['SENTRY_CURRENT_ENV'] || ENV['SENTRY_ENVIRONMENT'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
457
472
  end
458
473
 
459
474
  def server_name_from_env
data/lib/raven/event.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'socket'
3
4
  require 'securerandom'
4
5
 
@@ -59,10 +60,7 @@ module Raven
59
60
  return unless configuration.exception_class_allowed?(exc)
60
61
 
61
62
  new(options) do |evt|
62
- evt.message = "#{exc.class}: #{exc.message}"
63
-
64
63
  evt.add_exception_interface(exc)
65
-
66
64
  yield evt if block
67
65
  end
68
66
  end
@@ -79,7 +77,7 @@ module Raven
79
77
  end
80
78
 
81
79
  def message
82
- @interfaces[:logentry] && @interfaces[:logentry].unformatted_message
80
+ @interfaces[:logentry]&.unformatted_message
83
81
  end
84
82
 
85
83
  def message=(args)
@@ -99,12 +97,13 @@ module Raven
99
97
  end
100
98
 
101
99
  def level=(new_level) # needed to meet the Sentry spec
102
- @level = new_level == "warn" || new_level == :warn ? :warning : new_level
100
+ @level = new_level.to_s == "warn" ? :warning : new_level
103
101
  end
104
102
 
105
103
  def interface(name, value = nil, &block)
106
104
  int = Interface.registered[name]
107
105
  raise(Error, "Unknown interface: #{name}") unless int
106
+
108
107
  @interfaces[int.sentry_alias] = int.new(value, &block) if value || block
109
108
  @interfaces[int.sentry_alias]
110
109
  end
@@ -231,10 +230,7 @@ module Raven
231
230
  end
232
231
 
233
232
  def async_json_processors
234
- [
235
- Raven::Processor::RemoveCircularReferences,
236
- Raven::Processor::UTF8Conversion
237
- ].map { |v| v.new(self) }
233
+ configuration.processors.map { |v| v.new(self) }
238
234
  end
239
235
 
240
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
@@ -183,6 +184,10 @@ module Raven
183
184
  # Raven.tags_context('my_custom_tag' => 'tag_value')
184
185
  def tags_context(options = nil)
185
186
  context.tags.merge!(options || {})
187
+ yield if block_given?
188
+ context.tags
189
+ ensure
190
+ context.tags.delete_if { |k, _| options.keys.include? k } if block_given?
186
191
  end
187
192
 
188
193
  # Bind extra context. Merges with existing context (if any).
@@ -194,6 +199,10 @@ module Raven
194
199
  # Raven.extra_context('my_custom_data' => 'value')
195
200
  def extra_context(options = nil)
196
201
  context.extra.merge!(options || {})
202
+ yield if block_given?
203
+ context.extra
204
+ ensure
205
+ context.extra.delete_if { |k, _| options.keys.include? k } if block_given?
197
206
  end
198
207
 
199
208
  def rack_context(env)
@@ -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