Class: Honeybadger::Plugin

Inherits:
Object
  • Object
show all
Defined in:
lib/honeybadger/plugin.rb

Overview

Honeybadger::Plugin defines the API for registering plugins with Honeybadger. Each plugin has requirements which must be satisfied before executing the plugin’s execution block(s). This allows us to detect optional dependencies and load the plugin for each dependency only if it’s present in the application.

Plugins may also define a collect block that is repeatedly called from within a thread. The MetricsWorker contains a loop that will call all enabled plugins’ collect method, and then sleep for 1 second. This block is useful for collecting and/or sending metrics at regular intervals.

See the plugins/ directory for examples of official plugins. If you’re interested in developing a plugin for Honeybadger, see the Integration Guide: docs.honeybadger.io/ruby/gem-reference/integration.html

Examples:


require 'honeybadger/plugin'
require 'honeybadger/ruby'

module Honeybadger
  module Plugins
    # Register your plugin with an optional name. If the name (such as
    # "my_framework") is not provided, Honeybadger will try to infer the name
    # from the current file.
    Plugin.register 'my_framework' do
      requirement do
        # Check to see if the thing you're integrating with is loaded. Return true
        # if it is, or false if it isn't. An exception in this block is equivalent
        # to returning false. Multiple requirement blocks are supported.
        defined?(MyFramework)
      end

      execution do
        # Write your integration. This code will be executed only if all requirement
        # blocks return true. An exception in this block will disable the plugin.
        # Multiple execution blocks are supported.
        MyFramework.on_exception do |exception|
          Honeybadger.notify(exception)
        end
      end

      collect do
        # This block will be periodically called at regular intervals. Here you can
        # gather metrics or inspect services. See the Honeybadger::InstrumentationHelper
        # module to see availble methods for metric collection.
        gauge 'scheduled_jobs', -> { MyFramework.stats.scheduled_jobs.count }
        gauge 'latency', -> { MyFramework.stats.latency }
      end
    end
  end
end

Defined Under Namespace

Classes: CollectorExecution, Execution

Constant Summary collapse

CALLER_FILE =

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

Regexp.new('\A(?:\w:)?([^:]+)(?=(:\d+))').freeze
@@instances =

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

