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



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

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



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

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



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

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



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

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



368
369
370
# File 'lib/rollbar/notifier.rb', line 368

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

#build_item_with_payload(payload) ⇒ Object



270
271
272
273
274
# File 'lib/rollbar/notifier.rb', line 270

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



203
204
205
# File 'lib/rollbar/notifier.rb', line 203

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

#current_bindingsObject



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

def current_bindings
  trace_with_bindings.frames
end

#debug(*args) ⇒ Object

See log() above



178
179
180
# File 'lib/rollbar/notifier.rb', line 178

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

#disable_localsObject



404
405
406
# File 'lib/rollbar/notifier.rb', line 404

def disable_locals
  trace_with_bindings.disable if enable_locals?
end

#enable_localsObject



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

def enable_locals
  trace_with_bindings.enable if enable_locals?
end

#enable_locals?Boolean

Returns:

  • (Boolean)


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

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

#enabled?Boolean

Returns:

  • (Boolean)


207
208
209
210
211
212
# File 'lib/rollbar/notifier.rb', line 207

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



198
199
200
# File 'lib/rollbar/notifier.rb', line 198

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

#exception_bindingsObject



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

def exception_bindings
  trace_with_bindings.exception_frames
end

#failsafe_add_original_error_data(payload_notifier, original_error) ⇒ Object



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

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



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

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)


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

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



183
184
185
# File 'lib/rollbar/notifier.rb', line 183

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

#levelObject

Logging



373
374
375
376
377
# File 'lib/rollbar/notifier.rb', line 373

%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
148
149
150
# 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)
  is_uncaught = uncaught?(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, is_uncaught
  )

  raise(exception) if configuration.raise_on_error && exception

  ret
end

#loggerObject



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

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



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

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.



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

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



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

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, is_uncaught) ⇒ Object



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

def report_with_rescue(level, message, exception, extra, context, is_uncaught)
  report(level, message, exception, extra, context, is_uncaught)
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



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

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



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

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



188
189
190
# File 'lib/rollbar/notifier.rb', line 188

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

#warning(*args) ⇒ Object

See log() above



193
194
195
# File 'lib/rollbar/notifier.rb', line 193

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