sentry-raven 3.0.0 → 3.1.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/.craft.yml +15 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  4. data/.github/pull_request_template.md +16 -0
  5. data/.github/workflows/test.yml +92 -0
  6. data/.github/workflows/zeus_upload.yml +32 -0
  7. data/.gitignore +3 -0
  8. data/.rubocop.yml +50 -12
  9. data/.scripts/bump-version.sh +9 -0
  10. data/{changelog.md → CHANGELOG.md} +147 -1
  11. data/CONTRIBUTING.md +71 -0
  12. data/Gemfile +20 -25
  13. data/README.md +26 -16
  14. data/lib/raven/backtrace.rb +9 -5
  15. data/lib/raven/base.rb +6 -2
  16. data/lib/raven/breadcrumbs.rb +1 -1
  17. data/lib/raven/breadcrumbs/{activesupport.rb → active_support_logger.rb} +9 -3
  18. data/lib/raven/breadcrumbs/logger.rb +2 -92
  19. data/lib/raven/breadcrumbs/sentry_logger.rb +73 -0
  20. data/lib/raven/cli.rb +10 -21
  21. data/lib/raven/client.rb +9 -4
  22. data/lib/raven/configuration.rb +86 -10
  23. data/lib/raven/context.rb +13 -8
  24. data/lib/raven/core_ext/object/deep_dup.rb +57 -0
  25. data/lib/raven/core_ext/object/duplicable.rb +153 -0
  26. data/lib/raven/event.rb +27 -15
  27. data/lib/raven/helpers/deprecation_helper.rb +17 -0
  28. data/lib/raven/instance.rb +9 -4
  29. data/lib/raven/integrations/delayed_job.rb +13 -14
  30. data/lib/raven/integrations/rack-timeout.rb +7 -4
  31. data/lib/raven/integrations/rack.rb +3 -2
  32. data/lib/raven/integrations/rails.rb +13 -3
  33. data/lib/raven/integrations/rails/active_job.rb +6 -4
  34. data/lib/raven/integrations/rails/backtrace_cleaner.rb +29 -0
  35. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  36. data/lib/raven/integrations/sidekiq.rb +4 -78
  37. data/lib/raven/integrations/sidekiq/cleanup_middleware.rb +13 -0
  38. data/lib/raven/integrations/sidekiq/context_filter.rb +42 -0
  39. data/lib/raven/integrations/sidekiq/error_handler.rb +38 -0
  40. data/lib/raven/interface.rb +2 -2
  41. data/lib/raven/interfaces/stack_trace.rb +1 -1
  42. data/lib/raven/linecache.rb +5 -2
  43. data/lib/raven/logger.rb +3 -2
  44. data/lib/raven/processor/cookies.rb +16 -6
  45. data/lib/raven/processor/post_data.rb +2 -0
  46. data/lib/raven/processor/removecircularreferences.rb +3 -1
  47. data/lib/raven/processor/sanitizedata.rb +65 -17
  48. data/lib/raven/processor/utf8conversion.rb +2 -0
  49. data/lib/raven/transports.rb +4 -0
  50. data/lib/raven/transports/http.rb +5 -5
  51. data/lib/raven/utils/exception_cause_chain.rb +1 -0
  52. data/lib/raven/utils/real_ip.rb +1 -1
  53. data/lib/raven/version.rb +2 -2
  54. data/lib/sentry-raven-without-integrations.rb +6 -1
  55. data/lib/sentry_raven_without_integrations.rb +1 -0
  56. metadata +20 -5
  57. data/.travis.yml +0 -43
@@ -1,6 +1,6 @@
1
1
  module Raven
2
2
  class CLI
3
- def self.test(dsn = nil, silent = false, config = nil) # rubocop:disable all
3
+ def self.test(dsn = nil, silent = false, config = nil)
4
4
  config ||= Raven.configuration
5
5
 
6
6
  config.logger = if silent
@@ -18,7 +18,7 @@ module Raven
18
18
 
