sentry-raven 3.0.2 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.craft.yml +9 -5
  3. data/.scripts/bump-version.rb +5 -0
  4. data/{changelog.md → CHANGELOG.md} +165 -8
  5. data/Gemfile +10 -3
  6. data/Makefile +3 -0
  7. data/README.md +42 -15
  8. data/lib/raven/backtrace.rb +2 -0
  9. data/lib/raven/base.rb +3 -0
  10. data/lib/raven/breadcrumbs/active_support_logger.rb +25 -0
  11. data/lib/raven/breadcrumbs/logger.rb +2 -92
  12. data/lib/raven/breadcrumbs/sentry_logger.rb +73 -0
  13. data/lib/raven/cli.rb +8 -19
  14. data/lib/raven/client.rb +1 -1
  15. data/lib/raven/configuration.rb +80 -5
  16. data/lib/raven/context.rb +13 -8
  17. data/lib/raven/core_ext/object/deep_dup.rb +57 -0
  18. data/lib/raven/core_ext/object/duplicable.rb +153 -0
  19. data/lib/raven/event.rb +27 -13
  20. data/lib/raven/helpers/deprecation_helper.rb +17 -0
  21. data/lib/raven/instance.rb +17 -2
  22. data/lib/raven/integrations/delayed_job.rb +2 -1
  23. data/lib/raven/integrations/rack-timeout.rb +5 -1
  24. data/lib/raven/integrations/rack.rb +5 -4
  25. data/lib/raven/integrations/rails.rb +12 -3
  26. data/lib/raven/integrations/rails/active_job.rb +2 -1
  27. data/lib/raven/integrations/rails/backtrace_cleaner.rb +29 -0
  28. data/lib/raven/integrations/sidekiq.rb +4 -78
  29. data/lib/raven/integrations/sidekiq/cleanup_middleware.rb +13 -0
  30. data/lib/raven/integrations/sidekiq/error_handler.rb +38 -0
  31. data/lib/raven/processor/cookies.rb +1 -1
  32. data/lib/raven/processor/removecircularreferences.rb +2 -1
  33. data/lib/raven/transports/http.rb +2 -3
  34. data/lib/raven/utils/context_filter.rb +42 -0
  35. data/lib/raven/utils/request_id.rb +16 -0
  36. data/lib/raven/version.rb +1 -1
  37. data/lib/sentry-raven-without-integrations.rb +6 -1
  38. data/lib/sentry_raven_without_integrations.rb +1 -0
  39. data/sentry-raven.gemspec +7 -0
  40. metadata +21 -11
  41. data/.github/workflows/test.yml +0 -77
  42. data/.gitignore +0 -15
  43. data/.gitmodules +0 -0
  44. data/.rspec +0 -1
  45. data/.rubocop.yml +0 -109
  46. data/.scripts/bump-version.sh +0 -9
  47. data/lib/raven/breadcrumbs/activesupport.rb +0 -19
data/lib/raven/event.rb CHANGED
@@ -8,6 +8,7 @@ module Raven
8
8
  # See Sentry server default limits at
9
9
  # https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
10
10
  MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
11
+ REQUIRED_OPTION_KEYS = [:configuration, :context, :breadcrumbs].freeze
11
12
 
12
13
  SDK = { "name" => "raven-ruby", "version" => Raven::VERSION }.freeze
13
14
 
@@ -19,7 +20,7 @@ module Raven
19
20
 
20
21
  attr_reader :level, :timestamp, :time_spent
21
22
 
22
- def initialize(init = {})
23
+ def initialize(options)
23
24
  # Set some simple default values
24
25
  self.id = SecureRandom.uuid.delete("-")
25
26
  self.timestamp = Time.now.utc
@@ -36,11 +37,17 @@ module Raven
36
37
  self.runtime = {} # TODO: contexts
37
38
  self.tags = {} # TODO: contexts
38
39
 
39
- copy_initial_state
40
+ unless REQUIRED_OPTION_KEYS.all? { |key| options.key?(key) }
41
+ raise "you must 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]
40
47
 
41
48
  # Allow attributes to be set on the event at initialization
42
49
  yield self if block_given?
43
- 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? }
44
51
 
45
52
  set_core_attributes_from_configuration
46
53
  set_core_attributes_from_context
@@ -56,8 +63,7 @@ module Raven
56
63
  end
57
64
  options = Raven::Utils::DeepMergeHash.deep_merge(exception_context, options)
