class Skylight::Core::Config

Constants

MUTEX

@api private

Attributes

alert_logger[W]
environment[R]

@api private

logger[W]

Public Class Methods

default_values() click to toggle source

Default values for Skylight configuration keys

# File lib/skylight/core/config.rb, line 50
def self.default_values
  {
    log_file:                 "-".freeze,
    log_level:                "INFO".freeze,
    alert_log_file:           "-".freeze,
    log_sql_parse_errors:     true,
    enable_segments:          true,
    enable_sidekiq:           false,
    sinatra_route_prefixes:   false,
    'heroku.dyno_info_path':  "/etc/heroku/dyno"
  }
end
env_matcher() click to toggle source
# File lib/skylight/core/config.rb, line 20
def self.env_matcher; /^(?:SK|SKYLIGHT)_(.+)$/ end
env_prefix() click to toggle source
# File lib/skylight/core/config.rb, line 21
def self.env_prefix; "SKYLIGHT_" end
env_to_key() click to toggle source

Map environment variable keys with Skylight configuration keys

# File lib/skylight/core/config.rb, line 25
def self.env_to_key
  {
    # == Logging ==
    "LOG_FILE"       => :log_file,
    "LOG_LEVEL"      => :log_level,
    "ALERT_LOG_FILE" => :alert_log_file,
    "LOG_SQL_PARSE_ERRORS" => :log_sql_parse_errors,

    # == Proxy ==
    "PROXY_URL" => :proxy_url,

    # == Instrumenter ==
    "ENABLE_SEGMENTS" => :enable_segments,
    "ENABLE_SIDEKIQ" => :enable_sidekiq,
    "SINATRA_ROUTE_PREFIXES" => :sinatra_route_prefixes,

    # == User config settings ==
    "USER_CONFIG_PATH" => :user_config_path,

    # == Heroku settings ==
    "HEROKU_DYNO_INFO_PATH" => :'heroku.dyno_info_path'
  }
end
legacy_keys() click to toggle source

Maps legacy config keys to new config keys

# File lib/skylight/core/config.rb, line 83
def self.legacy_keys
  # No legacy keys for now
  {}
end
load(opts = {}, env = ENV) click to toggle source
# File lib/skylight/core/config.rb, line 127
def self.load(opts = {}, env = ENV)
  attrs = {}
  path = opts.delete(:file)
  environment = opts.delete(:environment)

  if path
    error = nil
    begin
      attrs = YAML.safe_load(ERB.new(File.read(path)).result,
                             [], # permitted_classes
                             [], # permitted_symbols
                             true # aliases enabled
                            )
      error = "empty file" unless attrs
      error = "invalid format" if attrs && !attrs.is_a?(Hash)
    rescue Exception => e
      error = e.message
    end

    raise ConfigError, "could not load config file; msg=#{error}" if error
  end

  if env
    attrs[:priority] = remap_env(env)
  end

  config = new(environment, attrs)

  opts.each do |k, v|
    config[k] = v
  end

  config
end
log_name() click to toggle source

rubocop:disable Style/SingleLineMethods, Layout/EmptyLineBetweenDefs

# File lib/skylight/core/config.rb, line 17
def self.log_name; "Skylight" end
native_env_keys() click to toggle source
# File lib/skylight/core/config.rb, line 74
def self.native_env_keys
  %i[
    version
    root
    proxy_url
  ]
end
new(*args) click to toggle source

@api private

# File lib/skylight/core/config.rb, line 97
def initialize(*args)
  attrs = {}

  if args.last.is_a?(Hash)
    attrs = args.pop.dup
  end

  @values   = {}
  @priority = {}
  @regexp   = nil
  @alert_logger = nil
  @logger = nil

  p = attrs.delete(:priority)

  if (@environment = args[0])
    @regexp = /^#{Regexp.escape(@environment)}\.(.+)$/
  end

  attrs.each do |k, v|
    self[k] = v
  end

  if p
    p.each do |k, v|
      @priority[self.class.remap_key(k)] = v
    end
  end
end
remap_env(env) click to toggle source

@api private

# File lib/skylight/core/config.rb, line 168
def self.remap_env(env)
  ret = {}

  return ret unless env

  # Only set if it exists, we don't want to set to a nil value
  if (proxy_url = Util::Proxy.detect_url(env))
    ret[:proxy_url] = proxy_url
  end

  env.each do |k, val|
    next unless k =~ env_matcher
    next unless (key = env_to_key[$1])

    ret[key] =
      case val
      when /^false$/i      then false
      when /^true$/i       then true
      when /^(nil|null)$/i then nil
      when /^\d+$/         then val.to_i
      when /^\d+\.\d+$/    then val.to_f
      else val
      end
  end

  ret
