Class: Honeybadger::Agent

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Logging::Helper
Defined in:
lib/honeybadger/agent.rb

Overview

The Honeybadger agent contains all the methods for interacting with the Honeybadger service. It can be used to send notifications to multiple projects in large apps. The global agent instance (Agent.instance) should always be accessed through the Honeybadger singleton.

Context

Context is global by default, meaning agents created via Honeybadger::Agent.new will share context (added via Honeybadger.context or #context) with other agents. This also includes the Rack environment when using the Rack::ErrorNotifier middleware. To localize context for a custom agent, use the local_context: true option when initializing.

Examples:


# Standard usage:
OtherBadger = Honeybadger::Agent.new

# With local context:
OtherBadger = Honeybadger::Agent.new(local_context: true)

OtherBadger.configure do |config|
  config.api_key = 'project api key'
end

begin
  # Risky operation
rescue => e
  OtherBadger.notify(e)
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Agent

Returns a new instance of Agent.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/honeybadger/agent.rb', line 64

def initialize(opts = {})
  if opts.kind_of?(Config)
    @config = opts
    opts = {}
  end

  @context = opts.delete(:context)
  local_context = opts.delete(:local_context)

  @config ||= Config.new(opts)

  if local_context
    @context ||= ContextManager.new
    @breadcrumbs = Breadcrumbs::Collector.new(config)
  else
    @breadcrumbs = nil
  end

  init_worker
end

Instance Attribute Details

#configObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



452
453
454
# File 'lib/honeybadger/agent.rb', line 452

def config
  @config
end

#events_workerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



525
526
527
# File 'lib/honeybadger/agent.rb', line 525

def events_worker
  @events_worker
end

#metrics_workerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



525
526
527
# File 'lib/honeybadger/agent.rb', line 525

def metrics_worker
  @metrics_worker
end

#workerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



525
526
527
# File 'lib/honeybadger/agent.rb', line 525

def worker
  @worker
end

Class Method Details

.instanceObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



55
56
57
# File 'lib/honeybadger/agent.rb', line 55

def self.instance
  @instance
end

.instance=(instance) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



60
61
62
# File 'lib/honeybadger/agent.rb', line 60

def self.instance=(instance)
  @instance = instance
end

Instance Method Details

#add_breadcrumb(message, metadata: {}, category: "custom") ⇒ Object

Appends a breadcrumb to the trace. Use this when you want to add some custom data to your breadcrumb trace in effort to help debugging. If a notice is reported to Honeybadger, all breadcrumbs within the execution path will be appended to the notice. You will be able to view the breadcrumb trace in the Honeybadger interface to see what events led up to the notice.

Examples:

Honeybadger.add_breadcrumb("Email Sent", metadata: { user: user.id, message: message })

Parameters:

  • message (String)

    The message you want to send with the breadcrumb

  • params (Hash)

    extra options for breadcrumb building

Returns:

  • self



319
320
321
322
323
324
325
326
327
328
329
# File 'lib/honeybadger/agent.rb', line 319

def add_breadcrumb(message, metadata: {}, category: "custom")
  params = Util::Sanitizer.new(max_depth: 2).sanitize({
    category: category,
    message: message,
    metadata: 
  })

  breadcrumbs.add!(Breadcrumbs::Breadcrumb.new(**params))

  self
end

#backendObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

See Also:



535
# File 'lib/honeybadger/agent.rb', line 535

def_delegators :config, :backend

#backtrace_filter {|line| ... } ⇒ Object

DEPRECATED: Callback to filter backtrace lines. One use for this is to make additional [PROJECT_ROOT] or [GEM_ROOT] substitutions, which are used by Honeybadger when grouping errors and displaying application traces.

Examples:

Honeybadger.backtrace_filter do |line|
  line.gsub(/^\/my\/unknown\/bundle\/path/, "[GEM_ROOT]")
end

Yield Parameters:

  • line (String)

    The backtrace line to modify.

Yield Returns:

  • (String)

    The new (modified) backtrace line.



512
# File 'lib/honeybadger/agent.rb', line 512

def_delegator :config, :backtrace_filter

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Direct access to the Breadcrumbs::Collector instance



293
294
295
296
297
# File 'lib/honeybadger/agent.rb', line 293

def breadcrumbs
  return @breadcrumbs if @breadcrumbs

  Thread.current[:__hb_breadcrumbs] ||= Breadcrumbs::Collector.new(config)
end

#check_in(id) ⇒ Boolean

Perform a synchronous check_in.

Examples:

Honeybadger.check_in('1MqIo1')

Parameters:

  • id (String)

    The unique check in id (e.g. ‘1MqIo1’) or the check in url.

Returns:

  • (Boolean)

    true if the check in was successful and false otherwise.



200
201
202
203
204
205
# File 'lib/honeybadger/agent.rb', line 200

def check_in(id)
  # this is to allow check ins even if a url is passed
  check_in_id = id.to_s.strip.gsub(/\/$/, '').split('/').last
  response = backend.check_in(check_in_id)
  response.success?
end

#clear!Object

Clear all transaction scoped data.



275
276
277
278
# File 'lib/honeybadger/agent.rb', line 275

def clear!
  context_manager.clear!
  breadcrumbs.clear!
end

#collect(collector) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



436
437
438
439
440
441
# File 'lib/honeybadger/agent.rb', line 436

def collect(collector)
  return unless config.insights_enabled?

  init_metrics_worker
  metrics_worker.push(collector)
end

#configure {|Config::Ruby| ... } ⇒ Object

Configure the Honeybadger agent via Ruby.

Examples:

Honeybadger.configure do |config|
  config.api_key = 'project api key'
  config.exceptions.ignore += [CustomError]
end

Yields:



464
# File 'lib/honeybadger/agent.rb', line 464

def_delegator :config, :configure

#context(context = nil, &block) ⇒ Object, self

Save global context for the current request.

Examples:

Honeybadger.context({my_data: 'my value'})

# Inside a Rails controller:
before_action do
  Honeybadger.context({user_id: current_user.id})
end

# Explicit conversion
class User < ActiveRecord::Base
  def to_honeybadger_context
    { user_id: id, user_email: email }
  end
end

user = User.first
Honeybadger.context(user)

# Clearing global context:
Honeybadger.context.clear!

Parameters:

  • context (Hash) (defaults to: nil)

    A Hash of data which will be sent to Honeybadger when an error occurs. If the object responds to #to_honeybadger_context, the return value of that method will be used (explicit conversion). Can include any key/value, but a few keys have a special meaning in Honeybadger.

Options Hash (context):

  • :user_id (String)

    The user ID used by Honeybadger to aggregate user data across occurrences on the error page (optional).

  • :user_email (String)

    The user email address (optional).

  • :tags (String)

    The comma-separated list of tags. When present, tags will be applied to errors with this context (optional).

Returns:

  • (Object, self)

    value of the block if passed, otherwise self



267
268
269
270
271
272
# File 'lib/honeybadger/agent.rb', line 267

def context(context = nil, &block)
  block_result = context_manager.set_context(context, &block) unless context.nil?
  return block_result if block_given?

  self
end

#decrement_counterObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



560
# File 'lib/honeybadger/agent.rb', line 560

def_delegator :instrumentation, :decrement_counter

#event(event_type, payload = {}) ⇒ void

This method returns an undefined value.

Sends event to events backend

Examples:

# With event type as first argument (recommended):
Honeybadger.event("user_signed_up", user_id: 123)

# With just a payload:
Honeybadger.event(event_type: "user_signed_up", user_id: 123)

Parameters:

  • event_name (String, Hash)

    a String describing the event or a Hash when the second argument is omitted.

  • payload (Hash) (defaults to: {})

    Additional data to be sent with the event as keyword arguments



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/honeybadger/agent.rb', line 404

def event(event_type, payload = {})
  init_events_worker

  extra_payload = {}.tap do |p|
    p[:request_id] = context_manager.get_request_id if context_manager.get_request_id
    p[:hostname] = config[:hostname].to_s if config[:'events.attach_hostname']
  end

  event = Event.new(event_type, extra_payload.merge(payload))

  config.before_event_hooks.each do |hook|
    with_error_handling { hook.call(event) }
  end

  return if config.ignored_events.any? do |check|
    with_error_handling do
      check.all? do |keys, value|
        if keys == [:event_type]
          event.event_type&.match?(value)
        elsif event.dig(*keys)
          event.dig(*keys).to_s.match?(value)
        end
      end
    end
  end

  return if event.halted?

  events_worker.push(event.as_json)
end

#exception_filterObject

DEPRECATED: Callback to ignore exceptions.

See public API documentation for Notice for available attributes.

Examples:

# Ignoring based on error message:
Honeybadger.exception_filter do |notice|
  notice.error_message =~ /sensitive data/
end

# Ignore an entire class of exceptions:
Honeybadger.exception_filter do |notice|
  notice.exception.class < MyError
end

Yield Returns:

  • (Boolean)

    true (to ignore) or false (to send).



483
# File 'lib/honeybadger/agent.rb', line 483

def_delegator :config, :exception_filter

#exception_fingerprintObject

DEPRECATED: Callback to add a custom grouping strategy for exceptions. The return value is hashed and sent to Honeybadger. Errors with the same fingerprint will be grouped.

See public API documentation for Notice for available attributes.

Examples:

Honeybadger.exception_fingerprint do |notice|
  [notice.error_class, notice.component, notice.backtrace.to_s].join(':')
end

Yield Returns:

  • (#to_s)

    The fingerprint of the error.



498
# File 'lib/honeybadger/agent.rb', line 498

def_delegator :config, :exception_fingerprint

#flush { ... } ⇒ Object, Boolean

Flushes all data from workers before returning. This is most useful in tests when using the test backend, where normally the asynchronous nature of this library could create race conditions.

Examples:

# Without a block:
it "sends a notification to Honeybadger" do
  expect {
    Honeybadger.notify(StandardError.new('test backend'))
    Honeybadger.flush
  }.to change(Honeybadger::Backend::Test.notifications[:notices], :size).by(0)
end

# With a block:
it "sends a notification to Honeybadger" do
  expect {
    Honeybadger.flush do
      49.times do
        Honeybadger.notify(StandardError.new('test backend'))
      end
    end
  }.to change(Honeybadger::Backend::Test.notifications[:notices], :size).by(49)
end

Yields:

  • An optional block to execute (exceptions will propagate after data is flushed).

Returns:

  • (Object, Boolean)

    value of block if block is given, otherwise true on success or false if Honeybadger isn’t running.



360
361
362
363
364
365
366
367
# File 'lib/honeybadger/agent.rb', line 360

def flush
  return true unless block_given?
  yield
ensure
  worker.flush
  events_worker&.flush
  metrics_worker&.flush
end

#gaugeObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



550
# File 'lib/honeybadger/agent.rb', line 550

def_delegator :instrumentation, :gauge

#get_contextHash?

Get global context for the current request.

Examples:

Honeybadger.context({my_data: 'my value'})
Honeybadger.get_context # => {my_data: 'my value'}

Returns:

  • (Hash, nil)


287
288
289
# File 'lib/honeybadger/agent.rb', line 287

def get_context
  context_manager.get_context
end

#histogramObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



545
# File 'lib/honeybadger/agent.rb', line 545

def_delegator :instrumentation, :histogram

#increment_counterObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



555
# File 'lib/honeybadger/agent.rb', line 555

def_delegator :instrumentation, :increment_counter

#init!(...) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

See Also:



530
# File 'lib/honeybadger/agent.rb', line 530

def_delegators :config, :init!

#instrumentationObject



562
563
564
# File 'lib/honeybadger/agent.rb', line 562

def instrumentation
  @instrumentation ||= Honeybadger::Instrumentation.new(self)
end

#notify(exception_or_opts = nil, opts = {}, **kwargs) ⇒ String, false

Sends an exception to Honeybadger. Does not report ignored exceptions by default.

Examples:

# With an exception:
begin
  fail 'oops'
rescue => exception
  Honeybadger.notify(exception, context: {
    my_data: 'value'
  }) # => '-1dfb92ae-9b01-42e9-9c13-31205b70744a'
end

# Custom notification:
Honeybadger.notify('Something went wrong.',
  error_class: 'MyClass',
  context: {my_data: 'value'}
) # => '06220c5a-b471-41e5-baeb-de247da45a56'

Parameters:

  • exception_or_opts (Exception, Hash, Object) (defaults to: nil)

    An Exception object, or a Hash of options which is used to build the notice. All other types of objects will be converted to a String and used as the :error_message.

  • opts (Hash) (defaults to: {})

    The options Hash when the first argument is an Exception.

  • kwargs (Hash)

    options as keyword args.

Options Hash (opts):

  • :error_message (String)

    The error message.

  • :error_class (String) — default: 'Notice'

    The class name of the error.

  • :backtrace (Array)

    The backtrace of the error (optional).

  • :fingerprint (String)

    The grouping fingerprint of the exception (optional).

  • :force (Boolean) — default: false

    Always report the exception when true, even when ignored (optional).

  • :sync (Boolean) — default: false

    Send data synchronously (skips the worker) (optional).

  • :tags (String)

    The comma-separated list of tags (optional).

  • :context (Hash)

    The context to associate with the exception (optional).

  • :controller (String)

    The controller name (such as a Rails controller) (optional).

  • :action (String)

    The action name (such as a Rails controller action) (optional).

  • :parameters (Hash)

    The HTTP request paramaters (optional).

  • :session (Hash)

    The HTTP request session (optional).

  • :url (String)

    The HTTP request URL (optional).

  • :cause (Exception)

    The cause for this error (optional).

Returns:

  • (String)

    UUID reference to the notice within Honeybadger.

  • (false)

    when ignored.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/honeybadger/agent.rb', line 127

def notify(exception_or_opts = nil, opts = {}, **kwargs)
  opts = opts.dup
  opts.merge!(kwargs)

  if exception_or_opts.is_a?(Exception)
    already_reported_notice_id = exception_or_opts.instance_variable_get(:@__hb_notice_id)
    return already_reported_notice_id if already_reported_notice_id
    opts[:exception] = exception_or_opts
  elsif exception_or_opts.respond_to?(:to_hash)
    opts.merge!(exception_or_opts.to_hash)
  elsif !exception_or_opts.nil?
    opts[:error_message] = exception_or_opts.to_s
  end

  validate_notify_opts!(opts)

  add_breadcrumb(
    "Honeybadger Notice",
    metadata: opts,
    category: "notice"
  ) if config[:'breadcrumbs.enabled']

  opts[:rack_env] ||= context_manager.get_rack_env
  opts[:global_context] ||= context_manager.get_context
  opts[:breadcrumbs] ||= breadcrumbs.dup
  opts[:request_id] ||= context_manager.get_request_id

  notice = Notice.new(config, opts)

  config.before_notify_hooks.each do |hook|
    break if notice.halted?
    with_error_handling { hook.call(notice) }
  end

  unless notice.api_key =~ NOT_BLANK
    error { sprintf('Unable to send error report: API key is missing. id=%s', notice.id) }
    return false
  end

  if !opts[:force] && notice.ignore?
    debug { sprintf('ignore notice feature=notices id=%s', notice.id) }
    return false
  end

  if notice.halted?
    debug { 'halted notice feature=notices' }
    return false
  end

  info { sprintf('Reporting error id=%s', notice.id) }

  if opts[:sync] || config[:sync]
    send_now(notice)
  else
    push(notice)
  end

  if exception_or_opts.is_a?(Exception)
    exception_or_opts.instance_variable_set(:@__hb_notice_id, notice.id) unless exception_or_opts.frozen?
  end

  notice.id
end

#registryObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



444
445
446
447
448
449
# File 'lib/honeybadger/agent.rb', line 444

def registry
  return @registry if defined?(@registry)
  @registry = Honeybadger::Registry.new.tap do |r|
    collect(Honeybadger::RegistryExecution.new(r, config, {}))
  end
end

#stop(force = false) ⇒ Object

Stops the Honeybadger service.

Examples:

Honeybadger.stop # => nil


373
374
375
376
377
378
# File 'lib/honeybadger/agent.rb', line 373

def stop(force = false)
  worker.shutdown(force)
  events_worker&.shutdown(force)
  metrics_worker&.shutdown(force)
  true
end

#stop_insights(force = false) ⇒ Object

Stops the Honeybadger Insights related services.

Examples:

Honeybadger.stop_insights # => nil


384
385
386
387
388
# File 'lib/honeybadger/agent.rb', line 384

def stop_insights(force = false)
  events_worker&.shutdown(force)
  metrics_worker&.shutdown(force)
  true
end

#timeObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



540
# File 'lib/honeybadger/agent.rb', line 540

def_delegator :instrumentation, :time

#track_deployment(environment: nil, revision: nil, local_username: nil, repository: nil) ⇒ Boolean

Track a new deployment

Examples:

Honeybadger.track_deployment(revision: 'be2ceb6')

Parameters:

  • :environment (String)

    The environment name. Defaults to the current configured environment.

  • :revision (String)

    The VCS revision being deployed. Defaults to the currently configured revision.

  • :local_username (String)

    The name of the user who performed the deploy.

  • :repository (String)

    The base URL of the VCS repository. It should be HTTPS-style.

Returns:

  • (Boolean)

    true if the deployment was successfully tracked and false otherwise.



219
220
221
222
223
224
225
226
227
228
# File 'lib/honeybadger/agent.rb', line 219

def track_deployment(environment: nil, revision: nil, local_username: nil, repository: nil)
  opts = {
    environment: environment || config[:env],
    revision: revision || config[:revision],
    local_username: local_username,
    repository: repository
  }
  response = backend.track_deployment(opts)
  response.success?
end

#with_rack_env(rack_env, &block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



515
516
517
518
519
520
521
522
# File 'lib/honeybadger/agent.rb', line 515

def with_rack_env(rack_env, &block)
  context_manager.set_rack_env(rack_env)
  context_manager.set_request_id(rack_env["action_dispatch.request_id"] || SecureRandom.uuid)
  yield
ensure
  context_manager.set_rack_env(nil)
  context_manager.set_request_id(nil)
end