sentry-raven 3.0.0 → 3.1.2

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 +19 -0
  3. data/.scripts/bump-version.rb +5 -0
  4. data/{changelog.md → CHANGELOG.md} +174 -1
  5. data/Gemfile +24 -25
  6. data/Makefile +3 -0
  7. data/README.md +44 -16
  8. data/lib/raven/backtrace.rb +9 -5
  9. data/lib/raven/base.rb +7 -2
  10. data/lib/raven/breadcrumbs.rb +1 -1
  11. data/lib/raven/breadcrumbs/{activesupport.rb → active_support_logger.rb} +9 -3
  12. data/lib/raven/breadcrumbs/logger.rb +2 -92
  13. data/lib/raven/breadcrumbs/sentry_logger.rb +73 -0
  14. data/lib/raven/cli.rb +10 -21
  15. data/lib/raven/client.rb +9 -4
  16. data/lib/raven/configuration.rb +95 -10
  17. data/lib/raven/context.rb +13 -8
  18. data/lib/raven/core_ext/object/deep_dup.rb +57 -0
  19. data/lib/raven/core_ext/object/duplicable.rb +153 -0
  20. data/lib/raven/event.rb +31 -15
  21. data/lib/raven/helpers/deprecation_helper.rb +17 -0
  22. data/lib/raven/instance.rb +21 -5
  23. data/lib/raven/integrations/delayed_job.rb +15 -15
  24. data/lib/raven/integrations/rack-timeout.rb +7 -4
  25. data/lib/raven/integrations/rack.rb +8 -6
  26. data/lib/raven/integrations/rails.rb +13 -3
  27. data/lib/raven/integrations/rails/active_job.rb +6 -4
  28. data/lib/raven/integrations/rails/backtrace_cleaner.rb +29 -0
  29. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  30. data/lib/raven/integrations/sidekiq.rb +4 -78
  31. data/lib/raven/integrations/sidekiq/cleanup_middleware.rb +13 -0
  32. data/lib/raven/integrations/sidekiq/error_handler.rb +38 -0
  33. data/lib/raven/interface.rb +2 -2
  34. data/lib/raven/interfaces/stack_trace.rb +1 -1
  35. data/lib/raven/linecache.rb +5 -2
  36. data/lib/raven/logger.rb +3 -2
  37. data/lib/raven/processor/cookies.rb +16 -6
  38. data/lib/raven/processor/post_data.rb +2 -0
  39. data/lib/raven/processor/removecircularreferences.rb +3 -1
  40. data/lib/raven/processor/sanitizedata.rb +65 -17
  41. data/lib/raven/processor/utf8conversion.rb +2 -0
  42. data/lib/raven/transports.rb +4 -0
  43. data/lib/raven/transports/http.rb +7 -8
  44. data/lib/raven/utils/context_filter.rb +42 -0
  45. data/lib/raven/utils/exception_cause_chain.rb +1 -0
  46. data/lib/raven/utils/real_ip.rb +1 -1
  47. data/lib/raven/utils/request_id.rb +16 -0
  48. data/lib/raven/version.rb +2 -2
  49. data/lib/sentry-raven-without-integrations.rb +6 -1
  50. data/lib/sentry_raven_without_integrations.rb +1 -0
  51. data/sentry-raven.gemspec +7 -0
  52. metadata +22 -10
  53. data/.gitignore +0 -13
  54. data/.gitmodules +0 -0
  55. data/.rspec +0 -1
  56. data/.rubocop.yml +0 -74
  57. data/.travis.yml +0 -43
@@ -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
@@ -8,19 +9,18 @@ module Delayed
8
9
  begin
9
10
  # Forward the call to the next callback in the callback chain
10
11
  block.call(job, *args)
11
-
12
- rescue Exception => exception
12
+ rescue Exception => e
13
13
  # Log error to Sentry
14
14
  extra = {
15
15
  :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
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
24
24
  }
25
25
  }
26
26
  # last_error can be nil
@@ -30,18 +30,18 @@ module Delayed
30
30
  extra[:handler] = job.handler[0...1000] if job.handler
31
31
 
32
32
  if job.respond_to?('payload_object') && job.payload_object.respond_to?('job_data')
33
- extra[:active_job] = job.payload_object.job_data
33
+ extra[:active_job] = ::Raven::Utils::ContextFilter.filter_context(job.payload_object.job_data)
34
34
  end
35
- ::Raven.capture_exception(exception,
36
- :logger => 'delayed_job',
37
- :tags => {
35
+ ::Raven.capture_exception(e,
36
+ :logger => 'delayed_job',
37
+ :tags => {
38
38
  :delayed_job_queue => job.queue,
39
39
  :delayed_job_id => job.id.to_s
40
40
  },
41
41
  :extra => extra)
42
42
 
43
43
  # Make sure we propagate the failure!
44
- raise exception
44
+ raise e
45
45
  ensure
46
46
  ::Raven::Context.clear!
47
47
  ::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,15 +92,15 @@ 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)
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,12 +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)
114
+
115
115
  # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
