Class: Rollbar::Notifier

Inherits:
Object
  • Object
show all
Defined in:
lib/rollbar/notifier.rb,
lib/rollbar/notifier/trace_with_bindings.rb

Overview

The notifier class. It has the core functionality for sending reports to the API.

Defined Under Namespace

Classes: TraceWithBindings

Constant Summary collapse

MUTEX =
Mutex.new
EXTENSION_REGEXP =
/.rollbar\z/.freeze
FAILSAFE_STRING_LENGTH =
10_000

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parent_notifier = nil, payload_options = nil, scope = nil) ⇒ Notifier

Returns a new instance of Notifier.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rollbar/notifier.rb', line 23

def initialize(parent_notifier = nil, payload_options = nil, scope = nil)
  if parent_notifier
    self.configuration = parent_notifier.configuration.clone
    self.scope_object = parent_notifier.scope_object.clone

    Rollbar::Util.deep_merge(scope_object, scope) if scope
  else
    self.configuration = ::Rollbar::Configuration.new
    self.scope_object = ::Rollbar::LazyStore.new(scope)
  end

  return unless payload_options

  Rollbar::Util.deep_merge(configuration.payload_options, payload_options)
end

Instance Attribute Details

#configurationObject

Returns the value of attribute configuration.



17
18
19
# File 'lib/rollbar/notifier.rb', line 17

def configuration
  @configuration
end

#last_reportObject

Returns the value of attribute last_report.



17
18
19
# File 'lib/rollbar/notifier.rb', line 17

def last_report
  @last_report
end

#scope_objectObject

Returns the value of attribute scope_object.



17
18
19
# File 'lib/rollbar/notifier.rb', line 17

def scope_object
  @scope_object
end

Instance Method Details

#add_configured_options(payload_notifier, original_error) ⇒ Object



351
352
353
354
355
356
357
358
359
# File 'lib/rollbar/notifier.rb', line 351

def add_configured_options(payload_notifier, original_error)
  if original_error[:configuration]
    configured = original_error[:configuration].configured_options.configured
    payload_notifier[:configured_options] =
      ::JSON.generate(configured).truncate(FAILSAFE_STRING_LENGTH)
  end
rescue StandardError => e
  payload_notifier[:configured_options] = "Failed: #{e.message}"
end

#add_original_error(diagnostic, original_error) ⇒ Object



338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/rollbar/notifier.rb', line 338

def add_original_error(diagnostic, original_error)
  if original_error[:exception]
    backtrace = original_error[:exception].backtrace
    message = original_error[:exception].message
    diagnostic[:original_error] = {
      :message => message && message.truncate(FAILSAFE_STRING_LENGTH),
      :stack => backtrace && backtrace.join(', ').truncate(FAILSAFE_STRING_LENGTH)
    }
  end
rescue StandardError => e
  diagnostic[:original_error] = "Failed: #{e.message}"
end

#add_original_host(diagnostic, original_error) ⇒ Object



361
362
363
# File 'lib/rollbar/notifier.rb', line 361

def add_original_host(diagnostic, original_error)
  diagnostic[:original_host] = original_error[:host] if original_error[:host]
end

#add_original_message(diagnostic, original_error) ⇒ Object



329
330
331
332
333
334
335
336
# File 'lib/rollbar/notifier.rb', line 329

def add_original_message(diagnostic, original_error)
  if original_error[:message]
    diagnostic[:original_message] =
      original_error[:message].truncate(FAILSAFE_STRING_LENGTH)
  end
rescue StandardError => e
  diagnostic[:original_message] = "Failed: #{e.message}"
end

#add_original_uuid(diagnostic, original_error) ⇒ Object



365
366
367
# File 'lib/rollbar/notifier.rb', line 365

def add_original_uuid(diagnostic, original_error)
  diagnostic[:original_uuid] = original_error[:uuid] if original_error[:uuid]
end

