Class: Honeybadger::Config

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Logging::Helper
Defined in:
lib/honeybadger/config.rb,
lib/honeybadger/config/env.rb,
lib/honeybadger/config/ruby.rb,
lib/honeybadger/config/yaml.rb,
lib/honeybadger/config/defaults.rb

Overview

Internal: The Config class is used to manage Honeybadger's initialization and configuration. Please don't depend on any internal classes or methods outside of Honeybadger as they may change without notice.

Defined Under Namespace

Modules: Env, Yaml Classes: Boolean, ConfigError, Mash, Ruby

Constant Summary collapse

KEY_REPLACEMENT =
Regexp.new('[^a-z\d_]', Regexp::IGNORECASE).freeze
DOTTED_KEY =
Regexp.new('\A([^\.]+)\.(.+)\z').freeze
NOT_BLANK =
Regexp.new('\S').freeze
IVARS =
[:@ruby, :@env, :@yaml, :@framework].freeze
IGNORE_DEFAULT =
['ActionController::RoutingError',
'AbstractController::ActionNotFound',
'ActionController::MethodNotAllowed',
'ActionController::UnknownHttpMethod',
'ActionController::NotImplemented',
'ActionController::UnknownFormat',
'ActionController::InvalidAuthenticityToken',
'ActionController::InvalidCrossOriginRequest',
'ActionDispatch::ParamsParser::ParseError',
'ActionController::BadRequest',
'ActionController::ParameterMissing',
'ActiveRecord::RecordNotFound',
'ActionController::UnknownAction',
'CGI::Session::CookieStore::TamperedWithCookie',
'Mongoid::Errors::DocumentNotFound',
'Sinatra::NotFound'].map(&:freeze).freeze
DEVELOPMENT_ENVIRONMENTS =
['development', 'test', 'cucumber'].map(&:freeze).freeze
DEFAULT_PATHS =
['honeybadger.yml', 'config/honeybadger.yml', "#{ENV['HOME']}/honeybadger.yml"].map(&:freeze).freeze
OPTIONS =
{
  api_key: {
    description: 'The API key for your Honeybadger project.',
    default: nil,
    type: String
  },
  env: {
    description: 'The current application\'s environment name.',
    default: nil,
    type: String
  },
  report_data: {
    description: 'Enable/disable reporting of data. Defaults to true for non-development environments.',
    default: nil,
    type: Boolean
  },
  root: {
    description: 'The project\'s absolute root path.',
    default: Dir.pwd,
    type: String
  },
  revision: {
    description: 'The git revision of the project.',
    default: nil,
    type: String
  },
  hostname: {
    description: 'The hostname of the current box.',
    default: Socket.gethostname,
    type: String
  },
  backend: {
    description: 'An alternate backend to use for reporting data.',
    default: nil,
    type: String
  },
  debug: {
    description: 'Enables debug logging.',
    default: false,
    type: Boolean
  },
  disabled: {
    description: 'Prevents Honeybadger from starting entirely.',
    default: false,
    type: Boolean
  },
  development_environments: {
    description: 'Environments which will not report data by default (use report_data to enable/disable explicitly).',
    default: DEVELOPMENT_ENVIRONMENTS,
    type: Array
  },
  :'send_data_at_exit' => {
    description: 'Send remaining data when Ruby exits.',
    default: true,
    type: Boolean
  },
  max_queue_size: {
    description: 'Maximum number of items for each worker queue.',
    default: 1000,
    type: Integer
  },
  plugins: {
    description: 'An optional list of plugins to load. Default is to load all plugins.',
    default: nil,
    type: Array
  },
  :'skipped_plugins' => {
    description: 'An optional list of plugins to skip.',
    default: nil,
    type: Array
  },
  :'config.path' => {
    description: 'The path (absolute, or relative from config.root) to the project\'s YAML configuration file.',
    default: DEFAULT_PATHS,
    type: String
  },
  :'logging.path' => {
    description: 'The path (absolute, or relative from config.root) to the log file.',
    default: nil,
    type: String
  },
  :'logging.level' => {
    description: 'The log level.',
    default: 'INFO',
    type: String
  },
  :'logging.debug' => {
    description: 'Override debug logging.',
    default: nil,
    type: Boolean
  },
  :'logging.tty_level' => {
    description: 'Level to log when attached to a terminal (anything < logging.level will always be ignored).',
    default: 'DEBUG',
    type: String
  },
  :'connection.secure' => {
    description: 'Use SSL when sending data.',
    default: true,
    type: Boolean
  },
  :'connection.host' => {
    description: 'The host to use when sending data.',
    default: 'api.honeybadger.io'.freeze,
    type: String
  },
  :'connection.port' => {
    description: 'The port to use when sending data.',
    default: nil,
    type: Integer
  },
  :'connection.system_ssl_cert_chain' => {
    description: 'Use the system\'s SSL certificate chain (if available).',
    default: false,
    type: Boolean
  },
  :'connection.ssl_ca_bundle_path' => {
    description: 'Use this ca bundle when establishing secure connections.',
    default: nil,
    type: String
  },
  :'connection.http_open_timeout' => {
    description: 'The HTTP open timeout when connecting to the server.',
    default: 2,
    type: Integer
  },
  :'connection.http_read_timeout' => {
    description: 'The HTTP read timeout when connecting to the server.',
    default: 5,
    type: Integer
  },
  :'connection.proxy_host' => {
    description: 'The proxy host to use when sending data.',
    default: nil,
    type: String
  },
  :'connection.proxy_port' => {
    description: 'The proxy port to use when sending data.',
    default: nil,
    type: Integer
  },
  :'connection.proxy_user' => {
    description: 'The proxy user to use when sending data.',
    default: nil,
    type: String
  },
  :'connection.proxy_pass' => {
    description: 'The proxy password to use when sending data.',
    default: nil,
    type: String
  },
  :'request.filter_keys' => {
    description: 'A list of keys to filter when sending request data.',
    default: ['password'.freeze, 'password_confirmation'.freeze, 'HTTP_AUTHORIZATION'.freeze].freeze,
    type: Array
  },
  :'request.disable_session' => {
    description: 'Prevent session from being sent with request data.',
    default: false,
    type: Boolean
  },
  :'request.disable_params' => {
    description: 'Prevent params from being sent with request data.',
    default: false,
    type: Boolean
  },
  :'request.disable_environment' => {
    description: 'Prevent Rack environment from being sent with request data.',
    default: false,
    type: Boolean
  },
  :'request.disable_url' => {
    description: 'Prevent url from being sent with request data (Rack environment may still contain it in some cases).',
    default: false,
    type: Boolean
  },
  :'user_informer.enabled' => {
    description: 'Enable the UserInformer middleware.',
    default: true,
    type: Boolean
  },
  :'user_informer.info' => {
    description: 'Replacement string for HTML comment in templates.',
    default: 'Honeybadger Error {{error_id}}'.freeze,
    type: String
  },
  :'feedback.enabled' => {
    description: 'Enable the UserFeedback middleware.',
    default: true,
    type: Boolean
  },
  :'exceptions.enabled' => {
    description: 'Enable automatic reporting of exceptions.',
    default: true,
    type: Boolean
  },
  :'exceptions.ignore' => {
    description: 'A list of additional exceptions to ignore (includes default ignored exceptions).',
    default: IGNORE_DEFAULT,
    type: Array
  },
  :'exceptions.ignore_only' => {
    description: 'A list of exceptions to ignore (overrides the default ignored exceptions).',
    default: nil,
    type: Array
  },
  :'exceptions.ignored_user_agents' => {
    description: 'A list of user agents to ignore.',
    default: [].freeze,
    type: Array
  },
  :'exceptions.rescue_rake' => {
    description: 'Enable reporting exceptions in rake tasks.',
    default: !STDOUT.tty?,
    type: Boolean
  },
  :'exceptions.notify_at_exit' => {
    description: 'Report unhandled exception when Ruby crashes (at_exit).',
    default: true,
    type: Boolean
  },
  :'exceptions.source_radius' => {
    description: 'The number of lines before and after the source when reporting snippets.',
    default: 2,
    type: Integer
  },
  :'exceptions.local_variables' => {
    description: 'Enable sending local variables. Requires binding_of_caller to be loaded.',
    default: false,
    type: Boolean
  },
  :'exceptions.unwrap' => {
    description: 'Reports #original_exception or #cause one level up from rescued exception when available.',
    default: false,
    type: Boolean
  },
  :'delayed_job.attempt_threshold' => {
    description: 'The number of attempts before notifications will be sent.',
    default: 0,
    type: Integer
  },
  :'sidekiq.attempt_threshold' => {
    description: 'The number of attempts before notifications will be sent.',
    default: 0,
    type: Integer
  },
  :'shoryuken.attempt_threshold' => {
    description: 'The number of attempts before notifications will be sent.',
    default: 0,
    type: Integer
  },
  :'sidekiq.use_component' => {
    description: 'Automatically set the component to the class of the job. Helps with grouping.',
    default: true,
    type: Boolean
  },
  :'sinatra.enabled' => {
    description: 'Enable Sinatra auto-initialization.',
    default: true,
    type: Boolean
  },
  :'resque.resque_retry.send_exceptions_when_retrying' => {
    description: 'Send exceptions when retrying job.',
    default: true,
    type: Boolean
  }
}.freeze
DEFAULTS =
Hash[OPTIONS.map{|k,v| [k, v[:default]] }].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Config