19
19
  # wipe out env settings to ensure we send the event
20
20
  unless config.capture_allowed?
21
- env_name = config.environments.pop || 'production'
21
+ env_name = config.environments.last || 'production'
22
22
  config.current_environment = env_name
23
23
  end
24
24
 
@@ -29,31 +29,20 @@ 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
- if evt && !(evt.is_a? Thread)
37
- if evt.is_a? Hash
38
- instance.logger.debug "-> event ID: #{evt[:event_id]}"
39
- else
40
- instance.logger.debug "-> event ID: #{evt.id}"
41
- end
42
- elsif evt # async configuration
43
- if evt.value.is_a? Hash
44
- instance.logger.debug "-> event ID: #{evt.value[:event_id]}"
45
- else
46
- instance.logger.debug "-> event ID: #{evt.value.id}"
47
- end
36
+ if evt
37
+ instance.logger.debug "-> event ID: #{evt.id}"
38
+ instance.logger.debug ""
39
+ instance.logger.debug "Done!"
40
+ evt
48
41
  else
49
42
  instance.logger.debug ""
50
43
  instance.logger.debug "An error occurred while attempting to send the event."
51
- exit 1
44
+ false
52
45
  end
53
-
54
- instance.logger.debug ""
55
- instance.logger.debug "Done!"
56
- evt
57
46
  end
58
47
  end
59
48
  end
@@ -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
 
@@ -120,7 +123,9 @@ module Raven
120
123
  configuration.logger.warn "Not sending event due to previous failure(s)."
121
124
  end
122
125
  configuration.logger.warn("Failed to submit event: #{get_log_message(event)}")
123
- 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, e) if configuration.transport_failure_callback # rubocop:disable Style/SafeNavigation
124
129
  end
125
130
  end
126
131
 
@@ -12,6 +12,11 @@ module Raven
12
12
  attr_reader :async
13
13
  alias async? async
14
14
 
15
+ # An array of breadcrumbs loggers to be used. Available options are:
16
+ # - :sentry_logger
17
+ # - :active_support_logger
18
+ attr_reader :breadcrumbs_logger
19
+
15
20
  # Number of lines of code context to capture, or nil for none
16
21
  attr_accessor :context_lines
17
22
 
@@ -83,7 +88,7 @@ module Raven
83
88
  attr_accessor :public_key
84
89
 
85
90
  # Turns on ActiveSupport breadcrumbs integration
86
- attr_accessor :rails_activesupport_breadcrumbs
91
+ attr_reader :rails_activesupport_breadcrumbs
87
92
 
88
93
  # Rails catches exceptions in the ActionDispatch::ShowExceptions or
89
94
  # ActionDispatch::DebugExceptions middlewares, depending on the environment.
@@ -118,6 +123,19 @@ module Raven
118
123
  # Otherwise, can be one of "http", "https", or "dummy"
119
124
  attr_accessor :scheme
120
125
 
126
+ # a proc/lambda that takes an array of stack traces
127
+ # it'll be used to silence (reduce) backtrace of the exception
128
+ #
129
+ # for example:
130
+ #
131
+ # ```ruby
132
+ # Raven.configuration.backtrace_cleanup_callback = lambda do |backtrace|
133
+ # Rails.backtrace_cleaner.clean(backtrace)
134
+ # end
135
+ # ```
136
+ #
137
+ attr_accessor :backtrace_cleanup_callback
138
+
121
139
  # Secret key for authentication with the Sentry server
122
140
  # If you provide a DSN, this will be set automatically.
123
141
  #
@@ -172,16 +190,32 @@ module Raven
172
190
  # Errors object - an Array that contains error messages. See #
173
191
  attr_reader :errors
174
192
 