{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Plugin

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.

Returns a new instance of Plugin.



159
160
161
162
163
164
165
# File 'lib/honeybadger/plugin.rb', line 159

def initialize(name)
  @name         = name
  @loaded       = false
  @requirements = []
  @executions   = []
  @collectors   = []
end

Instance Attribute Details

#executionsObject (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.



284
285
286
# File 'lib/honeybadger/plugin.rb', line 284

def executions
  @executions
end

#nameObject (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.



284
285
286
# File 'lib/honeybadger/plugin.rb', line 284

def name
  @name
end

#requirementsObject (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.



284
285
286
# File 'lib/honeybadger/plugin.rb', line 284

def requirements
  @requirements
end

Class Method Details

.instancesObject

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.



66
67
68
# File 'lib/honeybadger/plugin.rb', line 66

def instances
  @@instances
end

.load!(config) ⇒ 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.



93
94
95
96
97
98
99
100
101
# File 'lib/honeybadger/plugin.rb', line 93

def load!(config)
  instances.each_pair do |name, plugin|
    if config.load_plugin?(name)
      plugin.load!(config)
    else
      config.logger.debug(sprintf('skip plugin name=%s reason=disabled', name))
    end
  end
end

.name_from_caller(caller) ⇒ 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.



104
105
106
107
108
# File 'lib/honeybadger/plugin.rb', line 104

def name_from_caller(caller)
  caller && caller[0].match(CALLER_FILE) or
    fail("Unable to determine name from caller: #{caller.inspect}")
  File.basename($1)[/[^\.]+/]
end

.register(name = nil, &block) ⇒ Object

Register a new plugin with Honeybadger. See #requirement, #execution, and #collect..

Examples:


Honeybadger::Plugin.register 'my_framework' do
  requirement { }
  execution { }
  collect { }
end

Parameters:

  • name (String, Symbol) (defaults to: nil)

    The optional name of the plugin. Should use snake_case. The name is inferred from the current file name if omitted.

Returns:

  • nil



85
86
87
88
89
90
# File 'lib/honeybadger/plugin.rb', line 85

def register(name = nil, &block)
  name ||= name_from_caller(caller) or
    raise(ArgumentError, 'Plugin name is required, but was nil.')
  instances[key = name.to_sym] and fail("Already registered: #{name}")
  instances[key] = new(name).tap { |d| d.instance_eval(&block) }
end

Instance Method Details

#collect(options = {}, &block) ⇒ Object

Define an collect block. Collect blocks will be added to an execution queue if requirement blocks return true. The block will be called as frequently as once per second, but can be configured to increase it’s interval.

Examples:


Honeybadger::Plugin.register 'my_framework' do
  requirement { defined?(MyFramework) }

  collect do
    stats = MyFramework.stats
    gauge 'capacity', -> { stats.capcity }
  end

  collect(interval: 10) do
    stats = MyFramework.more_expensive_stats
    gauge 'other_stat', -> { stats.expensive_metric }
  end
end

Returns:

  • nil



234
235
236
# File 'lib/honeybadger/plugin.rb', line 234

def collect(options={}, &block)
  @collectors << [options, block]
end

#collectorsObject

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.



268
269
270
# File 'lib/honeybadger/plugin.rb', line 268

def collectors
  @collectors
end

#execution(&block) ⇒ Object

Define an execution block. Execution blocks will be executed if all requirement blocks return true.

Examples:


Honeybadger::Plugin.register 'my_framework' do
  requirement { defined?(MyFramework) }

  execution do
    MyFramework.on_exception {|err| Honeybadger.notify(err) }
  end

  execution do
    # Honeybadger's configuration object is available inside
    # execution blocks. It should generally not be used outside of
    # internal plugins. See +Config+.
    MyFramework.use_middleware(MyMiddleware) if config[:'my_framework.use_middleware']
  end
end

Returns:

  • nil



209
210
211
# File 'lib/honeybadger/plugin.rb', line 209

def execution(&block)
  @executions << block
end

#load!(config) ⇒ 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.



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

def load!(config)
  if @loaded
    config.logger.debug(sprintf('skip plugin name=%s reason=loaded', name))
    return false
  elsif ok?(config)
    config.logger.debug(sprintf('load plugin name=%s', name))
    @executions.each {|e| Execution.new(config, &e).call }
    @collectors.each {|o,b| CollectorExecution.new(name, config, o, &b).register! }
    @loaded = true
  else
    config.logger.debug(sprintf('skip plugin name=%s reason=requirement', name))
  end

  @loaded
rescue => e
  config.logger.error(sprintf("plugin error name=%s class=%s message=%s\n\t%s", name, e.class, e.message.dump, Array(e.backtrace).join("\n\t")))
  @loaded = true
  false
end

#loaded?Boolean

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.

Returns:

  • (Boolean)


273
274
275
# File 'lib/honeybadger/plugin.rb', line 273

def loaded?
  @loaded
end

#ok?(config) ⇒ Boolean

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.

Returns:

  • (Boolean)


239
240
241
242
243
244
# File 'lib/honeybadger/plugin.rb', line 239

def ok?(config)
  @requirements.all? {|r| Execution.new(config, &r).call }
rescue => e
  config.logger.error(sprintf("plugin error name=%s class=%s message=%s\n\t%s", name, e.class, e.message.dump, Array(e.backtrace).join("\n\t")))
  false
end

#requirement(&block) ⇒ Object

Define a requirement. All requirement blocks must return true for the plugin to be executed.

Examples:


Honeybadger::Plugin.register 'my_framework' do
  requirement { defined?(MyFramework) }

  # Honeybadger's configuration object is available inside
  # requirement blocks. It should generally not be used outside of
  # internal plugins. See +Config+.
  requirement { config[:'my_framework.enabled'] }

  execution { }
end

Returns:

  • nil



184
185
186
# File 'lib/honeybadger/plugin.rb', line 184

def requirement(&block)
  @requirements << block
end

#reset!Object

Used for testing only; don’t normally call this. :)



279
280
281
# File 'lib/honeybadger/plugin.rb', line 279

def reset!
  @loaded = false
end