end
remap_key(key) click to toggle source
# File lib/skylight/core/config.rb, line 162
def self.remap_key(key)
  key = key.to_sym
  legacy_keys[key] || key
end
required_keys() click to toggle source
# File lib/skylight/core/config.rb, line 63
def self.required_keys
  # Nothing is required in this base class.
  {}
end
server_validated_keys() click to toggle source
# File lib/skylight/core/config.rb, line 68
def self.server_validated_keys
  # Nothing is validated for now, but this is a list of symbols
  # for the key we want to validate.
  []
end
service_name() click to toggle source
# File lib/skylight/core/config.rb, line 18
def self.service_name; log_name end
support_email() click to toggle source
# File lib/skylight/core/config.rb, line 19
def self.support_email; "support@skylight.io" end
validators() click to toggle source
# File lib/skylight/core/config.rb, line 88
def self.validators
  # None for now
  {}
end

Public Instance Methods

[](key, default = nil)
Alias for: get
[]=(key, val, scope = nil)
Alias for: set
alert_logger() click to toggle source
# File lib/skylight/core/config.rb, line 392
def alert_logger
  @alert_logger ||= MUTEX.synchronize do
    unless (l = @alert_logger)
      out = get(:alert_log_file)
      out = Util::AlertLogger.new(load_logger) if out == "-"

      l = create_logger(out)
      l.level = Logger::DEBUG
    end

    l
  end
end
as_json(*) click to toggle source
# File lib/skylight/core/config.rb, line 319
def as_json(*)
  {
    config: {
      priority: @priority,
      values:   @values
    }
  }
end
check_file_permissions(file, key) click to toggle source
# File lib/skylight/core/config.rb, line 217
def check_file_permissions(file, key)
  file_root = File.dirname(file)

  # Try to make the directory, don't blow up if we can't. Our writable? check will fail later.
  FileUtils.mkdir_p file_root rescue nil

  if File.exist?(file) && !FileTest.writable?(file)
    raise ConfigError, "File `#{file}` is not writable. Please set #{key} in your config to a writable path"
  end

  unless FileTest.writable?(file_root)
    raise ConfigError, "Directory `#{file_root}` is not writable. Please set #{key} in your config to a writable path"
  end
end
check_logfile_permissions(log_file, key) click to toggle source
# File lib/skylight/core/config.rb, line 232
def check_logfile_permissions(log_file, key)
  return if log_file == "-" # STDOUT
  log_file = File.expand_path(log_file, root)
  check_file_permissions(log_file, key)
end
duration_ms(key, default = nil) click to toggle source
# File lib/skylight/core/config.rb, line 297
def duration_ms(key, default = nil)
  if (v = self[key]) && v.to_s =~ /^\s*(\d+)(s|sec|ms|micros|nanos)?\s*$/
    v = $1.to_i
    case $2
    when "ms"
      v
    when "micros"
      v / 1_000
    when "nanos"
      v / 1_000_000
    else # "s", "sec", nil
      v * 1000
    end
  else
    default
  end
end
enable_segments?() click to toggle source
# File lib/skylight/core/config.rb, line 408
def enable_segments?
  !!get(:enable_segments)
end
enable_sidekiq?() click to toggle source
# File lib/skylight/core/config.rb, line 412
def enable_sidekiq?
  !!get(:enable_sidekiq)
end
gc() click to toggle source

@api private

# File lib/skylight/core/config.rb, line 357
def gc
  @gc ||= GC.new(self, get("gc.profiler", VM::GC.new))
end
get(key, default = nil) { |key| ... } click to toggle source
# File lib/skylight/core/config.rb, line 243
def get(key, default = nil)
  key = self.class.remap_key(key)

  return @priority[key] if @priority.key?(key)
  return @values[key]   if @values.key?(key)
  return self.class.default_values[key] if self.class.default_values.key?(key)

  if default
    return default
  elsif block_given?
    return yield key
  end

  nil
end
Also aliased as: []
ignored_endpoints() click to toggle source

@api private

# File lib/skylight/core/config.rb, line 362
def ignored_endpoints
  @ignored_endpoints ||=
    begin
      ignored_endpoints = get(:ignored_endpoints)

      # If, for some odd reason you have a comma in your endpoint name, use the
      # YML config instead.
      if ignored_endpoints.is_a?(String)
        ignored_endpoints = ignored_endpoints.split(/\s*,\s*/)
      end

      val = Array(get(:ignored_endpoint))
      val.concat(Array(ignored_endpoints))
      val
    end
