sentry-raven 2.9.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
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