Returns a new instance of Config.


40
41
42
43
44
45
# File 'lib/honeybadger/config.rb', line 40

def initialize(opts = {})
  @ruby = opts.freeze
  @env = {}.freeze
  @yaml = {}.freeze
  @framework = {}.freeze
end

Instance Attribute Details

#envObject

Returns the value of attribute env


47
48
49
# File 'lib/honeybadger/config.rb', line 47

def env
  @env
end

#frameworkObject

Returns the value of attribute framework


47
48
49
# File 'lib/honeybadger/config.rb', line 47

def framework
  @framework
end

#rubyObject

Returns the value of attribute ruby


47
48
49
# File 'lib/honeybadger/config.rb', line 47

def ruby
  @ruby
end

#yamlObject

Returns the value of attribute yaml


47
48
49
# File 'lib/honeybadger/config.rb', line 47

def yaml
  @yaml
end

Instance Method Details

#backendObject


133
134
135
136
# File 'lib/honeybadger/config.rb', line 133

def backend
  init_backend! unless @backend
  @backend
end

#backend=(backend) ⇒ Object


138
139
140
141
# File 'lib/honeybadger/config.rb', line 138

def backend=(backend)
  set(:backend, backend)
  @backend = nil
end

#backtrace_filterObject


82
83
84
85
# File 'lib/honeybadger/config.rb', line 82