#build_item_with_payload(payload) ⇒ Object



267
268
269
270
271
# File 'lib/rollbar/notifier.rb', line 267

def build_item_with_payload(payload)
  Item.build_with(payload, :notifier => self,
                           :configuration => configuration,
                           :logger => logger)
end

#configure {|configuration.configured_options| ... } ⇒ Object

Configures the notifier instance

Yields:



50
51
52
53
54
# File 'lib/rollbar/notifier.rb', line 50

def configure
  configuration.enabled = true if configuration.enabled.nil?

  yield(configuration.configured_options)
end

#critical(*args) ⇒ Object

See log() above



200
201
202
# File 'lib/rollbar/notifier.rb', line 200

def critical(*args)
  log('critical', *args)
end

#current_bindingsObject



388
389
390
# File 'lib/rollbar/notifier.rb', line 388

def current_bindings
  trace_with_bindings.frames
end

#debug(*args) ⇒ Object

See log() above



175
176
177
# File 'lib/rollbar/notifier.rb', line 175

def debug(*args)
  log('debug', *args)
end

#disable_localsObject



401
402
403
# File 'lib/rollbar/notifier.rb', line 401

def disable_locals
  trace_with_bindings.disable if enable_locals?
end

#enable_localsObject



397
398
399
# File 'lib/rollbar/notifier.rb', line 397

def enable_locals
  trace_with_bindings.enable if enable_locals?
end

#enable_locals?Boolean

Returns:

  • (Boolean)


392
393
394
395
# File 'lib/rollbar/notifier.rb', line 392

def enable_locals?
  configuration.locals[:enabled] &&
    [:app, :all].include?(configuration.send_extra_frame_data)
end

#enabled?Boolean

Returns:

  • (Boolean)


204
205
206
207
208
209
# File 'lib/rollbar/notifier.rb', line 204

def enabled?
  # Require access_token so we don't try to send events when unconfigured.
  configuration.enabled &&
    configuration.access_token &&
    !configuration.access_token.empty?
end

#error(*args) ⇒ Object

See log() above



195
196
197
# File 'lib/rollbar/notifier.rb', line 195

def error(*args)
  log('error', *args)
end

#exception_bindingsObject



384
385
386
# File 'lib/rollbar/notifier.rb', line 384

def exception_bindings
  trace_with_bindings.exception_frames
end

#failsafe_add_original_error_data(payload_notifier, original_error) ⇒ Object



317
318
319
320
321
322
323
324
325
326
327
# File 'lib/rollbar/notifier.rb', line 317

def failsafe_add_original_error_data(payload_notifier, original_error)
  return unless original_error

  payload_notifier[:diagnostic] ||= {}

  add_original_host(payload_notifier[:diagnostic], original_error)
  add_original_uuid(payload_notifier[:diagnostic], original_error)
  add_original_message(payload_notifier[:diagnostic], original_error)
  add_original_error(payload_notifier[:diagnostic], original_error)
  add_configured_options(payload_notifier, original_error)
end

#failsafe_initial_data(exception_reason) ⇒ Object



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/rollbar/notifier.rb', line 273

def failsafe_initial_data(exception_reason)
  {
    :level => 'error',
    :environment => configuration.environment.to_s,
    :body => {
      :message => {
        :body => failsafe_body(exception_reason)
      }
    },
    :notifier => {
      :name => 'rollbar-gem',
      :version => VERSION
    },
    :internal => true,
    'failsafe' => true
  }
end

#ignore_before_process?(level, exception, message, extra) ⇒ Boolean

Returns:

  • (Boolean)


149
150
151
152
153
154
155
156
157
158
# File 'lib/rollbar/notifier.rb', line 149

def ignore_before_process?(level, exception, message, extra)
  status = call_before_process(:level => level,
                               :exception => exception,
                               :message => message,
                               :extra => extra)

  status == 'ignored'
rescue Rollbar::Ignore
  true