end
key?(key) click to toggle source
# File lib/skylight/core/config.rb, line 238
def key?(key)
  key = self.class.remap_key(key)
  @priority.key?(key) || @values.key?(key)
end
logger() click to toggle source
# File lib/skylight/core/config.rb, line 383
def logger
  @logger ||=
    MUTEX.synchronize do
      load_logger
    end
end
on_heroku?() click to toggle source
# File lib/skylight/core/config.rb, line 424
def on_heroku?
  File.exist?(get(:'heroku.dyno_info_path'))
end
root() click to toggle source
# File lib/skylight/core/config.rb, line 379
def root
  self[:root] || Dir.pwd
end
send_or_get(val) click to toggle source
# File lib/skylight/core/config.rb, line 293
def send_or_get(val)
  respond_to?(val) ? send(val) : get(val)
end
set(key, val, scope = nil) click to toggle source
# File lib/skylight/core/config.rb, line 261
def set(key, val, scope = nil)
  if scope
    key = [scope, key].join(".")
  end

  if val.is_a?(Hash)
    val.each do |k, v|
      set(k, v, key)
    end
  else
    k = self.class.remap_key(key)

    if (validator = self.class.validators[k])
      blk, msg = validator

      unless blk.call(val, self)
        error_msg = "invalid value for #{k} (#{val})"
        error_msg << ", #{msg}" if msg
        raise ConfigError, error_msg
      end
    end

    if @regexp && k =~ @regexp
      @priority[$1.to_sym] = val
    end

    @values[k] = val
  end
end
Also aliased as: []=
sinatra_route_prefixes?() click to toggle source
# File lib/skylight/core/config.rb, line 416
def sinatra_route_prefixes?
  !!get(:sinatra_route_prefixes)
end
to_json(*) click to toggle source
# File lib/skylight/core/config.rb, line 315
def to_json(*)
  JSON.generate(as_json)
end
to_native_env() click to toggle source
# File lib/skylight/core/config.rb, line 328
def to_native_env
  ret = []

  self.class.native_env_keys.each do |key|
    value = send_or_get(key)
    unless value.nil?
      env_key = self.class.env_to_key.key(key) || key.upcase
      ret << "#{self.class.env_prefix}#{env_key}" << cast_for_env(value)
    end
  end

  ret
end
user_config() click to toggle source
# File lib/skylight/core/config.rb, line 420
def user_config
  @user_config ||= UserConfig.new(self)
end
validate!() click to toggle source

@api private

# File lib/skylight/core/config.rb, line 197
def validate!
  self.class.required_keys.each do |k, v|
    unless get(k)
      raise ConfigError, "#{v} required"
    end
  end

  log_file = self[:log_file]
  alert_log_file = self[:alert_log_file]

  check_logfile_permissions(log_file, "log_file")
  check_logfile_permissions(alert_log_file, "alert_log_file")

  true
end
validate_with_server() click to toggle source
# File lib/skylight/core/config.rb, line 213
def validate_with_server
  true
end
version() click to toggle source
Helpers =====
# File lib/skylight/core/config.rb, line 352
def version
  VERSION
end
write(_path) click to toggle source
# File lib/skylight/core/config.rb, line 342
def write(_path)
  raise "not implemented"
end

Private Instance Methods

cast_for_env(val) click to toggle source
# File lib/skylight/core/config.rb, line 470
def cast_for_env(val)
  case val
  when true  then "true"
  when false then "false"
  when nil   then "nil"
  else val.to_s
  end
end
create_logger(out) click to toggle source
# File lib/skylight/core/config.rb, line 430
def create_logger(out)
  l = begin
    if out.is_a?(String)
      out = File.expand_path(out, root)
      # May be redundant since we also do this in the permissions check
      FileUtils.mkdir_p(File.dirname(out))
    end

    Logger.new(out)
  rescue
    Logger.new(STDOUT)
  end
  l.progname = self.class.log_name
  l
end
load_logger() click to toggle source
# File lib/skylight/core/config.rb, line 446
def load_logger
  unless (l = @logger)
    out = get(:log_file)
    out = STDOUT if out == "-"

    l = create_logger(out)
    l.level =
      if trace?
        Logger::DEBUG
      else
        case get(:log_level)
        when /^debug$/i then Logger::DEBUG
        when /^info$/i  then Logger::INFO
        when /^warn$/i  then Logger::WARN
        when /^error$/i then Logger::ERROR
        when /^fatal$/i then Logger::FATAL
        else Logger::ERROR
        end
      end
  end

  l
end