def backtrace_filter
  self[:backtrace_filter] = Proc.new if block_given?
  self[:backtrace_filter]
end

#ca_bundle_pathObject


174
175
176
177
178
179
180
181
182
# File 'lib/honeybadger/config.rb', line 174

def ca_bundle_path
  if self[:'connection.system_ssl_cert_chain'] && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
    OpenSSL::X509::DEFAULT_CERT_FILE
  elsif self[:'connection.ssl_ca_bundle_path']
    self[:'connection.ssl_ca_bundle_path']
  else
    local_cert_path
  end
end

#configure {|ruby_config| ... } ⇒ Object

Yields:

  • (ruby_config)

74
75
76
77
78
79
80
# File 'lib/honeybadger/config.rb', line 74

def configure
  ruby_config = Ruby.new(self)
  yield(ruby_config)
  self.ruby = ruby.merge(ruby_config).freeze
  @logger = @backend = nil
  self
end

#connection_portObject


188
189
190
191
192
193
194
195
196
# File 'lib/honeybadger/config.rb', line 188

def connection_port
  if self[:'connection.port']
    self[:'connection.port']
  elsif self[:'connection.secure']
    443
  else
    80
  end
end

#connection_protocolObject


198
199
200
201
202
203
204
# File 'lib/honeybadger/config.rb', line 198