end

#info(*args) ⇒ Object

See log() above



180
181
182
# File 'lib/rollbar/notifier.rb', line 180

def info(*args)
  log('info', *args)
end

#levelObject

Logging



370
371
372
373
374
# File 'lib/rollbar/notifier.rb', line 370

%w[debug info warn error].each do |level|
  define_method(:"log_#{level}") do |message|
    logger.send(level, message)
  end
end

#log(level, *args) ⇒ Object

Sends a report to Rollbar.

Accepts a level string plus any number of arguments. The last String argument will become the message or description of the report. The last Exception argument will become the associated exception for the report. The last hash argument will be used as the extra data for the report.

If the extra hash contains a symbol key :custom_data_method_context the value of the key will be used as the context for configuration.custom_data_method and will be removed from the extra hash.

Examples:

begin
  foo = bar
rescue => e
  Rollbar.log('error', e)
end
Rollbar.log('info', 'This is a simple log message')
Rollbar.log('error', e, 'This is a description of the exception')


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/rollbar/notifier.rb', line 130

def log(level, *args)
  return 'disabled' unless enabled?

  message, exception, extra, context = extract_arguments(args)
  use_exception_level_filters = use_exception_level_filters?(extra)

  return 'ignored' if ignored?(exception, use_exception_level_filters) ||
                      ignore_before_process?(level, exception, message, extra)

  level = lookup_exception_level(level, exception,
                                 use_exception_level_filters)

  ret = report_with_rescue(level, message, exception, extra, context)

  raise(exception) if configuration.raise_on_error && exception

  ret
end

#loggerObject



376
377
378
# File 'lib/rollbar/notifier.rb', line 376

def logger
  @logger ||= LoggerProxy.new(configuration.logger)
end

#preconfigure {|configuration.configured_options| ... } ⇒ Object

Similar to configure below, but used only internally within the gem to configure it without initializing any of the third party hooks

Yields:



45
46
47
# File 'lib/rollbar/notifier.rb', line 45

def preconfigure
  yield(configuration.configured_options)
end

#process_failsafe_item(failsafe_payload) ⇒ Object



309
310
311
312
313
314
315
# File 'lib/rollbar/notifier.rb', line 309

def process_failsafe_item(failsafe_payload)
  item = build_item_with_payload(failsafe_payload)
  process_item(item)
  log_and_return_item_data(item)
rescue StandardError => e
  log_error "[Rollbar] Error sending failsafe : #{e}"
end

#process_from_async_handler(payload) ⇒ Object

We will reraise exceptions in this method so async queues can retry the job or, in general, handle an error report some way.

At same time that exception is silenced so we don’t generate infinite reports. This example is what we want to avoid:

  1. New exception in a the project is raised

  2. That report enqueued to Sidekiq queue.

  3. The Sidekiq job tries to send the report to our API

  4. The report fails, for example cause a network failure, and a exception is raised

  5. We report an internal error for that exception

  6. We reraise the exception so Sidekiq job fails and Sidekiq can retry the job reporting the original exception

  7. Because the job failed and Sidekiq can be managed by rollbar we’ll report a new exception.

  8. Go to point 2.

We’ll then push to Sidekiq queue indefinitely until the network failure is fixed.

Using Rollbar.silenced we avoid the above behavior but Sidekiq will have a chance to retry the original job.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/rollbar/notifier.rb', line 248

def process_from_async_handler(payload)
  Rollbar.silenced do
    begin
      if payload.is_a?(String)
        # The final payload has already been built.
        send_body(payload)
      else
        item = build_item_with_payload(payload)

        process_item(item)
      end
    rescue StandardError => e
      report_internal_error(e)

      raise
    end
  end
end

#process_item(item) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/rollbar/notifier.rb', line 211

def process_item(item)
  return send_item(item) unless configuration.write_to_file

  return do_write_item(item) unless configuration.use_async

  MUTEX.synchronize { do_write_item(item) }