193
+ # the dsn value, whether it's set via `config.dsn=` or `ENV["SENTRY_DSN"]`
194
+ attr_reader :dsn
195
+
196
+ # Most of these errors generate 4XX responses. In general, Sentry clients
197
+ # only automatically report 5xx responses.
175
198
  IGNORE_DEFAULT = [
176
199
  'AbstractController::ActionNotFound',
200
+ 'ActionController::BadRequest',
177
201
  'ActionController::InvalidAuthenticityToken',
202
+ 'ActionController::InvalidCrossOriginRequest',
203
+ 'ActionController::MethodNotAllowed',
204
+ 'ActionController::NotImplemented',
205
+ 'ActionController::ParameterMissing',
178
206
  'ActionController::RoutingError',
179
207
  'ActionController::UnknownAction',
208
+ 'ActionController::UnknownFormat',
209
+ 'ActionController::UnknownHttpMethod',
210
+ 'ActionDispatch::Http::Parameters::ParseError',
211
+ 'ActionView::MissingTemplate',
212
+ 'ActiveJob::DeserializationError', # Can cause infinite loops
180
213
  'ActiveRecord::RecordNotFound',
181
214
  'CGI::Session::CookieStore::TamperedWithCookie',
182
215
  'Mongoid::Errors::DocumentNotFound',
183
- 'Sinatra::NotFound',
184
- 'ActiveJob::DeserializationError'
216
+ 'Rack::QueryParser::InvalidParameterError',
217
+ 'Rack::QueryParser::ParameterTypeError',
218
+ 'Sinatra::NotFound'
185
219
  ].freeze
186
220
 
187
221
  # Note the order - we have to remove circular references and bad characters
@@ -201,8 +235,11 @@ module Raven
201
235
  LOG_PREFIX = "** [Raven] ".freeze
202
236
  MODULE_SEPARATOR = "::".freeze
203
237
 
238
+ AVAILABLE_BREADCRUMBS_LOGGERS = [:sentry_logger, :active_support_logger].freeze
239
+
204
240
  def initialize
205
241
  self.async = false
242
+ self.breadcrumbs_logger = []
206
243
  self.context_lines = 3
207
244
  self.current_environment = current_environment_from_env
208
245
  self.encoding = 'gzip'
@@ -215,7 +252,8 @@ module Raven
215
252
  self.open_timeout = 1
216
253
  self.processors = DEFAULT_PROCESSORS.dup
217
254
  self.project_root = detect_project_root
218
- self.rails_activesupport_breadcrumbs = false
255
+ @rails_activesupport_breadcrumbs = false
256
+
219
257
  self.rails_report_rescued_exceptions = true
220
258
  self.release = detect_release
221
259
  self.sample_rate = 1.0
@@ -236,6 +274,9 @@ module Raven
236
274
 
237
275
  def server=(value)
238
276
  return if value.nil?
277
+
278
+ @dsn = value
279
+
239
280
  uri = URI.parse(value)
240
281
  uri_path = uri.path.split('/')
241
282
 
@@ -253,13 +294,14 @@ module Raven
253
294
 
254
295
  # For anyone who wants to read the base server string
255
296
  @server = "#{scheme}://#{host}"
256
- @server << ":#{port}" unless port == { 'http' => 80, 'https' => 443 }[scheme]
257
- @server << path
297
+ @server += ":#{port}" unless port == { 'http' => 80, 'https' => 443 }[scheme]
298
+ @server += path
258
299
  end
259
300
  alias dsn= server=
260
301
 
261
302
  def encoding=(encoding)
262
303
  raise(Error, 'Unsupported encoding') unless %w(gzip json).include? encoding
304
+
263
305
  @encoding = encoding
264
306
  end
265
307
 
@@ -267,13 +309,32 @@ module Raven
267
309
  unless value == false || value.respond_to?(:call)
268
310
  raise(ArgumentError, "async must be callable (or false to disable)")
269
311
  end
312
+
270
313
  @async = value
271
314
  end
272
315
 