116
116
  key = key.sub(/^HTTP_/, "")
117
117
  key = key.split('_').map(&:capitalize).join('-')
118
- memo[key] = value
118
+ memo[key] = value.to_s
119
119
  rescue StandardError => e
120
120
  # Rails adds objects to the Rack env that can sometimes raise exceptions
121
121
  # when `to_s` is called.
@@ -127,8 +127,10 @@ module Raven
127
127
  end
128
128
 
129
129
  def format_env_for_sentry(env_hash)
130
+ return env_hash if Raven.configuration.rack_env_whitelist.empty?
131
+
130
132
  env_hash.select do |k, _v|
131
- %w(REMOTE_ADDR SERVER_NAME SERVER_PORT).include? k.to_s
133
+ Raven.configuration.rack_env_whitelist.include? k.to_s
132
134
  end
133
135
  end
134
136
  end
@@ -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
@@ -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
@@ -1,9 +1,9 @@
1
1
  module Raven
2
2
  class Interface
3
3
  def initialize(attributes = nil)
4
- attributes.each do |attr, value|
4
+ attributes&.each do |attr, value|
5
5
  public_send "#{attr}=", value
6
- end if attributes
6
+ end
7
7
 
8
8
  yield self if block_given?
9
9
  end
@@ -58,7 +58,7 @@ module Raven
58
58
  end
59
59
 
60
60
  def project_root
61
- @project_root ||= Raven.configuration.project_root && Raven.configuration.project_root.to_s
61
+ @project_root ||= Raven.configuration.project_root&.to_s
62
62
  end
63
63
 
64
64
  def longest_load_path
@@ -10,6 +10,7 @@ module Raven
10
10
  # line should be the line requested by lineno. See specs for more information.
11
11
  def get_file_context(filename, lineno, context)
12
12
  return nil, nil, nil unless valid_path?(filename)
13
+
13
14
  lines = Array.new(2 * context + 1) do |i|
14
15
  getline(filename, lineno - context + i)
15
16
  end
@@ -26,15 +27,17 @@ module Raven
26
27
  def getlines(path)
27
28
  @cache[path] ||= begin
28
29
  IO.readlines(path)
29
- rescue
30
- nil
30
+ rescue
31
+ nil
31
32
  end
32
33
  end
33
34
 
34
35
  def getline(path, n)
35
36
  return nil if n < 1
37
+
36
38
  lines = getlines(path)
37
39
  return nil if lines.nil?
40
+
38
41
  lines[n - 1]
39
42
  end
40
43
  end
data/lib/raven/logger.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'logger'
3
4
 
4
5
  module Raven
5
6
  class Logger < ::Logger
6
- LOG_PREFIX = "** [Raven] ".freeze
7
- PROGNAME = "sentry".freeze
7
+ LOG_PREFIX = "** [Raven] "
8
+ PROGNAME = "sentry"
8
9
 
9
10
  def initialize(*)
10
11
  super
@@ -10,17 +10,27 @@ module Raven
10
10
  private
11
11
 
12
12
  def process_if_symbol_keys(data)
13
- data[:request][:cookies] = STRING_MASK if data[:request][:cookies]
13
+ if cookies = data.dig(:request, :cookies)
14
+ data[:request][:cookies] = generate_masked_cookies(cookies)
15
+ end
14
16
 
15
- return unless data[:request][:headers] && data[:request][:headers]["Cookie"]
16
- data[:request][:headers]["Cookie"] = STRING_MASK
17
+ if cookies_header = data[:request][:headers]["Cookie"]
18
+ data[:request][:headers]["Cookie"] = generate_masked_cookies(cookies_header)
19
+ end
17
20
  end
18
21
 
19
22
  def process_if_string_keys(data)
20
- data["request"]["cookies"] = STRING_MASK if data["request"]["cookies"]
23
+ if cookies = data.dig("request", "cookies")
24
+ data["request"]["cookies"] = generate_masked_cookies(cookies)
25
+ end
21
26
 
22
- return unless data["request"]["headers"] && data["request"]["headers"]["Cookie"]
23
- data["request"]["headers"]["Cookie"] = STRING_MASK
27
+ if cookies_header = data.dig("request", "headers", "Cookie")
28
+ data["request"]["headers"]["Cookie"] = generate_masked_cookies(cookies_header)
29
+ end
30
+ end
31
+
32
+ def generate_masked_cookies(cookies)
33
+ cookies.merge(cookies) { STRING_MASK } if cookies.respond_to?(:merge)
24
34
  end
25
35
  end
26
36
  end