def connection_protocol
  if self[:'connection.secure']
    'https'
  else
    'http'
  end
end

#debug?Boolean

Returns:


157
158
159
# File 'lib/honeybadger/config.rb', line 157

def debug?
  !!self[:debug]
end

#detected_frameworkObject


253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/honeybadger/config.rb', line 253

def detected_framework
  if self[:framework] =~ NOT_BLANK
    self[:framework].to_sym
  elsif defined?(::Rails::VERSION) && ::Rails::VERSION::STRING > '3.0'
    :rails
  elsif defined?(::Sinatra::VERSION)
    :sinatra
  elsif defined?(::Rack.release)
    :rack
  else
    :ruby
  end
end

#dev?Boolean

Returns:


147
148
149
# File 'lib/honeybadger/config.rb', line 147

def dev?
  self[:env] && Array(self[:development_environments]).include?(self[:env])
end

#disabled?Boolean

Returns:


143
144
145
# File 'lib/honeybadger/config.rb', line 143

def disabled?
  !!self[:disabled]
end

#exception_filterObject


87
88
89
90
# File 'lib/honeybadger/config.rb', line 87

def exception_filter
  self[:exception_filter] = Proc.new if block_given?
  self[:exception_filter]
end

#exception_fingerprintObject


92
93
94
95
# File 'lib/honeybadger/config.rb', line 92

def exception_fingerprint
  self[:exception_fingerprint] = Proc.new if block_given?
  self[:exception_fingerprint]
end

#excluded_request_keysObject


214
215
216
217
218
219
220
221
# File 'lib/honeybadger/config.rb', line 214

def excluded_request_keys
  [].tap do |keys|
    keys << :session  if self[:'request.disable_session']
    keys << :params   if self[:'request.disable_params']
    keys << :cgi_data if self[:'request.disable_environment']
    keys << :url      if self[:'request.disable_url']
  end
end

#framework_nameObject


267
268
269
270
271
272
273
274
275
# File 'lib/honeybadger/config.rb', line 267

def framework_name
  case detected_framework
  when :rails then "Rails #{::Rails::VERSION::STRING}"
  when :sinatra then "Sinatra #{::Sinatra::VERSION}"
  when :rack then "Rack #{::Rack.release}"
  else
    "Ruby #{RUBY_VERSION}"
  end
end

#get(key) ⇒ Object Also known as: []


97
98
99
100
101
102
103
104
105
106
# File 'lib/honeybadger/config.rb', line 97

def get(key)
  IVARS.each do |var|
    source = instance_variable_get(var)
    if source.has_key?(key)
      return source[key]
    end
  end

  DEFAULTS[key]
end

#ignored_classesObject


166
167
168
169
170
171
172
# File 'lib/honeybadger/config.rb', line 166

def ignored_classes
  ignore_only = get(:'exceptions.ignore_only')
  return ignore_only if ignore_only
  return DEFAULTS[:'exceptions.ignore'] unless ignore = get(:'exceptions.ignore')

  DEFAULTS[:'exceptions.ignore'] | Array(ignore)
end

#init!(opts = {}, env = ENV) ⇒ Object

Called by framework (see lib/honeybadger/init/) at the point of initialization. This is not required for the notifier to work (i.e. with `require 'honeybadger/ruby'`).


52
53
54
55
56
57
58
59
60
61
62
# File 'lib/honeybadger/config.rb', line 52