316
+ def breadcrumbs_logger=(logger)
317
+ loggers =
318
+ if logger.is_a?(Array)
319
+ logger
320
+ else
321
+ unless AVAILABLE_BREADCRUMBS_LOGGERS.include?(logger)
322
+ raise Raven::Error, "Unsupported breadcrumbs logger. Supported loggers: #{AVAILABLE_BREADCRUMBS_LOGGERS}"
323
+ end
324
+
325
+ Array(logger)
326
+ end
327
+
328
+ require "raven/breadcrumbs/sentry_logger" if loggers.include?(:sentry_logger)
329
+
330
+ @breadcrumbs_logger = logger
331
+ end
332
+
273
333
  def transport_failure_callback=(value)
274
334
  unless value == false || value.respond_to?(:call)
275
335
  raise(ArgumentError, "transport_failure_callback must be callable (or false to disable)")
276
336
  end
337
+
277
338
  @transport_failure_callback = value
278
339
  end
279
340
 
@@ -281,6 +342,7 @@ module Raven
281
342
  unless value == false || value.respond_to?(:call)
282
343
  raise ArgumentError, "should_capture must be callable (or false to disable)"
283
344
  end
345
+
284
346
  @should_capture = value
285
347
  end
286
348
 
@@ -288,6 +350,7 @@ module Raven
288
350
  unless value == false || value.respond_to?(:call)
289
351
  raise ArgumentError, "before_send must be callable (or false to disable)"
290
352
  end
353
+
291
354
  @before_send = value
292
355
  end
293
356
 
@@ -323,6 +386,11 @@ module Raven
323
386
  Backtrace::Line.instance_variable_set(:@in_app_pattern, nil) # blow away cache
324
387
  end
325
388
 
389
+ def rails_activesupport_breadcrumbs=(val)
390
+ DeprecationHelper.deprecate_old_breadcrumbs_configuration(:active_support_logger)
391
+ @rails_activesupport_breadcrumbs = val
392
+ end
393
+
326
394
  def exception_class_allowed?(exc)
327
395
  if exc.is_a?(Raven::Error)
328
396
  # Try to prevent error reporting loops
@@ -336,6 +404,10 @@ module Raven
336
404
  end
337
405
  end
338
406
 
407
+ def enabled_in_current_env?
408
+ environments.empty? || environments.include?(current_environment)
409
+ end
410
+
339
411
  private
340
412
 
341
413
  def detect_project_root
@@ -351,8 +423,8 @@ module Raven
351
423
  detect_release_from_git ||
352
424
  detect_release_from_capistrano ||
353
425
  detect_release_from_heroku
354
- rescue => ex
355
- logger.error "Error detecting release: #{ex.message}"
426
+ rescue => e
427
+ logger.error "Error detecting release: #{e.message}"
356
428
  end
357
429
 
358
430
  def excluded_exception?(incoming_exception)
@@ -417,19 +489,22 @@ module Raven
417
489
  end
418
490
 
419
491
  def capture_in_current_environment?
420
- return true unless environments.any? && !environments.include?(current_environment)
492
+ return true if enabled_in_current_env?
493
+
421
494
  @errors << "Not configured to send/capture in environment '#{current_environment}'"
422
495
  false
423
496
  end
424
497
 
425
498
  def capture_allowed_by_callback?(message_or_exc)
426
- return true if !should_capture || message_or_exc.nil? || should_capture.call(*[message_or_exc])
499
+ return true if !should_capture || message_or_exc.nil? || should_capture.call(message_or_exc)
500
+
427
501
  @errors << "should_capture returned false"
428
502
  false
429
503
  end
430
504
 
431
505
  def valid?
432
506
  return true if %w(server host path public_key project_id).all? { |k| public_send(k) }
507
+
433
508
  if server
434
509
  %w(server host path public_key project_id).map do |key|
435
510
  @errors << "No #{key} specified" unless public_send(key)
@@ -442,6 +517,7 @@ module Raven
442
517
 
443
518
  def sample_allowed?
444
519
  return true if sample_rate == 1.0
