sentry-raven 2.13.0 → 3.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.craft.yml +15 -0
  3. data/.github/workflows/test.yml +87 -0
  4. data/.github/workflows/zeus_upload.yml +32 -0
  5. data/.gitignore +2 -0
  6. data/.rubocop.yml +44 -9
  7. data/.scripts/bump-version.sh +9 -0
  8. data/{changelog.md → CHANGELOG.md} +38 -1
  9. data/CONTRIUTING.md +26 -0
  10. data/Gemfile +20 -25
  11. data/README.md +24 -14
  12. data/lib/raven/backtrace.rb +7 -5
  13. data/lib/raven/base.rb +5 -2
  14. data/lib/raven/breadcrumbs.rb +1 -1
  15. data/lib/raven/breadcrumbs/activesupport.rb +10 -10
  16. data/lib/raven/breadcrumbs/logger.rb +3 -3
  17. data/lib/raven/cli.rb +9 -20
  18. data/lib/raven/client.rb +9 -4
  19. data/lib/raven/configuration.rb +20 -6
  20. data/lib/raven/core_ext/object/deep_dup.rb +57 -0
  21. data/lib/raven/core_ext/object/duplicable.rb +153 -0
  22. data/lib/raven/event.rb +4 -2
  23. data/lib/raven/instance.rb +6 -3
  24. data/lib/raven/integrations/delayed_job.rb +13 -14
  25. data/lib/raven/integrations/rack-timeout.rb +2 -3
  26. data/lib/raven/integrations/rack.rb +4 -3
  27. data/lib/raven/integrations/rails.rb +1 -0
  28. data/lib/raven/integrations/rails/active_job.rb +5 -4
  29. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  30. data/lib/raven/interface.rb +2 -2
  31. data/lib/raven/interfaces/stack_trace.rb +1 -1
  32. data/lib/raven/linecache.rb +5 -2
  33. data/lib/raven/logger.rb +3 -2
  34. data/lib/raven/processor/cookies.rb +16 -6
  35. data/lib/raven/processor/post_data.rb +2 -0
  36. data/lib/raven/processor/removecircularreferences.rb +3 -1
  37. data/lib/raven/processor/sanitizedata.rb +65 -17
  38. data/lib/raven/processor/utf8conversion.rb +2 -0
  39. data/lib/raven/transports.rb +4 -0
  40. data/lib/raven/transports/http.rb +5 -5
  41. data/lib/raven/utils/exception_cause_chain.rb +1 -0
  42. data/lib/raven/utils/real_ip.rb +1 -1
  43. data/lib/raven/version.rb +2 -2
  44. data/sentry-raven.gemspec +2 -2
  45. metadata +11 -11
  46. data/.travis.yml +0 -47
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ## Inspired by Rails' and Airbrake's backtrace parsers.
2
4
 
3
5
  module Raven
@@ -5,16 +7,16 @@ module Raven
5
7
  class Backtrace
6
8
  # Handles backtrace parsing line by line
7
9
  class Line
8
- RB_EXTENSION = ".rb".freeze
10
+ RB_EXTENSION = ".rb"
9
11
  # regexp (optional leading X: on windows, or JRuby9000 class-prefix)
10
12
  RUBY_INPUT_FORMAT = /
11
13
  ^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
12
14
  (\d+)
