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.



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

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.



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

def configuration
  @configuration
end

#last_reportObject

Returns the value of attribute last_report.



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

def last_report
  @last_report
end

#scope_objectObject

Returns the value of attribute scope_object.



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

def scope_object
  @scope_object
end

Instance Method Details

#add_configured_options(payload_notifier, original_error) ⇒ Object



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

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



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

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



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

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



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

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



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

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

#build_item_with_payload(payload) ⇒ Object



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

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:



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

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

  yield(configuration.configured_options)
end

#critical(*args) ⇒ Object

See log() above



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

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

#current_bindingsObject



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

def current_bindings
  trace_with_bindings.frames
end

#debug(*args) ⇒ Object

See log() above



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

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

#disable_localsObject



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

def disable_locals
  trace_with_bindings.disable if enable_locals?
end

#enable_localsObject



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

def enable_locals
  trace_with_bindings.enable if enable_locals?
end

#enable_locals?Boolean

Returns:

  • (Boolean)


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

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

#enabled?Boolean

Returns:

  • (Boolean)


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

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



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

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

#exception_bindingsObject



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

def exception_bindings
  trace_with_bindings.exception_frames
end

#failsafe_add_original_error_data(payload_notifier, original_error) ⇒ Object



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

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



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

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)


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

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



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

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

#levelObject

Logging



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

%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')


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

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



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

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:



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

def preconfigure
  yield(configuration.configured_options)
end

#process_failsafe_item(failsafe_payload) ⇒ Object



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

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.



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

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



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

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:



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

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

  yield(configuration.configured_options)
end

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



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

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



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

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.



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

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

  new_notifier
end

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



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

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



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

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



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

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.



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

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

#trace_with_bindingsObject



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

def trace_with_bindings
  @trace_with_bindings ||= TraceWithBindings.new
end

#unconfigureObject



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

def unconfigure
  self.configuration = nil
end

#warn(*args) ⇒ Object

See log() above



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

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

#warning(*args) ⇒ Object

See log() above



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

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