lib/airbrake-ruby/config.rb
module Airbrake
# Represents the Airbrake config. A config contains all the options that you
# can use to configure an Airbrake instance.
#
# @api public
# @since v1.0.0
class Config
# @return [Integer] the project identificator. This value *must* be set.
# @api public
attr_accessor :project_id
# @return [String] the project key. This value *must* be set.
# @api public
attr_accessor :project_key
# @return [Hash] the proxy parameters such as (:host, :port, :user and
# :password)
# @api public
attr_accessor :proxy
# @return [Logger] the default logger used for debug output
# @api public
attr_reader :logger
# @return [String] the version of the user's application
# @api public
attr_accessor :app_version
# @return [Hash{String=>String}] arbitrary versions that your app wants to
# track
# @api public
# @since v2.10.0
attr_accessor :versions
# @return [Integer] the max number of notices that can be queued up
# @api public
attr_accessor :queue_size
# @return [Integer] the number of worker threads that process the notice
# queue
# @api public
attr_accessor :workers
# @return [String] the host, which provides the API endpoint to which
# exceptions should be sent
# @api public
# @since v5.0.0
attr_accessor :error_host
# @return [String] the host, which provides the API endpoint to which
# APM data should be sent
# @api public
# @since v5.0.0
attr_accessor :apm_host
# @return [String, Pathname] the working directory of your project
# @api public
attr_accessor :root_directory
# @return [String, Symbol] the environment the application is running in
# @api public
attr_accessor :environment
# @return [Array<String,Symbol,Regexp>] the array of environments that
# forbids sending exceptions when the application is running in them.
# Other possible environments not listed in the array will allow sending
# occurring exceptions.
# @api public
attr_accessor :ignore_environments
# @return [Integer] The HTTP timeout in seconds.
# @api public
attr_accessor :timeout
# @return [Array<String, Symbol, Regexp>] the keys, which should be
# filtered
# @api public
# @since v4.15.0
attr_accessor :allowlist_keys
# @return [Array<String, Symbol, Regexp>] the keys, which should be
# filtered
# @api public
# @since v4.15.0
attr_accessor :blocklist_keys
# @return [Boolean] true if the library should attach code hunks to each
# frame in a backtrace, false otherwise
# @api public
# @since v2.5.0
attr_accessor :code_hunks
# @return [Boolean] true if the library should send route performance stats
# to Airbrake, false otherwise
# @api public
# @since v3.2.0
attr_accessor :performance_stats
# @return [Integer] how many seconds to wait before sending collected route
# stats
# @api private
# @since v3.2.0
attr_accessor :performance_stats_flush_period
# @return [Boolean] true if the library should send SQL stats to Airbrake,
# false otherwise
# @api public
# @since v4.6.0
attr_accessor :query_stats
# @return [Boolean] true if the library should send job/queue/worker stats
# to Airbrake, false otherwise
# @api public
# @since v4.12.0
attr_accessor :job_stats
# @return [Boolean] true if the library should send error reports to
# Airbrake, false otherwise
# @api public
# @since v5.0.0
attr_accessor :error_notifications
# @return [String] the host which should be used for fetching remote
# configuration options
# @api public
# @since v5.0.0
attr_accessor :remote_config_host
# @return [String] true if notifier should periodically fetch remote
# configuration, false otherwise
# @api public
# @since v5.2.0
attr_accessor :remote_config
# @return [Boolean] true if the library should keep a backlog of failed
# notices or APM events and retry them after an interval, false otherwise
# @api public
# @since v6.2.0
attr_accessor :backlog
class << self
# @return [Config]
attr_writer :instance
# @return [Config]
def instance
@instance ||= new
end
end
# @param [Hash{Symbol=>Object}] user_config the hash to be used to build the
# config
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def initialize(user_config = {})
self.proxy = {}
self.queue_size = 100
self.workers = 1
self.code_hunks = true
self.logger = ::Logger.new(File::NULL).tap { |l| l.level = Logger::WARN }
self.project_id = user_config[:project_id]
self.project_key = user_config[:project_key]
self.error_host = self.apm_host = 'https://api.airbrake.io'
self.remote_config_host = 'https://notifier-configs.airbrake.io'
self.ignore_environments = []
self.timeout = user_config[:timeout]
self.blocklist_keys = []
self.allowlist_keys = []
self.root_directory = File.realpath(
(defined?(Bundler) && Bundler.root) ||
Dir.pwd,
)
self.versions = {}
self.performance_stats = true
self.performance_stats_flush_period = 15
self.query_stats = true
self.job_stats = true
self.error_notifications = true
self.remote_config = true
self.backlog = true
merge(user_config)
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
# The full URL to the Airbrake Notice API. Based on the +:error_host+ option.
# @return [URI] the endpoint address
def error_endpoint
@error_endpoint ||=
begin
self.error_host = ('https://' << error_host) if error_host !~ %r{\Ahttps?://}
api = "api/v3/projects/#{project_id}/notices"
URI.join(error_host, api)
end
end
# Sets the logger. Never allows to assign `nil` as the logger.
# @return [Logger] the logger
def logger=(logger)
@logger = logger || @logger
end
# Merges the given +config_hash+ with itself.
#
# @example
# config.merge(host: 'localhost:8080')
#
# @return [self] the merged config
def merge(config_hash)
config_hash.each_pair { |option, value| set_option(option, value) }
self
end
# @return [Boolean] true if the config meets the requirements, false
# otherwise
def valid?
validate.resolved?
end
# @return [Promise]
# @see Validator.validate
def validate
Validator.validate(self)
end
# @return [Promise]
# @see Validator.check_notify_ability
def check_notify_ability
Validator.check_notify_ability(self)
end
# @return [Boolean] true if the config ignores current environment, false
# otherwise
def ignored_environment?
check_notify_ability.rejected?
end
# @return [Promise] resolved promise if config is valid & can notify,
# rejected otherwise
def check_configuration
promise = validate
return promise if promise.rejected?
check_notify_ability
end
# @return [Promise] resolved promise if neither of the performance options
# reject it, false otherwise
def check_performance_options(metric)
promise = Airbrake::Promise.new
if !performance_stats
promise.reject("The Performance Stats feature is disabled")
elsif metric.is_a?(Airbrake::Query) && !query_stats
promise.reject("The Query Stats feature is disabled")
elsif metric.is_a?(Airbrake::Queue) && !job_stats
promise.reject("The Job Stats feature is disabled")
else
promise
end
end
HOST_DEPRECATION_MSG = "**Airbrake: the 'host' option is deprecated. Use " \
"'error_host' instead".freeze
def host
logger.warn(HOST_DEPRECATION_MSG)
@error_host
end
def host=(value)
logger.warn(HOST_DEPRECATION_MSG)
@error_host = value
end
private
def set_option(option, value)
__send__("#{option}=", value)
rescue NoMethodError
raise Airbrake::Error, "unknown option '#{option}'"
end
end
end