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,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'socket'
3
4
  require 'securerandom'
4
5
 
@@ -7,6 +8,7 @@ module Raven
7
8
  # See Sentry server default limits at
8
9
  # https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
9
10
  MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
11
+ REQUIRED_OPTION_KEYS = [:configuration, :context, :breadcrumbs].freeze
10
12
 
11
13
  SDK = { "name" => "raven-ruby", "version" => Raven::VERSION }.freeze
12
14
 
@@ -18,7 +20,7 @@ module Raven
18
20
 
19
21
  attr_reader :level, :timestamp, :time_spent
20
22
 
21
- def initialize(init = {})
23
+ def initialize(options)
22
24
  # Set some simple default values
23
25
  self.id = SecureRandom.uuid.delete("-")
24
26
  self.timestamp = Time.now.utc
@@ -35,11 +37,17 @@ module Raven
35
37
  self.runtime = {} # TODO: contexts
36
38
  self.tags = {} # TODO: contexts
37
39
 
38
- copy_initial_state
40
+ unless REQUIRED_OPTION_KEYS.all? { |key| options.key?(key) }
41
+ raise "you much provide configuration, context, and breadcrumbs when initializing a Raven::Event"
42
+ end
43
+
44
+ self.configuration = options[:configuration]
45
+ self.context = options[:context]
46
+ self.breadcrumbs = options[:breadcrumbs]
39
47
 
40
48
  # Allow attributes to be set on the event at initialization
41
49
  yield self if block_given?
42
- init.each_pair { |key, val| public_send("#{key}=", val) unless val.nil? }
50
+ options.each_pair { |key, val| public_send("#{key}=", val) unless val.nil? }
43
51
 
44
52
  set_core_attributes_from_configuration
45
53
  set_core_attributes_from_context
@@ -55,8 +63,7 @@ module Raven
55
63
  end
56
64
  options = Raven::Utils::DeepMergeHash.deep_merge(exception_context, options)
57
65
 
58
- configuration = options[:configuration] || Raven.configuration
59
- return unless configuration.exception_class_allowed?(exc)
66
+ return unless options[:configuration].exception_class_allowed?(exc)
60
67
 
61
68
  new(options) do |evt|
62
69
  evt.add_exception_interface(exc)
@@ -76,11 +83,21 @@ module Raven
76
83
  end
77
84
 
78
85
  def message
79
- @interfaces[:logentry] && @interfaces[:logentry].unformatted_message
86
+ @interfaces[:logentry]&.unformatted_message
80
87
  end
81
88
 
82
89
  def message=(args)
83
- message, params = *args
90
+ if args.is_a?(Array)
91
+ message, params = args[0], args[0..-1]
92
+ else
93
+ message = args
94
+ end
95
+
96
+ unless message.is_a?(String)
97
+ configuration.logger.debug("You're passing a non-string message")
98
+ message = message.to_s
99
+ end
100
+
84
101
  interface(:message) do |int|
85
102
  int.message = message.byteslice(0...MAX_MESSAGE_SIZE_IN_BYTES) # Messages limited to 10kb
86
103
  int.params = params
@@ -96,12 +113,13 @@ module Raven
96
113
  end
97
114
 
98
115
  def level=(new_level) # needed to meet the Sentry spec
99
- @level = new_level == "warn" || new_level == :warn ? :warning : new_level
116
+ @level = new_level.to_s == "warn" ? :warning : new_level
100
117
  end
101
118
 
102
119
  def interface(name, value = nil, &block)
103
120
  int = Interface.registered[name]
104
121
  raise(Error, "Unknown interface: #{name}") unless int
122
+
105
123
  @interfaces[int.sentry_alias] = int.new(value, &block) if value || block
106
124
  @interfaces[int.sentry_alias]
107
125
  end
@@ -157,7 +175,7 @@ module Raven
157
175
  end
158
176
 
159
177
  def stacktrace_interface_from(backtrace)
160
- Backtrace.parse(backtrace).lines.reverse.each_with_object([]) do |line, memo|
178
+ Backtrace.parse(backtrace, { configuration: configuration }).lines.reverse.each_with_object([]) do |line, memo|
161
179
  frame = StacktraceInterface::Frame.new
162
180
  frame.abs_path = line.file if line.file
163
181
  frame.function = line.method if line.method
@@ -184,12 +202,6 @@ module Raven
184
202
 
185
203
  private
186
204
 
187
- def copy_initial_state
188
- self.configuration = Raven.configuration
189
- self.breadcrumbs = Raven.breadcrumbs
190
- self.context = Raven.context
191
- end
192
-
193
205
  def set_core_attributes_from_configuration
194
206
  self.server_name ||= configuration.server_name
195
207
  self.release ||= configuration.release
