rollbar 2.12.0 → 2.13.0
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 +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
|