rollbar 2.12.0 → 2.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -6
- data/README.md +58 -8
- data/docs/configuration.md +12 -0
- data/gemfiles/rails30.gemfile +1 -0
- data/gemfiles/rails31.gemfile +1 -0
- data/gemfiles/rails32.gemfile +1 -0
- data/gemfiles/rails40.gemfile +3 -0
- data/gemfiles/rails41.gemfile +1 -0
- data/gemfiles/rails42.gemfile +7 -1
- data/gemfiles/rails50.gemfile +2 -1
- data/gemfiles/ruby_1_8_and_1_9_2.gemfile +3 -1
- data/lib/rollbar.rb +70 -654
- data/lib/rollbar/configuration.rb +32 -0
- data/lib/rollbar/item.rb +16 -6
- data/lib/rollbar/item/backtrace.rb +26 -17
- data/lib/rollbar/item/frame.rb +112 -0
- data/lib/rollbar/middleware/js.rb +39 -35
- data/lib/rollbar/middleware/rails/rollbar.rb +3 -3
- data/lib/rollbar/notifier.rb +645 -0
- data/lib/rollbar/plugins/delayed_job/job_data.rb +40 -21
- data/lib/rollbar/plugins/rails.rb +2 -2
- data/lib/rollbar/plugins/rake.rb +32 -6
- data/lib/rollbar/plugins/resque.rb +11 -0
- data/lib/rollbar/plugins/resque/failure.rb +39 -0
- data/lib/rollbar/plugins/validations.rb +10 -0
- data/lib/rollbar/request_data_extractor.rb +36 -18
- data/lib/rollbar/scrubbers/params.rb +2 -1
- data/lib/rollbar/truncation.rb +1 -1
- data/lib/rollbar/truncation/frames_strategy.rb +2 -1
- data/lib/rollbar/truncation/min_body_strategy.rb +2 -1
- data/lib/rollbar/truncation/strings_strategy.rb +1 -1
- data/lib/rollbar/version.rb +1 -1
- data/spec/controllers/home_controller_spec.rb +13 -24
- data/spec/delayed/backend/test.rb +1 -0
- data/spec/requests/home_spec.rb +1 -1
- data/spec/rollbar/configuration_spec.rb +22 -0
- data/spec/rollbar/item/backtrace_spec.rb +26 -0
- data/spec/rollbar/item/frame_spec.rb +267 -0
- data/spec/rollbar/item_spec.rb +27 -2
- data/spec/rollbar/middleware/js_spec.rb +23 -0
- data/spec/rollbar/middleware/sinatra_spec.rb +7 -7
- data/spec/rollbar/notifier_spec.rb +43 -0
- data/spec/rollbar/plugins/delayed_job/{job_data.rb → job_data_spec.rb} +15 -2
- data/spec/rollbar/plugins/rack_spec.rb +7 -7
- data/spec/rollbar/plugins/rake_spec.rb +1 -2
- data/spec/rollbar/plugins/resque/failure_spec.rb +36 -0
- data/spec/rollbar/request_data_extractor_spec.rb +103 -1
- data/spec/rollbar/truncation/min_body_strategy_spec.rb +1 -1
- data/spec/rollbar/truncation/strings_strategy_spec.rb +2 -2
- data/spec/rollbar_bc_spec.rb +4 -4
- data/spec/rollbar_spec.rb +99 -37
- data/spec/spec_helper.rb +2 -2
- data/spec/support/notifier_helpers.rb +2 -0
- metadata +16 -4
@@ -0,0 +1,645 @@
|
|
1
|
+
require 'rollbar'
|
2
|
+
require 'rollbar/lazy_store'
|
3
|
+
require 'rollbar/configuration'
|
4
|
+
require 'rollbar/util'
|
5
|
+
require 'rollbar/json'
|
6
|
+
require 'rollbar/exceptions'
|
7
|
+
require 'rollbar/language_support'
|
8
|
+
require 'rollbar/delay/girl_friday'
|
9
|
+
require 'rollbar/delay/thread'
|
10
|
+
require 'rollbar/logger_proxy'
|
11
|
+
require 'rollbar/item'
|
12
|
+
|
13
|
+
module Rollbar
|
14
|
+
class Notifier
|
15
|
+
attr_accessor :configuration
|
16
|
+
attr_accessor :last_report
|
17
|
+
attr_accessor :scope_object
|
18
|
+
|
19
|
+
@file_semaphore = Mutex.new
|
20
|
+
|
21
|
+
def initialize(parent_notifier = nil, payload_options = nil, scope = nil)
|
22
|
+
if parent_notifier
|
23
|
+
self.configuration = parent_notifier.configuration.clone
|
24
|
+
self.scope_object = parent_notifier.scope_object.clone
|
25
|
+
|
26
|
+
Rollbar::Util.deep_merge(scope_object, scope) if scope
|
27
|
+
else
|
28
|
+
self.configuration = ::Rollbar::Configuration.new
|
29
|
+
self.scope_object = ::Rollbar::LazyStore.new(scope)
|
30
|
+
end
|
31
|
+
|
32
|
+
Rollbar::Util.deep_merge(configuration.payload_options, payload_options) if payload_options
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset!
|
36
|
+
self.scope_object = ::Rollbar::LazyStore.new({})
|
37
|
+
end
|
38
|
+
|
39
|
+
# Similar to configure below, but used only internally within the gem
|
40
|
+
# to configure it without initializing any of the third party hooks
|
41
|
+
def preconfigure
|
42
|
+
yield(configuration)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Configures the notifier instance
|
46
|
+
def configure
|
47
|
+
configuration.enabled = true if configuration.enabled.nil?
|
48
|
+
|
49
|
+
yield(configuration)
|
50
|
+
end
|
51
|
+
|
52
|
+
def reconfigure
|
53
|
+
self.configuration = Configuration.new
|
54
|
+
configuration.enabled = true
|
55
|
+
|
56
|
+
yield(configuration)
|
57
|
+
end
|
58
|
+
|
59
|
+
def unconfigure
|
60
|
+
self.configuration = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def scope(scope_overrides = {}, config_overrides = {})
|
64
|
+
new_notifier = self.class.new(self, nil, scope_overrides)
|
65
|
+
new_notifier.configuration = configuration.merge(config_overrides)
|
66
|
+
|
67
|
+
new_notifier
|
68
|
+
end
|
69
|
+
|
70
|
+
def scope!(options = {}, config_overrides = {})
|
71
|
+
Rollbar::Util.deep_merge(scope_object, options)
|
72
|
+
configuration.merge!(config_overrides)
|
73
|
+
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns a new notifier with same configuration options
|
78
|
+
# but it sets Configuration#safely to true.
|
79
|
+
# We are using this flag to avoid having inifite loops
|
80
|
+
# when evaluating some custom user methods.
|
81
|
+
def safely
|
82
|
+
new_notifier = scope
|
83
|
+
new_notifier.configuration.safely = true
|
84
|
+
|
85
|
+
new_notifier
|
86
|
+
end
|
87
|
+
|
88
|
+
# Turns off reporting for the given block.
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# Rollbar.silenced { raise }
|
92
|
+
#
|
93
|
+
# @yield Block which exceptions won't be reported.
|
94
|
+
def silenced
|
95
|
+
yield
|
96
|
+
rescue => e
|
97
|
+
e.instance_variable_set(:@_rollbar_do_not_report, true)
|
98
|
+
raise
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sends a report to Rollbar.
|
102
|
+
#
|
103
|
+
# Accepts any number of arguments. The last String argument will become
|
104
|
+
# the message or description of the report. The last Exception argument
|
105
|
+
# will become the associated exception for the report. The last hash
|
106
|
+
# argument will be used as the extra data for the report.
|
107
|
+
#
|
108
|
+
# @example
|
109
|
+
# begin
|
110
|
+
# foo = bar
|
111
|
+
# rescue => e
|
112
|
+
# Rollbar.log(e)
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# Rollbar.log('This is a simple log message')
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
# Rollbar.log(e, 'This is a description of the exception')
|
120
|
+
#
|
121
|
+
def log(level, *args)
|
122
|
+
return 'disabled' unless configuration.enabled
|
123
|
+
|
124
|
+
message, exception, extra = extract_arguments(args)
|
125
|
+
use_exception_level_filters = extra && extra.delete(:use_exception_level_filters) == true
|
126
|
+
|
127
|
+
return 'ignored' if ignored?(exception, use_exception_level_filters)
|
128
|
+
|
129
|
+
begin
|
130
|
+
call_before_process(:level => level,
|
131
|
+
:exception => exception,
|
132
|
+
:message => message,
|
133
|
+
:extra => extra)
|
134
|
+
rescue Rollbar::Ignore
|
135
|
+
return 'ignored'
|
136
|
+
end
|
137
|
+
|
138
|
+
level = lookup_exception_level(level, exception,
|
139
|
+
use_exception_level_filters)
|
140
|
+
|
141
|
+
begin
|
142
|
+
report(level, message, exception, extra)
|
143
|
+
rescue Exception => e
|
144
|
+
report_internal_error(e)
|
145
|
+
|
146
|
+
'error'
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# See log() above
|
151
|
+
def debug(*args)
|
152
|
+
log('debug', *args)
|
153
|
+
end
|
154
|
+
|
155
|
+
# See log() above
|
156
|
+
def info(*args)
|
157
|
+
log('info', *args)
|
158
|
+
end
|
159
|
+
|
160
|
+
# See log() above
|
161
|
+
def warn(*args)
|
162
|
+
log('warning', *args)
|
163
|
+
end
|
164
|
+
|
165
|
+
# See log() above
|
166
|
+
def warning(*args)
|
167
|
+
log('warning', *args)
|
168
|
+
end
|
169
|
+
|
170
|
+
# See log() above
|
171
|
+
def error(*args)
|
172
|
+
log('error', *args)
|
173
|
+
end
|
174
|
+
|
175
|
+
# See log() above
|
176
|
+
def critical(*args)
|
177
|
+
log('critical', *args)
|
178
|
+
end
|
179
|
+
|
180
|
+
def process_item(item)
|
181
|
+
if configuration.write_to_file
|
182
|
+
if configuration.use_async
|
183
|
+
@file_semaphore.synchronize {
|
184
|
+
write_item(item)
|
185
|
+
}
|
186
|
+
else
|
187
|
+
write_item(item)
|
188
|
+
end
|
189
|
+
else
|
190
|
+
send_item(item)
|
191
|
+
end
|
192
|
+
rescue => e
|
193
|
+
log_error("[Rollbar] Error processing the item: #{e.class}, #{e.message}. Item: #{item.payload.inspect}")
|
194
|
+
raise e
|
195
|
+
end
|
196
|
+
|
197
|
+
# We will reraise exceptions in this method so async queues
|
198
|
+
# can retry the job or, in general, handle an error report some way.
|
199
|
+
#
|
200
|
+
# At same time that exception is silenced so we don't generate
|
201
|
+
# infinite reports. This example is what we want to avoid:
|
202
|
+
#
|
203
|
+
# 1. New exception in a the project is raised
|
204
|
+
# 2. That report enqueued to Sidekiq queue.
|
205
|
+
# 3. The Sidekiq job tries to send the report to our API
|
206
|
+
# 4. The report fails, for example cause a network failure,
|
207
|
+
# and a exception is raised
|
208
|
+
# 5. We report an internal error for that exception
|
209
|
+
# 6. We reraise the exception so Sidekiq job fails and
|
210
|
+
# Sidekiq can retry the job reporting the original exception
|
211
|
+
# 7. Because the job failed and Sidekiq can be managed by rollbar we'll
|
212
|
+
# report a new exception.
|
213
|
+
# 8. Go to point 2.
|
214
|
+
#
|
215
|
+
# We'll then push to Sidekiq queue indefinitely until the network failure
|
216
|
+
# is fixed.
|
217
|
+
#
|
218
|
+
# Using Rollbar.silenced we avoid the above behavior but Sidekiq
|
219
|
+
# will have a chance to retry the original job.
|
220
|
+
def process_from_async_handler(payload)
|
221
|
+
payload = Rollbar::JSON.load(payload) if payload.is_a?(String)
|
222
|
+
|
223
|
+
item = Item.build_with(payload,
|
224
|
+
:notifier => self,
|
225
|
+
:configuration => configuration,
|
226
|
+
:logger => logger)
|
227
|
+
|
228
|
+
Rollbar.silenced do
|
229
|
+
begin
|
230
|
+
process_item(item)
|
231
|
+
rescue => e
|
232
|
+
report_internal_error(e)
|
233
|
+
|
234
|
+
raise
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def send_failsafe(message, exception, uuid = nil, host = nil)
|
240
|
+
exception_reason = failsafe_reason(message, exception)
|
241
|
+
|
242
|
+
log_error "[Rollbar] Sending failsafe response due to #{exception_reason}"
|
243
|
+
|
244
|
+
body = failsafe_body(exception_reason)
|
245
|
+
|
246
|
+
failsafe_data = {
|
247
|
+
:level => 'error',
|
248
|
+
:environment => configuration.environment.to_s,
|
249
|
+
:body => {
|
250
|
+
:message => {
|
251
|
+
:body => body
|
252
|
+
}
|
253
|
+
},
|
254
|
+
:notifier => {
|
255
|
+
:name => 'rollbar-gem',
|
256
|
+
:version => VERSION
|
257
|
+
},
|
258
|
+
:custom => {
|
259
|
+
:orig_uuid => uuid,
|
260
|
+
:orig_host => host
|
261
|
+
},
|
262
|
+
:internal => true,
|
263
|
+
:failsafe => true
|
264
|
+
}
|
265
|
+
|
266
|
+
failsafe_payload = {
|
267
|
+
'access_token' => configuration.access_token,
|
268
|
+
'data' => failsafe_data
|
269
|
+
}
|
270
|
+
|
271
|
+
begin
|
272
|
+
item = Item.build_with(failsafe_payload,
|
273
|
+
:notifier => self,
|
274
|
+
:configuration => configuration,
|
275
|
+
:logger => logger)
|
276
|
+
schedule_item(item)
|
277
|
+
rescue => e
|
278
|
+
log_error "[Rollbar] Error sending failsafe : #{e}"
|
279
|
+
end
|
280
|
+
|
281
|
+
failsafe_payload
|
282
|
+
end
|
283
|
+
|
284
|
+
## Logging
|
285
|
+
%w(debug info warn error).each do |level|
|
286
|
+
define_method(:"log_#{level}") do |message|
|
287
|
+
logger.send(level, message)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
private
|
292
|
+
|
293
|
+
def call_before_process(options)
|
294
|
+
options = {
|
295
|
+
:level => options[:level],
|
296
|
+
:scope => scope_object,
|
297
|
+
:exception => options[:exception],
|
298
|
+
:message => options[:message],
|
299
|
+
:extra => options[:extra]
|
300
|
+
}
|
301
|
+
handlers = configuration.before_process
|
302
|
+
|
303
|
+
handlers.each do |handler|
|
304
|
+
begin
|
305
|
+
handler.call(options)
|
306
|
+
rescue Rollbar::Ignore
|
307
|
+
raise
|
308
|
+
rescue => e
|
309
|
+
log_error("[Rollbar] Error calling the `before_process` hook: #{e}")
|
310
|
+
|
311
|
+
break
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def extract_arguments(args)
|
317
|
+
message = nil
|
318
|
+
exception = nil
|
319
|
+
extra = nil
|
320
|
+
|
321
|
+
args.each do |arg|
|
322
|
+
if arg.is_a?(String)
|
323
|
+
message = arg
|
324
|
+
elsif arg.is_a?(Exception)
|
325
|
+
exception = arg
|
326
|
+
elsif arg.is_a?(Hash)
|
327
|
+
extra = arg
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
[message, exception, extra]
|
332
|
+
end
|
333
|
+
|
334
|
+
def lookup_exception_level(orig_level, exception, use_exception_level_filters)
|
335
|
+
return orig_level unless use_exception_level_filters
|
336
|
+
|
337
|
+
exception_level = filtered_level(exception)
|
338
|
+
return exception_level if exception_level
|
339
|
+
|
340
|
+
orig_level
|
341
|
+
end
|
342
|
+
|
343
|
+
def ignored?(exception, use_exception_level_filters = false)
|
344
|
+
return false unless exception
|
345
|
+
return true if use_exception_level_filters && filtered_level(exception) == 'ignore'
|
346
|
+
return true if exception.instance_variable_get(:@_rollbar_do_not_report)
|
347
|
+
|
348
|
+
false
|
349
|
+
end
|
350
|
+
|
351
|
+
def filtered_level(exception)
|
352
|
+
return unless exception
|
353
|
+
|
354
|
+
filter = configuration.exception_level_filters[exception.class.name]
|
355
|
+
if filter.respond_to?(:call)
|
356
|
+
filter.call(exception)
|
357
|
+
else
|
358
|
+
filter
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def report(level, message, exception, extra)
|
363
|
+
unless message || exception || extra
|
364
|
+
log_error '[Rollbar] Tried to send a report with no message, exception or extra data.'
|
365
|
+
|
366
|
+
return 'error'
|
367
|
+
end
|
368
|
+
|
369
|
+
item = build_item(level, message, exception, extra)
|
370
|
+
|
371
|
+
return 'ignored' if item.ignored?
|
372
|
+
|
373
|
+
schedule_item(item)
|
374
|
+
|
375
|
+
data = item['data']
|
376
|
+
log_instance_link(data)
|
377
|
+
Rollbar.last_report = data
|
378
|
+
|
379
|
+
data
|
380
|
+
end
|
381
|
+
|
382
|
+
# Reports an internal error in the Rollbar library. This will be reported within the configured
|
383
|
+
# Rollbar project. We'll first attempt to provide a report including the exception traceback.
|
384
|
+
# If that fails, we'll fall back to a more static failsafe response.
|
385
|
+
def report_internal_error(exception)
|
386
|
+
log_error "[Rollbar] Reporting internal error encountered while sending data to Rollbar."
|
387
|
+
|
388
|
+
begin
|
389
|
+
item = build_item('error', nil, exception, {:internal => true})
|
390
|
+
rescue => e
|
391
|
+
send_failsafe("build_item in exception_data", e)
|
392
|
+
return
|
393
|
+
end
|
394
|
+
|
395
|
+
begin
|
396
|
+
process_item(item)
|
397
|
+
rescue => e
|
398
|
+
send_failsafe("error in process_item", e)
|
399
|
+
return
|
400
|
+
end
|
401
|
+
|
402
|
+
begin
|
403
|
+
log_instance_link(item['data'])
|
404
|
+
rescue => e
|
405
|
+
send_failsafe("error logging instance link", e)
|
406
|
+
return
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
## Payload building functions
|
411
|
+
|
412
|
+
def build_item(level, message, exception, extra)
|
413
|
+
options = {
|
414
|
+
:level => level,
|
415
|
+
:message => message,
|
416
|
+
:exception => exception,
|
417
|
+
:extra => extra,
|
418
|
+
:configuration => configuration,
|
419
|
+
:logger => logger,
|
420
|
+
:scope => scope_object,
|
421
|
+
:notifier => self
|
422
|
+
}
|
423
|
+
|
424
|
+
item = Item.new(options)
|
425
|
+
item.build
|
426
|
+
|
427
|
+
item
|
428
|
+
end
|
429
|
+
|
430
|
+
## Delivery functions
|
431
|
+
|
432
|
+
def send_item_using_eventmachine(item)
|
433
|
+
body = item.dump
|
434
|
+
return unless body
|
435
|
+
|
436
|
+
headers = { 'X-Rollbar-Access-Token' => item['access_token'] }
|
437
|
+
req = EventMachine::HttpRequest.new(configuration.endpoint).post(:body => body, :head => headers)
|
438
|
+
|
439
|
+
req.callback do
|
440
|
+
if req.response_header.status == 200
|
441
|
+
log_info '[Rollbar] Success'
|
442
|
+
else
|
443
|
+
log_warning "[Rollbar] Got unexpected status code from Rollbar.io api: #{req.response_header.status}"
|
444
|
+
log_info "[Rollbar] Response: #{req.response}"
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
req.errback do
|
449
|
+
log_warning "[Rollbar] Call to API failed, status code: #{req.response_header.status}"
|
450
|
+
log_info "[Rollbar] Error's response: #{req.response}"
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
def send_item(item)
|
455
|
+
log_info '[Rollbar] Sending item'
|
456
|
+
|
457
|
+
if configuration.use_eventmachine
|
458
|
+
send_item_using_eventmachine(item)
|
459
|
+
return
|
460
|
+
end
|
461
|
+
|
462
|
+
body = item.dump
|
463
|
+
return unless body
|
464
|
+
|
465
|
+
uri = URI.parse(configuration.endpoint)
|
466
|
+
|
467
|
+
handle_response(do_post(uri, body, item['access_token']))
|
468
|
+
end
|
469
|
+
|
470
|
+
def do_post(uri, body, access_token)
|
471
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
472
|
+
http.open_timeout = configuration.open_timeout
|
473
|
+
http.read_timeout = configuration.request_timeout
|
474
|
+
|
475
|
+
if uri.scheme == 'https'
|
476
|
+
http.use_ssl = true
|
477
|
+
# This is needed to have 1.8.7 passing tests
|
478
|
+
http.ca_file = ENV['ROLLBAR_SSL_CERT_FILE'] if ENV.has_key?('ROLLBAR_SSL_CERT_FILE')
|
479
|
+
http.verify_mode = ssl_verify_mode
|
480
|
+
end
|
481
|
+
|
482
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
483
|
+
|
484
|
+
request.body = body
|
485
|
+
request.add_field('X-Rollbar-Access-Token', access_token)
|
486
|
+
|
487
|
+
handle_net_retries { http.request(request) }
|
488
|
+
end
|
489
|
+
|
490
|
+
def handle_net_retries
|
491
|
+
return yield if skip_retries?
|
492
|
+
|
493
|
+
retries = configuration.net_retries - 1
|
494
|
+
|
495
|
+
begin
|
496
|
+
yield
|
497
|
+
rescue *LanguageSupport.timeout_exceptions
|
498
|
+
raise if retries <= 0
|
499
|
+
|
500
|
+
retries -= 1
|
501
|
+
|
502
|
+
retry
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def skip_retries?
|
507
|
+
Rollbar::LanguageSupport.ruby_18? || Rollbar::LanguageSupport.ruby_19?
|
508
|
+
end
|
509
|
+
|
510
|
+
def handle_response(response)
|
511
|
+
if response.code == '200'
|
512
|
+
log_info '[Rollbar] Success'
|
513
|
+
else
|
514
|
+
log_warning "[Rollbar] Got unexpected status code from Rollbar api: #{response.code}"
|
515
|
+
log_info "[Rollbar] Response: #{response.body}"
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
def ssl_verify_mode
|
520
|
+
if configuration.verify_ssl_peer
|
521
|
+
OpenSSL::SSL::VERIFY_PEER
|
522
|
+
else
|
523
|
+
OpenSSL::SSL::VERIFY_NONE
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
def write_item(item)
|
528
|
+
if configuration.use_async
|
529
|
+
@file_semaphore.synchronize {
|
530
|
+
do_write_item(item)
|
531
|
+
}
|
532
|
+
else
|
533
|
+
do_write_item(item)
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def do_write_item(item)
|
538
|
+
log_info '[Rollbar] Writing item to file'
|
539
|
+
|
540
|
+
body = item.dump
|
541
|
+
return unless body
|
542
|
+
|
543
|
+
begin
|
544
|
+
unless @file
|
545
|
+
@file = File.open(configuration.filepath, "a")
|
546
|
+
end
|
547
|
+
|
548
|
+
@file.puts(body)
|
549
|
+
@file.flush
|
550
|
+
log_info "[Rollbar] Success"
|
551
|
+
rescue IOError => e
|
552
|
+
log_error "[Rollbar] Error opening/writing to file: #{e}"
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
def failsafe_reason(message, exception)
|
557
|
+
body = ''
|
558
|
+
|
559
|
+
if exception
|
560
|
+
begin
|
561
|
+
backtrace = exception.backtrace || []
|
562
|
+
nearest_frame = backtrace[0]
|
563
|
+
|
564
|
+
exception_info = exception.class.name
|
565
|
+
# #to_s and #message defaults to class.to_s. Add message only if add valuable info.
|
566
|
+
exception_info += %Q{: "#{exception.message}"} if exception.message != exception.class.to_s
|
567
|
+
exception_info += " in #{nearest_frame}" if nearest_frame
|
568
|
+
|
569
|
+
body += "#{exception_info}: #{message}"
|
570
|
+
rescue
|
571
|
+
end
|
572
|
+
else
|
573
|
+
begin
|
574
|
+
body += message.to_s
|
575
|
+
rescue
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
body
|
580
|
+
end
|
581
|
+
|
582
|
+
def failsafe_body(reason)
|
583
|
+
"Failsafe from rollbar-gem. #{reason}"
|
584
|
+
end
|
585
|
+
|
586
|
+
def schedule_item(item)
|
587
|
+
return unless item
|
588
|
+
|
589
|
+
log_info '[Rollbar] Scheduling item'
|
590
|
+
|
591
|
+
if configuration.use_async
|
592
|
+
process_async_item(item)
|
593
|
+
else
|
594
|
+
process_item(item)
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
def default_async_handler
|
599
|
+
return Rollbar::Delay::GirlFriday if defined?(GirlFriday)
|
600
|
+
|
601
|
+
Rollbar::Delay::Thread
|
602
|
+
end
|
603
|
+
|
604
|
+
def process_async_item(item)
|
605
|
+
configuration.async_handler ||= default_async_handler
|
606
|
+
configuration.async_handler.call(item.payload)
|
607
|
+
rescue => e
|
608
|
+
if configuration.failover_handlers.empty?
|
609
|
+
log_error '[Rollbar] Async handler failed, and there are no failover handlers configured. See the docs for "failover_handlers"'
|
610
|
+
return
|
611
|
+
end
|
612
|
+
|
613
|
+
async_failover(item)
|
614
|
+
end
|
615
|
+
|
616
|
+
def async_failover(item)
|
617
|
+
log_warning '[Rollbar] Primary async handler failed. Trying failovers...'
|
618
|
+
|
619
|
+
failover_handlers = configuration.failover_handlers
|
620
|
+
|
621
|
+
failover_handlers.each do |handler|
|
622
|
+
begin
|
623
|
+
handler.call(item.payload)
|
624
|
+
rescue
|
625
|
+
next unless handler == failover_handlers.last
|
626
|
+
|
627
|
+
log_error "[Rollbar] All failover handlers failed while processing item: #{Rollbar::JSON.dump(item.payload)}"
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
alias_method :log_warning, :log_warn
|
633
|
+
|
634
|
+
def log_instance_link(data)
|
635
|
+
return unless data[:uuid]
|
636
|
+
|
637
|
+
uuid_url = Util.uuid_rollbar_url(data, configuration)
|
638
|
+
log_info "[Rollbar] Details: #{uuid_url} (only available if report was successful)"
|
639
|
+
end
|
640
|
+
|
641
|
+
def logger
|
642
|
+
@logger ||= LoggerProxy.new(configuration.logger)
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|