520
+
445
521
  if Random::DEFAULT.rand >= sample_rate
446
522
  @errors << "Excluded by random sample"
447
523
  false
@@ -1,4 +1,5 @@
1
1
  require 'rbconfig'
2
+ require 'etc'
2
3
 
3
4
  module Raven
4
5
  class Context
@@ -24,18 +25,22 @@ module Raven
24
25
 
25
26
  class << self
26
27
  def os_context
27
- @os_context ||= {
28
- :name => Raven.sys_command("uname -s") || RbConfig::CONFIG["host_os"],
29
- :version => Raven.sys_command("uname -v"),
30
- :build => Raven.sys_command("uname -r"),
31
- :kernel_version => Raven.sys_command("uname -a") || Raven.sys_command("ver") # windows
32
- }
28
+ @os_context ||=
29
+ begin
30
+ uname = Etc.uname
31
+ {
32
+ name: uname[:sysname] || RbConfig::CONFIG["host_os"],
33
+ version: uname[:version],
34
+ build: uname[:release],
35
+ kernel_version: uname[:version]
36
+ }
37
+ end
33
38
  end
34
39
 
35
40
  def runtime_context
36
41
  @runtime_context ||= {
37
- :name => RbConfig::CONFIG["ruby_install_name"],
38
- :version => Raven.sys_command("ruby -v")
42
+ name: RbConfig::CONFIG["ruby_install_name"],
43
+ version: RUBY_DESCRIPTION || Raven.sys_command("ruby -v")
39
44
  }
40
45
  end
41
46
  end
