sentry-raven 3.0.0 → 3.1.0

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