sentry-raven 2.9.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.craft.yml +14 -0
- data/.github/workflows/test.yml +77 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +44 -9
- data/.scripts/bump-version.sh +9 -0
- data/Gemfile +17 -25
- data/README.md +5 -4
- data/changelog.md +77 -1
- data/lib/raven/backtrace.rb +7 -5
- data/lib/raven/base.rb +5 -3
- data/lib/raven/breadcrumbs/activesupport.rb +10 -10
- data/lib/raven/breadcrumbs/logger.rb +4 -4
- data/lib/raven/breadcrumbs.rb +1 -1
- data/lib/raven/cli.rb +2 -2
- data/lib/raven/client.rb +28 -10
- data/lib/raven/configuration.rb +23 -8
- data/lib/raven/event.rb +5 -9
- data/lib/raven/instance.rb +12 -3
- data/lib/raven/integrations/delayed_job.rb +14 -15
- data/lib/raven/integrations/rack-timeout.rb +2 -3
- data/lib/raven/integrations/rack.rb +4 -3
- data/lib/raven/integrations/rails/active_job.rb +10 -7
- data/lib/raven/integrations/rails/controller_transaction.rb +1 -1
- data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
- data/lib/raven/integrations/rails.rb +1 -0
- data/lib/raven/interface.rb +2 -2
- data/lib/raven/interfaces/stack_trace.rb +1 -1
- data/lib/raven/linecache.rb +5 -2
- data/lib/raven/logger.rb +3 -2
- data/lib/raven/processor/cookies.rb +16 -6
- data/lib/raven/processor/post_data.rb +2 -0
- data/lib/raven/processor/removecircularreferences.rb +1 -0
- data/lib/raven/processor/sanitizedata.rb +65 -17
- data/lib/raven/processor/utf8conversion.rb +3 -1
- data/lib/raven/transports/http.rb +5 -5
- data/lib/raven/transports.rb +4 -0
- data/lib/raven/utils/exception_cause_chain.rb +1 -0
- data/lib/raven/utils/real_ip.rb +1 -1
- data/lib/raven/version.rb +2 -2
- data/sentry-raven.gemspec +3 -3
- metadata +8 -13
- data/.travis.yml +0 -47
@@ -1,19 +1,19 @@
|
|
1
1
|
module Raven
|
2
2
|
module ActiveSupportBreadcrumbs
|
3
3
|
class << self
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
8
|
-
::Logger::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*/)
|
@@ -36,7 +36,7 @@ module Raven
|
|
36
36
|
|
37
37
|
# some loggers will add leading/trailing space as they (incorrectly, mind you)
|
38
38
|
# think of logging as a shortcut to std{out,err}
|
39
|
-
message = message.strip
|
39
|
+
message = message.to_s.strip
|
40
40
|
|
41
41
|
last_crumb = Raven.breadcrumbs.peek
|
42
42
|
# try to avoid dupes from logger broadcasts
|
data/lib/raven/breadcrumbs.rb
CHANGED
data/lib/raven/cli.rb
CHANGED
data/lib/raven/client.rb
CHANGED
@@ -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'
|
10
|
-
USER_AGENT = "raven-ruby/#{Raven::VERSION}"
|
11
|
-
CONTENT_TYPE = 'application/json'
|
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
|
|
@@ -35,7 +38,8 @@ module Raven
|
|
35
38
|
return
|
36
39
|
end
|
37
40
|
|
38
|
-
|
41
|
+
event_id = event[:event_id] || event['event_id']
|
42
|
+
configuration.logger.info "Sending event #{event_id} to Sentry"
|
39
43
|
|
40
44
|
content_type, encoded_data = encode(event)
|
41
45
|
|
@@ -79,8 +83,20 @@ module Raven
|
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
86
|
+
def get_message_from_exception(event)
|
87
|
+
(
|
88
|
+
event &&
|
89
|
+
event[:exception] &&
|
90
|
+
event[:exception][:values] &&
|
91
|
+
event[:exception][:values][0] &&
|
92
|
+
event[:exception][:values][0][:type] &&
|
93
|
+
event[:exception][:values][0][:value] &&
|
94
|
+
"#{event[:exception][:values][0][:type]}: #{event[:exception][:values][0][:value]}"
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
82
98
|
def get_log_message(event)
|
83
|
-
(event && event[:message]) || '<no message value>'
|
99
|
+
(event && event[:message]) || (event && event['message']) || get_message_from_exception(event) || '<no message value>'
|
84
100
|
end
|
85
101
|
|
86
102
|
def generate_auth_header
|
@@ -100,14 +116,16 @@ module Raven
|
|
100
116
|
end
|
101
117
|
|
102
118
|
def failed_send(e, event)
|
103
|
-
@state.failure
|
104
119
|
if e # exception was raised
|
105
|
-
|
120
|
+
@state.failure
|
121
|
+
configuration.logger.warn "Unable to record event with remote Sentry server (#{e.class} - #{e.message}):\n#{e.backtrace[0..10].join("\n")}"
|
106
122
|
else
|
107
|
-
configuration.logger.
|
123
|
+
configuration.logger.warn "Not sending event due to previous failure(s)."
|
108
124
|
end
|
109
|
-
configuration.logger.
|
110
|
-
|
125
|
+
configuration.logger.warn("Failed to submit event: #{get_log_message(event)}")
|
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
|
111
129
|
end
|
112
130
|
end
|
113
131
|
|
data/lib/raven/configuration.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'uri'
|
2
2
|
|
3
3
|
module Raven
|
4
|
-
class Configuration
|
4
|
+
class Configuration
|
5
5
|
# Directories to be recognized as part of your app. e.g. if you
|
6
6
|
# have an `engines` dir at the root of your project, you may want
|
7
7
|
# to set this to something like /(app|config|engines|lib)/
|
@@ -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
|
257
|
-
@server
|
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
|
|
@@ -347,11 +353,12 @@ module Raven
|
|
347
353
|
end
|
348
354
|
|
349
355
|
def detect_release
|
350
|
-
|
356
|
+
detect_release_from_env ||
|
357
|
+
detect_release_from_git ||
|
351
358
|
detect_release_from_capistrano ||
|
352
359
|
detect_release_from_heroku
|
353
|
-
rescue =>
|
354
|
-
logger.error "Error detecting release: #{
|
360
|
+
rescue => e
|
361
|
+
logger.error "Error detecting release: #{e.message}"
|
355
362
|
end
|
356
363
|
|
357
364
|
def excluded_exception?(incoming_exception)
|
@@ -411,20 +418,27 @@ module Raven
|
|
411
418
|
Raven.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
|
412
419
|
end
|
413
420
|
|
421
|
+
def detect_release_from_env
|
422
|
+
ENV['SENTRY_RELEASE']
|
423
|
+
end
|
424
|
+
|
414
425
|
def capture_in_current_environment?
|
415
426
|
return true unless environments.any? && !environments.include?(current_environment)
|
427
|
+
|
416
428
|
@errors << "Not configured to send/capture in environment '#{current_environment}'"
|
417
429
|
false
|
418
430
|
end
|
419
431
|
|
420
432
|
def capture_allowed_by_callback?(message_or_exc)
|
421
|
-
return true if !should_capture || message_or_exc.nil? || should_capture.call(
|
433
|
+
return true if !should_capture || message_or_exc.nil? || should_capture.call(message_or_exc)
|
434
|
+
|
422
435
|
@errors << "should_capture returned false"
|
423
436
|
false
|
424
437
|
end
|
425
438
|
|
426
439
|
def valid?
|
427
440
|
return true if %w(server host path public_key project_id).all? { |k| public_send(k) }
|
441
|
+
|
428
442
|
if server
|
429
443
|
%w(server host path public_key project_id).map do |key|
|
430
444
|
@errors << "No #{key} specified" unless public_send(key)
|
@@ -437,6 +451,7 @@ module Raven
|
|
437
451
|
|
438
452
|
def sample_allowed?
|
439
453
|
return true if sample_rate == 1.0
|
454
|
+
|
440
455
|
if Random::DEFAULT.rand >= sample_rate
|
441
456
|
@errors << "Excluded by random sample"
|
442
457
|
false
|
@@ -453,7 +468,7 @@ module Raven
|
|
453
468
|
end
|
454
469
|
|
455
470
|
def current_environment_from_env
|
456
|
-
ENV['SENTRY_CURRENT_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
|
471
|
+
ENV['SENTRY_CURRENT_ENV'] || ENV['SENTRY_ENVIRONMENT'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
|
457
472
|
end
|
458
473
|
|
459
474
|
def server_name_from_env
|
data/lib/raven/event.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'socket'
|
3
4
|
require 'securerandom'
|
4
5
|
|
@@ -59,10 +60,7 @@ module Raven
|
|
59
60
|
return unless configuration.exception_class_allowed?(exc)
|
60
61
|
|
61
62
|
new(options) do |evt|
|
62
|
-
evt.message = "#{exc.class}: #{exc.message}"
|
63
|
-
|
64
63
|
evt.add_exception_interface(exc)
|
65
|
-
|
66
64
|
yield evt if block
|
67
65
|
end
|
68
66
|
end
|
@@ -79,7 +77,7 @@ module Raven
|
|
79
77
|
end
|
80
78
|
|
81
79
|
def message
|
82
|
-
@interfaces[:logentry]
|
80
|
+
@interfaces[:logentry]&.unformatted_message
|
83
81
|
end
|
84
82
|
|
85
83
|
def message=(args)
|
@@ -99,12 +97,13 @@ module Raven
|
|
99
97
|
end
|
100
98
|
|
101
99
|
def level=(new_level) # needed to meet the Sentry spec
|
102
|
-
@level = new_level == "warn"
|
100
|
+
@level = new_level.to_s == "warn" ? :warning : new_level
|
103
101
|
end
|
104
102
|
|
105
103
|
def interface(name, value = nil, &block)
|
106
104
|
int = Interface.registered[name]
|
107
105
|
raise(Error, "Unknown interface: #{name}") unless int
|
106
|
+
|
108
107
|
@interfaces[int.sentry_alias] = int.new(value, &block) if value || block
|
109
108
|
@interfaces[int.sentry_alias]
|
110
109
|
end
|
@@ -231,10 +230,7 @@ module Raven
|
|
231
230
|
end
|
232
231
|
|
233
232
|
def async_json_processors
|
234
|
-
|
235
|
-
Raven::Processor::RemoveCircularReferences,
|
236
|
-
Raven::Processor::UTF8Conversion
|
237
|
-
].map { |v| v.new(self) }
|
233
|
+
configuration.processors.map { |v| v.new(self) }
|
238
234
|
end
|
239
235
|
|
240
236
|
def list_gem_specs
|
data/lib/raven/instance.rb
CHANGED
@@ -51,6 +51,7 @@ module Raven
|
|
51
51
|
# Tell the log that the client is good to go
|
52
52
|
def report_status
|
53
53
|
return if configuration.silence_ready
|
54
|
+
|
54
55
|
if configuration.capture_allowed?
|
55
56
|
logger.info "Raven #{VERSION} ready to catch errors"
|
56
57
|
else
|
@@ -111,15 +112,15 @@ module Raven
|
|
111
112
|
message_or_exc = obj.is_a?(String) ? "message" : "exception"
|
112
113
|
options[:configuration] = configuration
|
113
114
|
options[:context] = context
|
114
|
-
if
|
115
|
+
if evt = Event.send("from_" + message_or_exc, obj, options)
|
115
116
|
yield evt if block_given?
|
116
117
|
if configuration.async?
|
117
118
|
begin
|
118
119
|
# We have to convert to a JSON-like hash, because background job
|
119
120
|
# processors (esp ActiveJob) may not like weird types in the event hash
|
120
121
|
configuration.async.call(evt.to_json_compatible)
|
121
|
-
rescue =>
|
122
|
-
logger.error("async event sending failed: #{
|
122
|
+
rescue => e
|
123
|
+
logger.error("async event sending failed: #{e.message}")
|
123
124
|
send_event(evt, make_hint(obj))
|
124
125
|
end
|
125
126
|
else
|
@@ -183,6 +184,10 @@ module Raven
|
|
183
184
|
# Raven.tags_context('my_custom_tag' => 'tag_value')
|
184
185
|
def tags_context(options = nil)
|
185
186
|
context.tags.merge!(options || {})
|
187
|
+
yield if block_given?
|
188
|
+
context.tags
|
189
|
+
ensure
|
190
|
+
context.tags.delete_if { |k, _| options.keys.include? k } if block_given?
|
186
191
|
end
|
187
192
|
|
188
193
|
# Bind extra context. Merges with existing context (if any).
|
@@ -194,6 +199,10 @@ module Raven
|
|
194
199
|
# Raven.extra_context('my_custom_data' => 'value')
|
195
200
|
def extra_context(options = nil)
|
196
201
|
context.extra.merge!(options || {})
|
202
|
+
yield if block_given?
|
203
|
+
context.extra
|
204
|
+
ensure
|
205
|
+
context.extra.delete_if { |k, _| options.keys.include? k } if block_given?
|
197
206
|
end
|
198
207
|
|
199
208
|
def rack_context(env)
|
@@ -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
|
17
|
-
:priority
|
18
|
-
:attempts
|
19
|
-
:run_at
|
20
|
-
:locked_at
|
21
|
-
:locked_by
|
22
|
-
:queue
|
23
|
-
: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(
|
36
|
-
:logger
|
37
|
-
:tags
|
34
|
+
::Raven.capture_exception(e,
|
35
|
+
:logger => 'delayed_job',
|
36
|
+
:tags => {
|
38
37
|
:delayed_job_queue => job.queue,
|
39
|
-
:delayed_job_id => job.id
|
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
|
43
|
+
raise e
|
45
44
|
ensure
|
46
45
|
::Raven::Context.clear!
|
47
46
|
::Raven::BreadcrumbBuffer.clear!
|
@@ -14,6 +14,5 @@ module RackTimeoutExtensions
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
Rack::Timeout::
|
19
|
-
Rack::Timeout::RequestTimeoutException.__send__(:include, RackTimeoutExtensions)
|
17
|
+
Rack::Timeout::Error.include(RackTimeoutExtensions)
|
18
|
+
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 =>
|
96
|
-
|
95
|
+
rescue IOError => e
|
96
|
+
e.message
|
97
97
|
end
|
98
98
|
|
99
99
|
def format_headers_for_sentry(env_hash)
|
@@ -112,8 +112,9 @@ 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
|
-
key = key.
|
117
|
+
key = key.sub(/^HTTP_/, "")
|
117
118
|
key = key.split('_').map(&:capitalize).join('-')
|
118
119
|
memo[key] = value
|
119
120
|
rescue StandardError => e
|