58
65
 
59
- configuration = options[:configuration] || Raven.configuration
60
- return unless configuration.exception_class_allowed?(exc)
66
+ return unless options[:configuration].exception_class_allowed?(exc)
61
67
 
62
68
  new(options) do |evt|
63
69
  evt.add_exception_interface(exc)
@@ -81,7 +87,17 @@ module Raven
81
87
  end
82
88
 
83
89
  def message=(args)
84
- 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
+
85
101
  interface(:message) do |int|
86
102
  int.message = message.byteslice(0...MAX_MESSAGE_SIZE_IN_BYTES) # Messages limited to 10kb
87
103
  int.params = params
@@ -159,7 +175,7 @@ module Raven
159
175
  end
160
176
 
161
177
  def stacktrace_interface_from(backtrace)
162
- 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|
163
179
  frame = StacktraceInterface::Frame.new
164
180
  frame.abs_path = line.file if line.file
165
181
  frame.function = line.method if line.method
@@ -186,12 +202,6 @@ module Raven
186
202
 
187
203
  private
188
204
 
189
- def copy_initial_state
190
- self.configuration = Raven.configuration
191
- self.breadcrumbs = Raven.breadcrumbs
192
- self.context = Raven.context
193
- end
194
-
195
205
  def set_core_attributes_from_configuration
196
206
  self.server_name ||= configuration.server_name
197
207
  self.release ||= configuration.release
@@ -216,6 +226,10 @@ module Raven
216
226
  int.from_rack(context.rack_env)
217
227
  end
218
228
  context.user[:ip_address] = calculate_real_ip_from_rack
229
+
230
+ if request_id = Utils::RequestId.read_from(context.rack_env)
231
+ context.tags[:request_id] = request_id
232
+ end
219
233
  end
220
234
 
221
235
  # When behind a proxy (or if the user is using a proxy), we can't use
@@ -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,6 +50,7 @@ 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
54
55
 
55
56
  if configuration.capture_allowed?
@@ -76,7 +77,7 @@ module Raven
76
77
  # Send an event to the configured Sentry server
77
78
  #
78
79
  # @example
79
- # evt = Raven::Event.new(:message => "An error")
80
+ # evt = Raven::Event.new(:message => "An errore)
80
81
  # Raven.send_event(evt)
81
82
  def send_event(event, hint = nil)
82
83
  client.send_event(event, hint)
@@ -110,8 +111,11 @@ module Raven
110
111
  end
111
112
 
112
113
  message_or_exc = obj.is_a?(String) ? "message" : "exception"
114
+ options = options.deep_dup
113
115
  options[:configuration] = configuration
114
116
  options[:context] = context
117
+ options[:breadcrumbs] = breadcrumbs
118
+
115
119
  if evt = Event.send("from_" + message_or_exc, obj, options)
116
120
  yield evt if block_given?
117
121
  if configuration.async?
@@ -172,7 +176,18 @@ module Raven
172
176
  # @example
173
177
  # Raven.user_context('id' => 1, 'email' => 'foo@example.com')
174
178
  def user_context(options = nil)
175
- context.user = options || {}
179
+ original_user_context = context.user
180
+
181
+ if options
182
+ context.user.merge!(options)
183
+ else
184
+ context.user = {}
185
+ end
186
+
187
+ yield if block_given?
188
+ context.user
189
+ ensure
190
+ context.user = original_user_context if block_given?
176
191
  end
177
192
 
178
193
  # Bind tags context. Merges with existing context (if any).
@@ -1,4 +1,5 @@
1
1
  require 'delayed_job'
2
+ require 'raven/utils/context_filter'
2
3
 
3
4
  module Delayed
4
5
  module Plugins
@@ -29,7 +30,7 @@ module Delayed
29
30
  extra[:handler] = job.handler[0...1000] if job.handler
30
31
 
31
32
  if job.respond_to?('payload_object') && job.payload_object.respond_to?('job_data')
32
- extra[:active_job] = job.payload_object.job_data
33
+ extra[:active_job] = ::Raven::Utils::ContextFilter.filter_context(job.payload_object.job_data)
33
34
  end
34
35
  ::Raven.capture_exception(e,
35
36
  :logger => 'delayed_job',
@@ -10,7 +10,11 @@ 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
 
@@ -100,7 +100,7 @@ module Raven
100
100
  env_hash.each_with_object({}) do |(key, value), memo|
101
101
  begin
102
102
  key = key.to_s # rack env can contain symbols
103
- value = value.to_s
103
+ next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env_hash) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
104
104
  next unless key.upcase == key # Non-upper case stuff isn't either