rescue StandardError => e
  log_error '[Rollbar] Error processing the item: ' \
    "#{e.class}, #{e.message}. Item: #{item.payload.inspect}"
  raise e unless via_failsafe?(item)

  log_error('[Rollbar] Item has already failed. Not re-raising')
end

#reconfigure {|configuration.configured_options| ... } ⇒ Object

Yields:



56
57
58
59
60
61
# File 'lib/rollbar/notifier.rb', line 56

def reconfigure
  self.configuration = Configuration.new
  configuration.enabled = true

  yield(configuration.configured_options)
end

#report_with_rescue(level, message, exception, extra, context) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/rollbar/notifier.rb', line 160

def report_with_rescue(level, message, exception, extra, context)
  report(level, message, exception, extra, context)
rescue StandardError, SystemStackError => e
  original_error = {
    :message => message,
    :exception => exception,
    :configuration => configuration
  }

  report_internal_error(e, original_error)

  'error'
end

#reset!Object



39
40
41
# File 'lib/rollbar/notifier.rb', line 39

def reset!
  self.scope_object = ::Rollbar::LazyStore.new({})
end

#safelyObject

Returns a new notifier with same configuration options but it sets Configuration#safely to true. We are using this flag to avoid having inifite loops when evaluating some custom user methods.



85
86
87
88
89
90
# File 'lib/rollbar/notifier.rb', line 85

def safely
  new_notifier = scope
  new_notifier.configuration.safely = true

  new_notifier
end

#scope(scope_overrides = {}, config_overrides = {}) ⇒ Object



67
68
69
70
71
72
# File 'lib/rollbar/notifier.rb', line 67

def scope(scope_overrides = {}, config_overrides = {})
  new_notifier = self.class.new(self, nil, scope_overrides)
  new_notifier.configuration = configuration.merge(config_overrides)

  new_notifier
end

#scope!(options = {}, config_overrides = {}) ⇒ Object



74
75
76
77
78
79
# File 'lib/rollbar/notifier.rb', line 74

def scope!(options = {}, config_overrides = {})
  Rollbar::Util.deep_merge(scope_object, options)
  configuration.merge!(config_overrides)

  self
end

#send_failsafe(message, exception, original_error = nil) ⇒ Object



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/rollbar/notifier.rb', line 291

def send_failsafe(message, exception, original_error = nil)
  exception_reason = failsafe_reason(message, exception)

  log_error "[Rollbar] Sending failsafe response due to #{exception_reason}"

  failsafe_data = failsafe_initial_data(exception_reason)

  failsafe_add_original_error_data(failsafe_data[:notifier], original_error)

  failsafe_payload = {
    'data' => failsafe_data
  }

  process_failsafe_item(failsafe_payload)

  failsafe_payload
end

#silenced { ... } ⇒ Object

Turns off reporting for the given block.

Examples:

Rollbar.silenced { raise }

Yields:

  • Block which exceptions won’t be reported.



98
99
100
101
102
103
# File 'lib/rollbar/notifier.rb', line 98

def silenced
  yield
rescue StandardError => e
  e.instance_variable_set(:@_rollbar_do_not_report, true)
  raise
end

#trace_with_bindingsObject



380
381
382
# File 'lib/rollbar/notifier.rb', line 380

def trace_with_bindings
  @trace_with_bindings ||= TraceWithBindings.new
end

#unconfigureObject



63
64
65
# File 'lib/rollbar/notifier.rb', line 63

def unconfigure
  self.configuration = nil
end

#warn(*args) ⇒ Object

See log() above



185
186
187
# File 'lib/rollbar/notifier.rb', line 185

def warn(*args)
  log('warning', *args)
end

#warning(*args) ⇒ Object

See log() above



190
191
192
# File 'lib/rollbar/notifier.rb', line 190

def warning(*args)
  log('warning', *args)
end