@@ -0,0 +1,57 @@
1
+ require 'raven/core_ext/object/duplicable'
2
+
3
+ #########################################
4
+ # This file was copied from Rails 5.2 #
5
+ #########################################
6
+
7
+ class Object
8
+ # Returns a deep copy of object if it's duplicable. If it's
9
+ # not duplicable, returns +self+.
10
+ #
11
+ # object = Object.new
12
+ # dup = object.deep_dup
13
+ # dup.instance_variable_set(:@a, 1)
14
+ #
15
+ # object.instance_variable_defined?(:@a) # => false
16
+ # dup.instance_variable_defined?(:@a) # => true
17
+ def deep_dup
18
+ duplicable? ? dup : self
19
+ end
20
+ end
21
+
22
+ class Array
23
+ # Returns a deep copy of array.
24
+ #
25
+ # array = [1, [2, 3]]
26
+ # dup = array.deep_dup
27
+ # dup[1][2] = 4
28
+ #
29
+ # array[1][2] # => nil
30
+ # dup[1][2] # => 4
31
+ def deep_dup
32
+ map(&:deep_dup)
33
+ end
34
+ end
35
+
36
+ class Hash
37
+ # Returns a deep copy of hash.
38
+ #
39
+ # hash = { a: { b: 'b' } }
40
+ # dup = hash.deep_dup
41
+ # dup[:a][:c] = 'c'
42
+ #
43
+ # hash[:a][:c] # => nil
44
+ # dup[:a][:c] # => "c"
45
+ def deep_dup
46
+ hash = dup
47
+ each_pair do |key, value|
48
+ if key.frozen? && ::String === key
49
+ hash[key] = value.deep_dup
50
+ else
51
+ hash.delete(key)
52
+ hash[key.deep_dup] = value.deep_dup
53
+ end
54
+ end
55
+ hash
56
+ end
57
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ #########################################
4
+ # This file was copied from Rails 5.2 #
5
+ #########################################
6
+
7
+ #--
8
+ # Most objects are cloneable, but not all. For example you can't dup methods:
9
+ #
10
+ # method(:puts).dup # => TypeError: allocator undefined for Method
11
+ #
12
+ # Classes may signal their instances are not duplicable removing +dup+/+clone+
13
+ # or raising exceptions from them. So, to dup an arbitrary object you normally
14
+ # use an optimistic approach and are ready to catch an exception, say:
15
+ #
16
+ # arbitrary_object.dup rescue object
17
+ #
18
+ # Rails dups objects in a few critical spots where they are not that arbitrary.
19
+ # That rescue is very expensive (like 40 times slower than a predicate), and it
20
+ # is often triggered.
21
+ #
22
+ # That's why we hardcode the following cases and check duplicable? instead of
23
+ # using that rescue idiom.
24
+ #++
25
+ class Object
26
+ # Can you safely dup this object?
27
+ #
28
+ # False for method objects;
29
+ # true otherwise.
30
+ def duplicable?
31
+ true
32
+ end
33
+ end
34
+
35
+ class NilClass
36
+ begin
37
+ nil.dup
38
+ rescue TypeError
39
+ # +nil+ is not duplicable:
40
+ #
41
+ # nil.duplicable? # => false
42
+ # nil.dup # => TypeError: can't dup NilClass
43
+ def duplicable?
44
+ false
45
+ end
46
+ end
47
+ end
48
+
49
+ class FalseClass
50
+ begin
51
+ false.dup
52
+ rescue TypeError
53
+ # +false+ is not duplicable:
54
+ #
55
+ # false.duplicable? # => false
56
+ # false.dup # => TypeError: can't dup FalseClass
57
+ def duplicable?
58
+ false
59
+ end
60
+ end
61
+ end
62
+
63
+ class TrueClass
64
+ begin
65
+ true.dup
66
+ rescue TypeError
67
+ # +true+ is not duplicable:
68
+ #
69
+ # true.duplicable? # => false
70
+ # true.dup # => TypeError: can't dup TrueClass
71
+ def duplicable?
72
+ false
73
+ end
74
+ end
75
+ end
76
+
77
+ class Symbol
78
+ begin
79
+ :symbol.dup # Ruby 2.4.x.
80
+ "symbol_from_string".to_sym.dup # Some symbols can't `dup` in Ruby 2.4.0.
81
+ rescue TypeError
82
+ # Symbols are not duplicable:
83
+ #
84
+ # :my_symbol.duplicable? # => false
85
+ # :my_symbol.dup # => TypeError: can't dup Symbol
86
+ def duplicable?
87
+ false
88
+ end
89
+ end
90
+ end
91
+
92
+ class Numeric
93
+ begin
94
+ 1.dup
95
+ rescue TypeError
96
+ # Numbers are not duplicable:
97
+ #
98
+ # 3.duplicable? # => false
99
+ # 3.dup # => TypeError: can't dup Integer
100
+ def duplicable?
101
+ false
102
+ end
103
+ end
104
+ end
105
+
106
+ require "bigdecimal"
107
+ class BigDecimal
108
+ # BigDecimals are duplicable:
109
+ #
110
+ # BigDecimal("1.2").duplicable? # => true
111
+ # BigDecimal("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
112
+ def duplicable?
113
+ true
114
+ end
115
+ end
116
+
117
+ class Method
118
+ # Methods are not duplicable:
119
+ #
120
+ # method(:puts).duplicable? # => false
121
+ # method(:puts).dup # => TypeError: allocator undefined for Method
122
+ def duplicable?
123
+ false
124
+ end
125
+ end
126
+
127
+ class Complex
128
+ begin
129
+ Complex(1).dup
130
+ rescue TypeError
131
+ # Complexes are not duplicable:
132
+ #
133
+ # Complex(1).duplicable? # => false
134
+ # Complex(1).dup # => TypeError: can't copy Complex
135
+ def duplicable?
136
+ false
137
+ end
138
+ end
139
+ end
140
+
141
+ class Rational
142
+ begin
143
+ Rational(1).dup
144
+ rescue TypeError
145
+ # Rationals are not duplicable:
146
+ #
147
+ # Rational(1).duplicable? # => false
148
+ # Rational(1).dup # => TypeError: can't copy Rational
149
+ def duplicable?
150
+ false
151
+ end
152
+ end
153
+ end