sentry-raven 1.1.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.
- checksums.yaml +5 -5
- data/.craft.yml +19 -0
- data/.scripts/bump-version.rb +5 -0
- data/CHANGELOG.md +703 -0
- data/Gemfile +37 -0
- data/Makefile +3 -0
- data/README.md +116 -18
- data/Rakefile +30 -0
- data/exe/raven +32 -0
- data/lib/raven/backtrace.rb +17 -8
- data/lib/raven/base.rb +45 -194
- data/lib/raven/breadcrumbs/active_support_logger.rb +25 -0
- data/lib/raven/breadcrumbs/logger.rb +3 -0
- data/lib/raven/breadcrumbs/sentry_logger.rb +73 -0
- data/lib/raven/breadcrumbs.rb +76 -0
- data/lib/raven/cli.rb +31 -39
- data/lib/raven/client.rb +45 -32
- data/lib/raven/configuration.rb +427 -130
- data/lib/raven/context.rb +33 -6
- data/lib/raven/core_ext/object/deep_dup.rb +57 -0
- data/lib/raven/core_ext/object/duplicable.rb +153 -0
- data/lib/raven/event.rb +194 -206
- data/lib/raven/helpers/deprecation_helper.rb +17 -0
- data/lib/raven/instance.rb +249 -0
- data/lib/raven/integrations/delayed_job.rb +25 -23
- data/lib/raven/integrations/rack-timeout.rb +22 -0
- data/lib/raven/integrations/rack.rb +40 -24
- data/lib/raven/integrations/rails/active_job.rb +52 -20
- data/lib/raven/integrations/rails/backtrace_cleaner.rb +29 -0
- data/lib/raven/integrations/rails/controller_transaction.rb +13 -0
- data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
- data/lib/raven/integrations/rails.rb +39 -7
- data/lib/raven/integrations/rake.rb +7 -2
- data/lib/raven/integrations/sidekiq/cleanup_middleware.rb +13 -0
- data/lib/raven/integrations/sidekiq/error_handler.rb +38 -0
- data/lib/raven/integrations/sidekiq.rb +6 -48
- data/lib/raven/integrations/tasks.rb +1 -1
- data/lib/raven/interface.rb +25 -0
- data/lib/raven/interfaces/exception.rb +5 -8
- data/lib/raven/interfaces/http.rb +5 -12
- data/lib/raven/interfaces/message.rb +10 -6
- data/lib/raven/interfaces/single_exception.rb +1 -5
- data/lib/raven/interfaces/stack_trace.rb +23 -30
- data/lib/raven/linecache.rb +35 -23
- data/lib/raven/logger.rb +13 -16
- data/lib/raven/processor/cookies.rb +27 -7
- data/lib/raven/processor/http_headers.rb +55 -0
- data/lib/raven/processor/post_data.rb +16 -3
- data/lib/raven/processor/removecircularreferences.rb +12 -8
- data/lib/raven/processor/removestacktrace.rb +17 -6
- data/lib/raven/processor/sanitizedata.rb +92 -37
- data/lib/raven/processor/utf8conversion.rb +39 -14
- data/lib/raven/processor.rb +5 -1
- data/lib/raven/transports/http.rb +31 -22
- data/lib/raven/transports/stdout.rb +20 -0
- data/lib/raven/transports.rb +6 -10
- data/lib/raven/utils/context_filter.rb +42 -0
- data/lib/raven/utils/deep_merge.rb +6 -12
- data/lib/raven/utils/exception_cause_chain.rb +20 -0
- data/lib/raven/utils/real_ip.rb +62 -0
- data/lib/raven/utils/request_id.rb +16 -0
- data/lib/raven/version.rb +2 -1
- data/lib/sentry-raven-without-integrations.rb +6 -1
- data/lib/sentry_raven_without_integrations.rb +1 -0
- data/sentry-raven.gemspec +28 -0
- metadata +44 -127
- data/lib/raven/error.rb +0 -4
- data/lib/raven/interfaces.rb +0 -34
- data/lib/raven/okjson.rb +0 -614
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
module Raven
|
|
2
|
+
# A copy of Raven's base module class methods, minus some of the integration
|
|
3
|
+
# and global hooks since it's meant to be used explicitly. Useful for
|
|
4
|
+
# sending errors to multiple sentry projects in a large application.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# class Foo
|
|
8
|
+
# def initialize
|
|
9
|
+
# @other_raven = Raven::Instance.new
|
|
10
|
+
# @other_raven.configure do |config|
|
|
11
|
+
# config.server = 'http://...'
|
|
12
|
+
# end
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# def foo
|
|
16
|
+
# # ...
|
|
17
|
+
# rescue => e
|
|
18
|
+
# @other_raven.capture_exception(e)
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
class Instance
|
|
22
|
+
# See Raven::Client.
|
|
23
|
+
attr_writer :client
|
|
24
|
+
|
|
25
|
+
# See Raven::Configuration.
|
|
26
|
+
attr_accessor :configuration
|
|
27
|
+
|
|
28
|
+
def initialize(context = nil, config = nil)
|
|
29
|
+
@context = @explicit_context = context
|
|
30
|
+
self.configuration = config || Configuration.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def context
|
|
34
|
+
if @explicit_context
|
|
35
|
+
@context ||= Context.new
|
|
36
|
+
else
|
|
37
|
+
Context.current
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def logger
|
|
42
|
+
configuration.logger
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The client object is responsible for delivering formatted data to the
|
|
46
|
+
# Sentry server.
|
|
47
|
+
def client
|
|
48
|
+
@client ||= Client.new(configuration)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Tell the log that the client is good to go
|
|
52
|
+
def report_status
|
|
53
|
+
return unless configuration.enabled_in_current_env?
|
|
54
|
+
return if configuration.silence_ready
|
|
55
|
+
|
|
56
|
+
if configuration.capture_allowed?
|
|
57
|
+
logger.info "Raven #{VERSION} ready to catch errors"
|
|
58
|
+
else
|
|
59
|
+
logger.info "Raven #{VERSION} configured not to capture errors: #{configuration.error_messages}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Call this method to modify defaults in your initializers.
|
|
64
|
+
#
|
|
65
|
+
# @example
|
|
66
|
+
# Raven.configure do |config|
|
|
67
|
+
# config.server = 'http://...'
|
|
68
|
+
# end
|
|
69
|
+
def configure
|
|
70
|
+
yield(configuration) if block_given?
|
|
71
|
+
|
|
72
|
+
self.client = Client.new(configuration)
|
|
73
|
+
report_status
|
|
74
|
+
client
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Send an event to the configured Sentry server
|
|
78
|
+
#
|
|
79
|
+
# @example
|
|
80
|
+
# evt = Raven::Event.new(:message => "An errore)
|
|
81
|
+
# Raven.send_event(evt)
|
|
82
|
+
def send_event(event, hint = nil)
|
|
83
|
+
client.send_event(event, hint)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Capture and process any exceptions from the given block.
|
|
87
|
+
#
|
|
88
|
+
# @example
|
|
89
|
+
# Raven.capture do
|
|
90
|
+
# MyApp.run
|
|
91
|
+
# end
|
|
92
|
+
def capture(options = {})
|
|
93
|
+
if block_given?
|
|
94
|
+
begin
|
|
95
|
+
yield
|
|
96
|
+
rescue Error
|
|
97
|
+
raise # Don't capture Raven errors
|
|
98
|
+
rescue Exception => e
|
|
99
|
+
capture_type(e, options)
|
|
100
|
+
raise
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
install_at_exit_hook(options)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def capture_type(obj, options = {})
|
|
108
|
+
unless configuration.capture_allowed?(obj)
|
|
109
|
+
logger.debug("#{obj} excluded from capture: #{configuration.error_messages}")
|
|
110
|
+
return false
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
message_or_exc = obj.is_a?(String) ? "message" : "exception"
|
|
114
|
+
options = options.deep_dup
|
|
115
|
+
options[:configuration] = configuration
|
|
116
|
+
options[:context] = context
|
|
117
|
+
options[:breadcrumbs] = breadcrumbs
|
|
118
|
+
|
|
119
|
+
if evt = Event.send("from_" + message_or_exc, obj, options)
|
|
120
|
+
yield evt if block_given?
|
|
121
|
+
if configuration.async?
|
|
122
|
+
begin
|
|
123
|
+
# We have to convert to a JSON-like hash, because background job
|
|
124
|
+
# processors (esp ActiveJob) may not like weird types in the event hash
|
|
125
|
+
configuration.async.call(evt.to_json_compatible)
|
|
126
|
+
rescue => e
|
|
127
|
+
logger.error("async event sending failed: #{e.message}")
|
|
128
|
+
send_event(evt, make_hint(obj))
|
|
129
|
+
end
|
|
130
|
+
else
|
|
131
|
+
send_event(evt, make_hint(obj))
|
|
132
|
+
end
|
|
133
|
+
Thread.current["sentry_#{object_id}_last_event_id".to_sym] = evt.id
|
|
134
|
+
evt
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
alias capture_message capture_type
|
|
139
|
+
alias capture_exception capture_type
|
|
140
|
+
|
|
141
|
+
def last_event_id
|
|
142
|
+
Thread.current["sentry_#{object_id}_last_event_id".to_sym]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Provides extra context to the exception prior to it being handled by
|
|
146
|
+
# Raven. An exception can have multiple annotations, which are merged
|
|
147
|
+
# together.
|
|
148
|
+
#
|
|
149
|
+
# The options (annotation) is treated the same as the ``options``
|
|
150
|
+
# parameter to ``capture_exception`` or ``Event.from_exception``, and
|
|
151
|
+
# can contain the same ``:user``, ``:tags``, etc. options as these
|
|
152
|
+
# methods.
|
|
153
|
+
#
|
|
154
|
+
# These will be merged with the ``options`` parameter to
|
|
155
|
+
# ``Event.from_exception`` at the top of execution.
|
|
156
|
+
#
|
|
157
|
+
# @example
|
|
158
|
+
# begin
|
|
159
|
+
# raise "Hello"
|
|
160
|
+
# rescue => exc
|
|
161
|
+
# Raven.annotate_exception(exc, :user => { 'id' => 1,
|
|
162
|
+
# 'email' => 'foo@example.com' })
|
|
163
|
+
# end
|
|
164
|
+
def annotate_exception(exc, options = {})
|
|
165
|
+
notes = (exc.instance_variable_defined?(:@__raven_context) && exc.instance_variable_get(:@__raven_context)) || {}
|
|
166
|
+
Raven::Utils::DeepMergeHash.deep_merge!(notes, options)
|
|
167
|
+
exc.instance_variable_set(:@__raven_context, notes)
|
|
168
|
+
exc
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Bind user context. Merges with existing context (if any).
|
|
172
|
+
#
|
|
173
|
+
# It is recommending that you send at least the ``id`` and ``email``
|
|
174
|
+
# values. All other values are arbitrary.
|
|
175
|
+
#
|
|
176
|
+
# @example
|
|
177
|
+
# Raven.user_context('id' => 1, 'email' => 'foo@example.com')
|
|
178
|
+
def user_context(options = nil)
|
|
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?
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Bind tags context. Merges with existing context (if any).
|
|
194
|
+
#
|
|
195
|
+
# Tags are key / value pairs which generally represent things like
|
|
196
|
+
# application version, environment, role, and server names.
|
|
197
|
+
#
|
|
198
|
+
# @example
|
|
199
|
+
# Raven.tags_context('my_custom_tag' => 'tag_value')
|
|
200
|
+
def tags_context(options = nil)
|
|
201
|
+
context.tags.merge!(options || {})
|
|
202
|
+
yield if block_given?
|
|
203
|
+
context.tags
|
|
204
|
+
ensure
|
|
205
|
+
context.tags.delete_if { |k, _| options.keys.include? k } if block_given?
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Bind extra context. Merges with existing context (if any).
|
|
209
|
+
#
|
|
210
|
+
# Extra context shows up as Additional Data within Sentry, and is
|
|
211
|
+
# completely arbitrary.
|
|
212
|
+
#
|
|
213
|
+
# @example
|
|
214
|
+
# Raven.extra_context('my_custom_data' => 'value')
|
|
215
|
+
def extra_context(options = nil)
|
|
216
|
+
context.extra.merge!(options || {})
|
|
217
|
+
yield if block_given?
|
|
218
|
+
context.extra
|
|
219
|
+
ensure
|
|
220
|
+
context.extra.delete_if { |k, _| options.keys.include? k } if block_given?
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def rack_context(env)
|
|
224
|
+
env = nil if env.empty?
|
|
225
|
+
|
|
226
|
+
context.rack_env = env
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def breadcrumbs
|
|
230
|
+
BreadcrumbBuffer.current
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
private
|
|
234
|
+
|
|
235
|
+
def install_at_exit_hook(options)
|
|
236
|
+
at_exit do
|
|
237
|
+
exception = $ERROR_INFO
|
|
238
|
+
if exception
|
|
239
|
+
logger.debug "Caught a post-mortem exception: #{exception.inspect}"
|
|
240
|
+
capture_type(exception, options)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def make_hint(obj)
|
|
246
|
+
obj.is_a?(String) ? { :exception => nil, :message => obj } : { :exception => obj, :message => nil }
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -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,45 +9,46 @@ 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
|
|
17
|
-
:priority
|
|
18
|
-
:attempts
|
|
19
|
-
:run_at
|
|
20
|
-
:locked_at
|
|
21
|
-
:locked_by
|
|
22
|
-
:queue
|
|
23
|
-
:created_at
|
|
24
|
-
|
|
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
|
+
}
|
|
25
25
|
}
|
|
26
26
|
# last_error can be nil
|
|
27
|
-
extra[:last_error] = job.last_error[0...
|
|
27
|
+
extra[:last_error] = job.last_error[0...1000] if job.last_error
|
|
28
28
|
# handlers are YAML objects in strings, we definitely can't
|
|
29
29
|
# report all of that or the event will get truncated randomly
|
|
30
|
-
extra[:handler] = job.handler[0...
|
|
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(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
::Raven.capture_exception(e,
|
|
36
|
+
:logger => 'delayed_job',
|
|
37
|
+
:tags => {
|
|
38
|
+
:delayed_job_queue => job.queue,
|
|
39
|
+
:delayed_job_id => job.id.to_s
|
|
40
|
+
},
|
|
41
|
+
:extra => extra)
|
|
42
42
|
|
|
43
43
|
# Make sure we propagate the failure!
|
|
44
|
-
raise
|
|
44
|
+
raise e
|
|
45
|
+
ensure
|
|
46
|
+
::Raven::Context.clear!
|
|
47
|
+
::Raven::BreadcrumbBuffer.clear!
|
|
45
48
|
end
|
|
46
49
|
end
|
|
47
50
|
end
|
|
48
51
|
end
|
|
49
|
-
|
|
50
52
|
end
|
|
51
53
|
end
|
|
52
54
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# We need to do this because of the way integration loading works
|
|
2
|
+
require "rack/timeout/base" unless defined?(Rack::Timeout)
|
|
3
|
+
|
|
4
|
+
# This integration is a good example of how to change how exceptions
|
|
5
|
+
# get grouped by Sentry's UI. Simply override #raven_context in
|
|
6
|
+
# the exception class, and append something to the fingerprint
|
|
7
|
+
# that will distinguish exceptions in the way you desire.
|
|
8
|
+
module RackTimeoutExtensions
|
|
9
|
+
def raven_context
|
|
10
|
+
# Only rack-timeout 0.3.0+ provides the request environment, but we can't
|
|
11
|
+
# gate this based on a gem version constant because rack-timeout does
|
|
12
|
+
# not provide one.
|
|
13
|
+
if defined?(env)
|
|
14
|
+
{ :fingerprint => ["{{ default }}", env["REQUEST_URI"]] }
|
|
15
|
+
else
|
|
16
|
+
{}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Rack::Timeout::Error.include(RackTimeoutExtensions)
|
|
22
|
+
Rack::Timeout::RequestTimeoutException.include(RackTimeoutExtensions)
|
|
@@ -41,20 +41,17 @@ module Raven
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def call(env)
|
|
44
|
-
# clear context at the beginning of the request to ensure a clean slate
|
|
45
|
-
Context.clear!
|
|
46
|
-
|
|
47
44
|
# store the current environment in our local context for arbitrary
|
|
48
45
|
# callers
|
|
49
46
|
env['raven.requested_at'] = Time.now
|
|
50
47
|
Raven.rack_context(env)
|
|
48
|
+
Raven.context.transaction.push(env["PATH_INFO"]) if env["PATH_INFO"]
|
|
51
49
|
|
|
52
50
|
begin
|
|
53
51
|
response = @app.call(env)
|
|
54
52
|
rescue Error
|
|
55
53
|
raise # Don't capture Raven errors
|
|
56
54
|
rescue Exception => e
|
|
57
|
-
Raven.logger.debug "Collecting %p: %s" % [ e.class, e.message ]
|
|
58
55
|
Raven::Rack.capture_exception(e, env)
|
|
59
56
|
raise
|
|
60
57
|
end
|
|
@@ -63,6 +60,9 @@ module Raven
|
|
|
63
60
|
Raven::Rack.capture_exception(error, env) if error
|
|
64
61
|
|
|
65
62
|
response
|
|
63
|
+
ensure
|
|
64
|
+
Context.clear!
|
|
65
|
+
BreadcrumbBuffer.clear!
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
@@ -74,47 +74,63 @@ module Raven
|
|
|
74
74
|
self.method = req.request_method
|
|
75
75
|
self.query_string = req.query_string
|
|
76
76
|
self.data = read_data_from(req)
|
|
77
|
+
self.cookies = req.cookies
|
|
77
78
|
|
|
78
79
|
self.headers = format_headers_for_sentry(env_hash)
|
|
79
|
-
self.env
|
|
80
|
+
self.env = format_env_for_sentry(env_hash)
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
private
|
|
83
84
|
|
|
85
|
+
# See Sentry server default limits at
|
|
86
|
+
# https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
|
|
84
87
|
def read_data_from(request)
|
|
85
88
|
if request.form_data?
|
|
86
89
|
request.POST
|
|
87
|
-
elsif request.body
|
|
88
|
-
data = request.body.read
|
|
90
|
+
elsif request.body # JSON requests, etc
|
|
91
|
+
data = request.body.read(4096 * 4) # Sentry server limit
|
|
89
92
|
request.body.rewind
|
|
90
93
|
data
|
|
91
94
|
end
|
|
95
|
+
rescue IOError => e
|
|
96
|
+
e.message
|
|
92
97
|
end
|
|
93
98
|
|
|
94
99
|
def format_headers_for_sentry(env_hash)
|
|
95
100
|
env_hash.each_with_object({}) do |(key, value), memo|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
begin
|
|
102
|
+
key = key.to_s # rack env can contain symbols
|
|
103
|
+
next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env_hash) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
|
|
104
|
+
next unless key.upcase == key # Non-upper case stuff isn't either
|
|
105
|
+
|
|
106
|
+
# Rack adds in an incorrect HTTP_VERSION key, which causes downstream
|
|
107
|
+
# to think this is a Version header. Instead, this is mapped to
|
|
108
|
+
# env['SERVER_PROTOCOL']. But we don't want to ignore a valid header
|
|
109
|
+
# if the request has legitimately sent a Version header themselves.
|
|
110
|
+
# See: https://github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29
|
|
111
|
+
next if key == 'HTTP_VERSION' && value == env_hash['SERVER_PROTOCOL']
|
|
112
|
+
next if key == 'HTTP_COOKIE' # Cookies don't go here, they go somewhere else
|
|
113
|
+
next unless key.start_with?('HTTP_') || %w(CONTENT_TYPE CONTENT_LENGTH).include?(key)
|
|
114
|
+
|
|
115
|
+
# Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
|
|
116
|
+
key = key.sub(/^HTTP_/, "")
|
|
117
|
+
key = key.split('_').map(&:capitalize).join('-')
|
|
118
|
+
memo[key] = value.to_s
|
|
119
|
+
rescue StandardError => e
|
|
120
|
+
# Rails adds objects to the Rack env that can sometimes raise exceptions
|
|
121
|
+
# when `to_s` is called.
|
|
122
|
+
# See: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L134
|
|
123
|
+
Raven.logger.warn("Error raised while formatting headers: #{e.message}")
|
|
124
|
+
next
|
|
125
|
+
end
|
|
112
126
|
end
|
|
113
127
|
end
|
|
114
128
|
|
|
115
129
|
def format_env_for_sentry(env_hash)
|
|
130
|
+
return env_hash if Raven.configuration.rack_env_whitelist.empty?
|
|
131
|
+
|
|
116
132
|
env_hash.select do |k, _v|
|
|
117
|
-
|
|
133
|
+
Raven.configuration.rack_env_whitelist.include? k.to_s
|
|
118
134
|
end
|
|
119
135
|
end
|
|
120
136
|
end
|
|
@@ -1,31 +1,63 @@
|
|
|
1
1
|
module Raven
|
|
2
2
|
class Rails
|
|
3
|
-
module
|
|
3
|
+
module ActiveJobExtensions
|
|
4
|
+
ALREADY_SUPPORTED_SENTRY_ADAPTERS = %w(
|
|
5
|
+
ActiveJob::QueueAdapters::SidekiqAdapter
|
|
6
|
+
ActiveJob::QueueAdapters::DelayedJobAdapter
|
|
7
|
+
).freeze
|
|
8
|
+
|
|
4
9
|
def self.included(base)
|
|
5
10
|
base.class_eval do
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
:active_job => self.class.name,
|
|
12
|
-
:arguments => arguments,
|
|
13
|
-
:scheduled_at => scheduled_at,
|
|
14
|
-
:job_id => job_id,
|
|
15
|
-
:locale => locale,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
# Add provider_job_id details if Rails 5
|
|
19
|
-
if defined?(provider_job_id)
|
|
20
|
-
active_job_details[:provider_job_id] = provider_job_id
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
Raven.capture_exception(exception, :extra => active_job_details)
|
|
24
|
-
raise exception
|
|
11
|
+
around_perform do |job, block|
|
|
12
|
+
if already_supported_by_specific_integration?(job)
|
|
13
|
+
block.call
|
|
14
|
+
else
|
|
15
|
+
capture_and_reraise_with_sentry(job, block)
|
|
25
16
|
end
|
|
26
17
|
end
|
|
27
18
|
end
|
|
28
19
|
end
|
|
20
|
+
|
|
21
|
+
def capture_and_reraise_with_sentry(job, block)
|
|
22
|
+
block.call
|
|
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
|
|
29
|
+
ensure
|
|
30
|
+
Context.clear!
|
|
31
|
+
BreadcrumbBuffer.clear!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def already_supported_by_specific_integration?(job)
|
|
35
|
+
if ::Rails.version.to_f < 5.0
|
|
36
|
+
ALREADY_SUPPORTED_SENTRY_ADAPTERS.include?(job.class.queue_adapter.to_s)
|
|
37
|
+
else
|
|
38
|
+
ALREADY_SUPPORTED_SENTRY_ADAPTERS.include?(job.class.queue_adapter.class.to_s)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def raven_context(job)
|
|
43
|
+
ctx = {
|
|
44
|
+
:active_job => job.class.name,
|
|
45
|
+
:arguments => job.arguments,
|
|
46
|
+
:scheduled_at => job.scheduled_at,
|
|
47
|
+
:job_id => job.job_id,
|
|
48
|
+
:locale => job.locale
|
|
49
|
+
}
|
|
50
|
+
# Add provider_job_id details if Rails 5
|
|
51
|
+
if job.respond_to?(:provider_job_id)
|
|
52
|
+
ctx[:provider_job_id] = job.provider_job_id
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
ctx
|
|
56
|
+
end
|
|
29
57
|
end
|
|
30
58
|
end
|
|
31
59
|
end
|
|
60
|
+
|
|
61
|
+
class ActiveJob::Base
|
|
62
|
+
include Raven::Rails::ActiveJobExtensions
|
|
63
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Raven
|
|
2
|
+
class Rails
|
|
3
|
+
module ControllerTransaction
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.prepend_around_action do |controller, block|
|
|
6
|
+
Raven.context.transaction.push "#{controller.class}##{controller.action_name}"
|
|
7
|
+
block.call
|
|
8
|
+
Raven.context.transaction.pop
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
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
|
|
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
|
|
24
|
+
rescue
|
|
25
25
|
end
|
|
26
26
|
render_exception_without_raven(env_or_request, exception)
|
|
27
27
|
end
|