13
15
  (?: :in \s `([^']+)')?$
14
- /x
16
+ /x.freeze
15
17
 
16
18
  # org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
17
- JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/
19
+ JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/.freeze
18
20
 
19
21
  # The file portion of the line (such as app/models/user.rb)
20
22
  attr_reader :file
@@ -74,7 +76,7 @@ module Raven
74
76
 
75
77
  def self.in_app_pattern
76
78
  @in_app_pattern ||= begin
77
- project_root = Raven.configuration.project_root && Raven.configuration.project_root.to_s
79
+ project_root = Raven.configuration.project_root&.to_s
78
80
  Regexp.new("^(#{project_root}/)?#{Raven.configuration.app_dirs_pattern || APP_DIRS_PATTERN}")
79
81
  end
80
82
  end
@@ -84,7 +86,7 @@ module Raven
84
86
  attr_writer :file, :number, :method, :module_name
85
87
  end
86
88
 
87
- APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test)/
89
+ APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test)/.freeze
88
90
 
89
91
  # holder for an Array of Backtrace::Line instances
90
92
  attr_reader :lines
@@ -1,4 +1,5 @@
1
1
  require 'raven/version'
2
+ require 'raven/core_ext/object/deep_dup'
2
3
  require 'raven/backtrace'
3
4
  require 'raven/breadcrumbs'
4
5
  require 'raven/processor'
@@ -85,12 +86,13 @@ module Raven
85
86
 
86
87
  def load_integration(integration)
87
88
  require "raven/integrations/#{integration}"
88
- rescue Exception => error
89
- logger.warn "Unable to load raven/integrations/#{integration}: #{error}"
89
+ rescue Exception => e
90
+ logger.warn "Unable to load raven/integrations/#{integration}: #{e}"
90
91
  end
91
92
 
92
93
  def safely_prepend(module_name, opts = {})
93
94
  return if opts[:to].nil? || opts[:from].nil?
95
+
94
96
  if opts[:to].respond_to?(:prepend, true)
95
97
  opts[:to].send(:prepend, opts[:from].const_get(module_name))
96
98
  else
@@ -101,6 +103,7 @@ module Raven
101
103
  def sys_command(command)
102
104
  result = `#{command} 2>&1` rescue nil
103
105
  return if result.nil? || result.empty? || ($CHILD_STATUS && $CHILD_STATUS.exitstatus != 0)
106
+
104
107
  result.strip
105
108
  end
106
109
  end
@@ -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
@@ -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*/)
@@ -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) if configuration.transport_failure_callback # rubocop:disable Style/SafeNavigation
124
129
  end
125
130
  end
126
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
 
@@ -336,6 +342,10 @@ module Raven
336
342
  end
337
343
  end
338
344
 
345
+ def enabled_in_current_env?
346
+ environments.empty? || environments.include?(current_environment)
347
+ end
348
+
339
349
  private
340
350
 
341
351
  def detect_project_root
@@ -351,8 +361,8 @@ module Raven
351
361
  detect_release_from_git ||
352
362
  detect_release_from_capistrano ||
353
363
  detect_release_from_heroku
354
- rescue => ex
355
- logger.error "Error detecting release: #{ex.message}"
364
+ rescue => e
365
+ logger.error "Error detecting release: #{e.message}"
356
366
  end
357
367
 
358
368
  def excluded_exception?(incoming_exception)
@@ -417,19 +427,22 @@ module Raven
417
427
  end
418
428
 
419
429
  def capture_in_current_environment?
420
- return true unless environments.any? && !environments.include?(current_environment)
430
+ return true if enabled_in_current_env?
431
+
421
432
  @errors << "Not configured to send/capture in environment '#{current_environment}'"
422
433
  false
423
434
  end
424
435
 
425
436
  def capture_allowed_by_callback?(message_or_exc)
426
- return true if !should_capture || message_or_exc.nil? || should_capture.call(*[message_or_exc])
437
+ return true if !should_capture || message_or_exc.nil? || should_capture.call(message_or_exc)
438
+
427
439
  @errors << "should_capture returned false"
428
440
  false
429
441
  end
430
442
 
431
443
  def valid?
432
444
  return true if %w(server host path public_key project_id).all? { |k| public_send(k) }
445
+
433
446
  if server
434
447
  %w(server host path public_key project_id).map do |key|
435
448
  @errors << "No #{key} specified" unless public_send(key)
@@ -442,6 +455,7 @@ module Raven
442
455
 
443
456
  def sample_allowed?
444
457
  return true if sample_rate == 1.0
458
+
445
459
  if Random::DEFAULT.rand >= sample_rate
446
460
  @errors << "Excluded by random sample"
447
461
  false
@@ -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