@@ -0,0 +1,17 @@
1
+ module DeprecationHelper
2
+ def self.deprecate_dasherized_filename(correct_filename)
3
+ warn "[Deprecation Warning] Dasherized filename \"#{correct_filename.gsub('_', '-')}\" is deprecated and will be removed in 4.0; use \"#{correct_filename}\" instead" # rubocop:disable Style/LineLength
4
+ end
5
+
6
+ def self.deprecate_old_breadcrumbs_configuration(logger)
7
+ deprecated_usage =
8
+ if logger == :sentry_logger
9
+ "require \"raven/breadcrumbs/logger\""
10
+ else
11
+ "Raven.configuration.rails_activesupport_breadcrumbs = true"
12
+ end
13
+ recommended_usage = "Raven.configuration.breadcrumbs_logger = :#{logger}"
14
+
15
+ warn "[Deprecation Warning] The way you enable breadcrumbs logger (#{deprecated_usage}) is deprecated and will be removed in 4.0; use '#{recommended_usage}' instead" # rubocop:disable Style/LineLength
16
+ end
17
+ end
@@ -50,7 +50,9 @@ module Raven
50
50
 
51
51
  # Tell the log that the client is good to go
52
52
  def report_status
53
+ return unless configuration.enabled_in_current_env?
53
54
  return if configuration.silence_ready
55
+
54
56
  if configuration.capture_allowed?
55
57
  logger.info "Raven #{VERSION} ready to catch errors"
56
58
  else
@@ -75,7 +77,7 @@ module Raven
75
77
  # Send an event to the configured Sentry server
76
78
  #
77
79
  # @example
78
- # evt = Raven::Event.new(:message => "An error")
80
+ # evt = Raven::Event.new(:message => "An errore)
79
81
  # Raven.send_event(evt)
80
82
  def send_event(event, hint = nil)
81
83
  client.send_event(event, hint)
@@ -109,17 +111,20 @@ module Raven
109
111
  end
110
112
 
111
113
  message_or_exc = obj.is_a?(String) ? "message" : "exception"
114
+ options = options.deep_dup
112
115
  options[:configuration] = configuration
113
116
  options[:context] = context
114
- if (evt = Event.send("from_" + message_or_exc, obj, options))
117
+ options[:breadcrumbs] = breadcrumbs
118
+
119
+ if evt = Event.send("from_" + message_or_exc, obj, options)
115
120
  yield evt if block_given?
116
121
  if configuration.async?
117
122
  begin
118
123
  # We have to convert to a JSON-like hash, because background job
119
124
  # processors (esp ActiveJob) may not like weird types in the event hash
120
125
  configuration.async.call(evt.to_json_compatible)
121
- rescue => ex
122
- logger.error("async event sending failed: #{ex.message}")
126
+ rescue => e
127
+ logger.error("async event sending failed: #{e.message}")
123
128
  send_event(evt, make_hint(obj))
124
129
  end
125
130
  else
@@ -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.to_s,
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
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!
@@ -10,10 +10,13 @@ module RackTimeoutExtensions
10
10
  # Only rack-timeout 0.3.0+ provides the request environment, but we can't
11
11
  # gate this based on a gem version constant because rack-timeout does
12
12
  # not provide one.
13
- { :fingerprint => ["{{ default }}", env["REQUEST_URI"]] } if defined?(env)
13
+ if defined?(env)
14
+ { :fingerprint => ["{{ default }}", env["REQUEST_URI"]] }
15
+ else
16
+ {}
17
+ end
14
18
  end
15
19
  end
16
20
 
17
- # Include is private in Ruby 1.9
18
- Rack::Timeout::Error.__send__(:include, RackTimeoutExtensions)
19
- Rack::Timeout::RequestTimeoutException.__send__(:include, RackTimeoutExtensions)
21
+ Rack::Timeout::Error.include(RackTimeoutExtensions)
22
+ 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,6 +112,7 @@ 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
117
  key = key.sub(/^HTTP_/, "")
117
118
  key = key.split('_').map(&:capitalize).join('-')
@@ -5,6 +5,8 @@ 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/rails/backtrace_cleaner'
9
+ require 'raven/integrations/rack'
8
10
 
9
11
  initializer "raven.use_rack_middleware" do |app|
10
12
  app.config.middleware.insert 0, Raven::Rack
@@ -36,12 +38,20 @@ module Raven
36
38
 
37
39
  config.before_initialize do
38
40
  Raven.configuration.logger = ::Rails.logger
41
+
42
+ backtrace_cleaner = Raven::Rails::BacktraceCleaner.new
43
+
44
+ Raven.configuration.backtrace_cleanup_callback = lambda do |backtrace|
45
+ backtrace_cleaner.clean(backtrace)
46
+ end
39
47
  end
40
48
 
41
49
  config.after_initialize do
42
- if Raven.configuration.rails_activesupport_breadcrumbs
43
- require 'raven/breadcrumbs/activesupport'
44
- Raven::ActiveSupportBreadcrumbs.inject
50
+ if Raven.configuration.breadcrumbs_logger.include?(:active_support_logger) ||
51
+ Raven.configuration.rails_activesupport_breadcrumbs
52
+
53
+ require 'raven/breadcrumbs/active_support_logger'
54
+ Raven::Breadcrumbs::ActiveSupportLogger.inject
45
55
  end
46
56
 
47
57
  if Raven.configuration.rails_report_rescued_exceptions
@@ -20,10 +20,12 @@ module Raven
20
20
 