def init!(opts = {}, env = ENV)
  load!(framework: opts, env: env)

  init_logging!
  init_backend!

  logger.info(sprintf('Initializing Honeybadger Error Tracker for Ruby. Ship it! version=%s framework=%s', Honeybadger::VERSION, detected_framework))
  logger.warn('Entering development mode: data will not be reported.') if dev? && backend.kind_of?(Backend::Null)

  self
end

#load!(framework: {}, env: ENV) ⇒ Object


64
65
66
67
68
69
70
71
72
# File 'lib/honeybadger/config.rb', line 64

def load!(framework: {}, env: ENV)
  return self if @loaded
  self.framework = framework.freeze
  self.env = Env.new(env).freeze
  load_config_from_disk {|yaml| self.yaml = yaml.freeze }
  detect_revision!
  @loaded = true
  self
end

#load_plugin?(name) ⇒ Boolean

Returns:


234
235
236
237
238
# File 'lib/honeybadger/config.rb', line 234

def load_plugin?(name)
  return false if includes_token?(self[:'skipped_plugins'], name)
  return true unless self[:plugins].kind_of?(Array)
  includes_token?(self[:plugins], name)
end

#local_cert_pathObject


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

def local_cert_path
  File.expand_path(File.join('..', '..', '..', 'resources', 'ca-bundle.crt'), __FILE__)
end

#log_debug?Boolean

Returns:


161
162
163
164
# File 'lib/honeybadger/config.rb', line 161

def log_debug?
  return debug? if self[:'logging.debug'].nil?
  !!self[:'logging.debug']
end

#log_level(key = logging.level) ⇒ Object


223
224
225
226
227
228
229
230
231
232
# File 'lib/honeybadger/config.rb', line 223

def log_level(key = :'logging.level')
  case self[key].to_s
  when /\A(0|debug)\z/i then Logger::DEBUG
  when /\A(1|info)\z/i  then Logger::INFO
  when /\A(2|warn)\z/i  then Logger::WARN
  when /\A(3|error)\z/i then Logger::ERROR
  else
    Logger::INFO
  end
end

#loggerObject

Internal Helpers


128
129
130
131
# File 'lib/honeybadger/config.rb', line 128

def logger
  init_logging! unless @logger
  @logger
end

#max_queue_sizeObject


206
207
208
# File 'lib/honeybadger/config.rb', line 206

def max_queue_size
  self[:max_queue_size]
end

#params_filtersObject


210
211
212
# File 'lib/honeybadger/config.rb', line 210

def params_filters
  Array(self[:'request.filter_keys'])
end

#public?Boolean

Returns:


151
152
153
154
155
# File 'lib/honeybadger/config.rb', line 151

def public?
  return true if self[:report_data]
  return false if self[:report_data] == false
  !self[:env] || !dev?
end

#root_regexpObject

Match the project root.

Returns Regexp matching the project root in a file string.


243
244
245
246
247
248
249
250
251
# File 'lib/honeybadger/config.rb', line 243

def root_regexp
  return @root_regexp if @root_regexp
  return nil if @no_root

  root = get(:root).to_s
  @no_root = true and return nil unless root =~ NOT_BLANK

  @root_regexp = Regexp.new("^#{ Regexp.escape(root) }")
end

#set(key, value) ⇒ Object Also known as: []=


109
110
111
112
# File 'lib/honeybadger/config.rb', line 109

def set(key, value)
  self.ruby = ruby.merge(key => value).freeze
  @logger = @backend = nil
end

#to_hash(defaults = false) ⇒ Object Also known as: to_h


115
116
117
118
119
120
121
122
123
# File 'lib/honeybadger/config.rb', line 115

def to_hash(defaults = false)
  hash = [:@ruby, :@env, :@yaml, :@framework].reverse.reduce({}) do |a,e|
    a.merge!(instance_variable_get(e))
  end

  hash = DEFAULTS.merge(hash) if defaults

  undotify_keys(hash.select {|k,v| DEFAULTS.has_key?(k) })
end