105
105
 
106
106
  # Rack adds in an incorrect HTTP_VERSION key, which causes downstream
@@ -110,13 +110,12 @@ module Raven
110
110
  # See: https://github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29
111
111
  next if key == 'HTTP_VERSION' && value == env_hash['SERVER_PROTOCOL']
112
112
  next if key == 'HTTP_COOKIE' # Cookies don't go here, they go somewhere else
113
-
114
113
  next unless key.start_with?('HTTP_') || %w(CONTENT_TYPE CONTENT_LENGTH).include?(key)
115
114
 
116
115
  # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
117
116
  key = key.sub(/^HTTP_/, "")
118
117
  key = key.split('_').map(&:capitalize).join('-')
119
- memo[key] = value
118
+ memo[key] = value.to_s
120
119
  rescue StandardError => e
121
120
  # Rails adds objects to the Rack env that can sometimes raise exceptions
122
121
  # when `to_s` is called.
@@ -128,8 +127,10 @@ module Raven
128
127
  end
129
128
 
130
129
  def format_env_for_sentry(env_hash)
130
+ return env_hash if Raven.configuration.rack_env_whitelist.empty?
131
+
131
132
  env_hash.select do |k, _v|
132
- %w(REMOTE_ADDR SERVER_NAME SERVER_PORT).include? k.to_s
133
+ Raven.configuration.rack_env_whitelist.include? k.to_s
133
134
  end
134
135
  end
135
136
  end
@@ -5,6 +5,7 @@ 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'
8
9
  require 'raven/integrations/rack'
9
10
 
10
11
  initializer "raven.use_rack_middleware" do |app|
@@ -37,12 +38,20 @@ module Raven
37
38
 
38
39
  config.before_initialize do
39
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
40
47
  end
41
48
 
42
49
  config.after_initialize do
43
- if Raven.configuration.rails_activesupport_breadcrumbs
44
- require 'raven/breadcrumbs/activesupport'
45
- 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
46
55
  end
47
56
 
48
57
  if Raven.configuration.rails_report_rescued_exceptions
@@ -21,7 +21,8 @@ module Raven
21
21
  def capture_and_reraise_with_sentry(job, block)
22
22
  block.call
23
23
  rescue Exception => e # rubocop:disable Lint/RescueException
24
- return if rescue_with_handler(e)
24
+ rescue_handler_result = rescue_with_handler(e)
25
+ return rescue_handler_result if rescue_handler_result
25
26
 
26
27
  Raven.capture_exception(e, :extra => raven_context(job))
27
28
  raise e
@@ -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
@@ -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
@@ -0,0 +1,13 @@
1
+ module Raven
2
+ module Sidekiq
3
+ class CleanupMiddleware
4
+ def call(_worker, job, queue)
5
+ Raven.context.transaction.push "Sidekiq/#{job['class']}"
6
+ Raven.extra_context(:sidekiq => job.merge("queue" => queue))
7
+ yield
8
+ Context.clear!
9
+ BreadcrumbBuffer.clear!
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ require 'raven/utils/context_filter'
2
+
3
+ module Raven
4
+ module Sidekiq
5
+ class ErrorHandler
6
+ SIDEKIQ_NAME = "Sidekiq".freeze
7
+
8
+ def call(ex, context)
9
+ context = Utils::ContextFilter.filter_context(context)
10
+ Raven.context.transaction.push transaction_from_context(context)
11
+ Raven.capture_exception(
12
+ ex,
13
+ :message => ex.message,
14
+ :extra => { :sidekiq => context }
15
+ )
16
+ Context.clear!
17
+ BreadcrumbBuffer.clear!
18
+ end
19
+
20
+ private
21
+
22
+ # this will change in the future:
23
+ # https://github.com/mperham/sidekiq/pull/3161
24
+ def transaction_from_context(context)
25
+ classname = (context["wrapped"] || context["class"] ||
26
+ (context[:job] && (context[:job]["wrapped"] || context[:job]["class"]))
27
+ )
28
+ if classname
29
+ "#{SIDEKIQ_NAME}/#{classname}"
30
+ elsif context[:event]
31
+ "#{SIDEKIQ_NAME}/#{context[:event]}"
32
+ else
33
+ SIDEKIQ_NAME
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end