21
21
  def capture_and_reraise_with_sentry(job, block)
22
22
  block.call
23
- rescue Exception => exception # rubocop:disable Lint/RescueException
24
- return if rescue_with_handler(exception)
25
- Raven.capture_exception(exception, :extra => raven_context(job))
26
- raise exception
23
+ rescue Exception => e # rubocop:disable Lint/RescueException
24
+ rescue_handler_result = rescue_with_handler(e)
25
+ return rescue_handler_result if rescue_handler_result
26
+
27
+ Raven.capture_exception(e, :extra => raven_context(job))
28
+ raise e
27
29
  ensure
28
30
  Context.clear!
29
31
  BreadcrumbBuffer.clear!
@@ -0,0 +1,29 @@
1
+ require "active_support/backtrace_cleaner"
2
+ require "active_support/core_ext/string/access"
3
+
4
+ module Raven
5
+ class Rails
6
+ class BacktraceCleaner < ActiveSupport::BacktraceCleaner
7
+ APP_DIRS_PATTERN = /\A(?:\.\/)?(?:app|config|lib|test|\(\w*\))/.freeze
8
+ RENDER_TEMPLATE_PATTERN = /:in `.*_\w+_{2,3}\d+_\d+'/.freeze
9
+
10
+ def initialize
11
+ super
12
+ # we don't want any default silencers because they're too aggressive
13
+ remove_silencers!
14
+
15
+ @root = "#{Raven.configuration.project_root}/"
16
+ add_filter do |line|
17
+ line.start_with?(@root) ? line.from(@root.size) : line
18
+ end
19
+ add_filter do |line|
20
+ if line =~ RENDER_TEMPLATE_PATTERN
21
+ line.sub(RENDER_TEMPLATE_PATTERN, "")
22
+ else
23
+ line
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -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,87 +1,13 @@
1
1
  require 'time'
2
2
  require 'sidekiq'
3
-
4
- module Raven
5
- class SidekiqCleanupMiddleware
6
- def call(_worker, job, queue)
7
- Raven.context.transaction.push "Sidekiq/#{job['class']}"
8
- Raven.extra_context(:sidekiq => job.merge("queue" => queue))
9
- yield
10
- Context.clear!
11
- BreadcrumbBuffer.clear!
12
- end
13
- end
14
-
15
- class SidekiqErrorHandler
16
- ACTIVEJOB_RESERVED_PREFIX = "_aj_".freeze
17
- HAS_GLOBALID = const_defined?('GlobalID')
18
-
19
- def call(ex, context)
20
- context = filter_context(context)
21
- Raven.context.transaction.push transaction_from_context(context)
22
- Raven.capture_exception(
23
- ex,
24
- :message => ex.message,
25
- :extra => { :sidekiq => context }
26
- )
27
- Context.clear!
28
- BreadcrumbBuffer.clear!
29
- end
30
-
31
- private
32
-
33
- # Once an ActiveJob is queued, ActiveRecord references get serialized into
34
- # some internal reserved keys, such as _aj_globalid.
35
- #
36
- # The problem is, if this job in turn gets queued back into ActiveJob with
37
- # these magic reserved keys, ActiveJob will throw up and error. We want to
38
- # capture these and mutate the keys so we can sanely report it.
39
- def filter_context(context)
40
- case context
41
- when Array
42
- context.map { |arg| filter_context(arg) }
43
- when Hash
44
- Hash[context.map { |key, value| filter_context_hash(key, value) }]
45
- else
46
- format_globalid(context)
47
- end
48
- end
49
-
50
- def filter_context_hash(key, value)
51
- (key = key[3..-1]) if key [0..3] == ACTIVEJOB_RESERVED_PREFIX
52
- [key, filter_context(value)]
53
- end
54
-
55
- # this will change in the future:
56
- # https://github.com/mperham/sidekiq/pull/3161
57
- def transaction_from_context(context)
58
- classname = (context["wrapped"] || context["class"] ||
59
- (context[:job] && (context[:job]["wrapped"] || context[:job]["class"]))
60
- )
61
- if classname
62
- "Sidekiq/#{classname}"
63
- elsif context[:event]
64
- "Sidekiq/#{context[:event]}"
65
- else
66
- "Sidekiq"
67
- end
68
- end
69
-
70
- def format_globalid(context)
71
- if HAS_GLOBALID && context.is_a?(GlobalID)
72
- context.to_s
73
- else
74
- context
75
- end
76
- end
77
- end
78
- end
3
+ require 'raven/integrations/sidekiq/cleanup_middleware'
4
+ require 'raven/integrations/sidekiq/error_handler'
79
5
 
80
6
  if Sidekiq::VERSION > '3'
81
7
  Sidekiq.configure_server do |config|
82
- config.error_handlers << Raven::SidekiqErrorHandler.new
8
+ config.error_handlers << Raven::Sidekiq::ErrorHandler.new
83
9
  config.server_middleware do |chain|
84
- chain.add Raven::SidekiqCleanupMiddleware
10
+ chain.add Raven::Sidekiq::CleanupMiddleware
85
11
  end
86
12
  end
87
13
  end