class Appsignal::Config

def self.system_tmp_dir

Returns:
  • (String) - System's tmp directory.

Other tags:
    Api: - private
def self.system_tmp_dir
  if Gem.win_platform?
    Dir.tmpdir
  else
    File.realpath("/tmp")
  end
end

def [](key)

def [](key)
  config_hash[key]
end

def []=(key, value)

def []=(key, value)
  config_hash[key] = value
end

def active?

def active?
  @valid && config_hash[:active]
end

def config_file

def config_file
  @config_file ||=
    root_path.nil? ? nil : File.join(root_path, "config", "appsignal.yml")
end

def detect_from_system

def detect_from_system
  {}.tap do |hash|
    hash[:log] = "stdout" if Appsignal::System.heroku?
    # Make active by default if APPSIGNAL_PUSH_API_KEY is present
    hash[:active] = true if ENV["APPSIGNAL_PUSH_API_KEY"]
  end
end

def initialize(root_path, env, initial_config = {}, logger = Appsignal.logger)

def initialize(root_path, env, initial_config = {}, logger = Appsignal.logger)
  @root_path      = root_path
  @logger         = logger
  @valid          = false
  @config_hash    = Hash[DEFAULT_CONFIG]
  env_loaded_from_initial = env.to_s
  @env =
    if ENV.key?("APPSIGNAL_APP_ENV".freeze)
      env_loaded_from_env = ENV["APPSIGNAL_APP_ENV".freeze]
    else
      env_loaded_from_initial
    end
  # Set config based on the system
  @system_config = detect_from_system
  merge(system_config)
  # Initial config
  @initial_config = initial_config
  merge(initial_config)
  # Load the config file if it exists
  @file_config = load_from_disk || {}
  merge(file_config)
  # Load config from environment variables
  @env_config = load_from_environment
  merge(env_config)
  # Validate that we have a correct config
  validate
  # Track origin of env
  @initial_config[:env] = env_loaded_from_initial if env_loaded_from_initial
  @env_config[:env] = env_loaded_from_env if env_loaded_from_env
end

def load_from_disk

def load_from_disk
  return if !config_file || !File.exist?(config_file)
  configurations = YAML.load(ERB.new(IO.read(config_file)).result)
  config_for_this_env = configurations[env]
  if config_for_this_env
    config_for_this_env =
      config_for_this_env.each_with_object({}) do |(key, value), hash|
        hash[key.to_sym] = value # convert keys to symbols
      end
    maintain_backwards_compatibility(config_for_this_env)
  else
    @logger.error "Not loading from config file: config for '#{env}' not found"
    nil
  end
end

def load_from_environment

def load_from_environment
  config = {}
  # Configuration with string type
  %w[APPSIGNAL_PUSH_API_KEY APPSIGNAL_APP_NAME APPSIGNAL_PUSH_API_ENDPOINT
     APPSIGNAL_FRONTEND_ERROR_CATCHING_PATH APPSIGNAL_HTTP_PROXY
     APPSIGNAL_LOG APPSIGNAL_LOG_PATH APPSIGNAL_WORKING_DIR_PATH
     APPSIGNAL_HOSTNAME APPSIGNAL_CA_FILE_PATH APP_REVISION].each do |var|
    env_var = ENV[var]
    next unless env_var
    config[ENV_TO_KEY_MAPPING[var]] = env_var
  end
  # Configuration with boolean type
  %w[APPSIGNAL_ACTIVE APPSIGNAL_DEBUG APPSIGNAL_INSTRUMENT_NET_HTTP
     APPSIGNAL_INSTRUMENT_REDIS APPSIGNAL_INSTRUMENT_SEQUEL
     APPSIGNAL_SKIP_SESSION_DATA APPSIGNAL_ENABLE_FRONTEND_ERROR_CATCHING
     APPSIGNAL_ENABLE_ALLOCATION_TRACKING APPSIGNAL_ENABLE_GC_INSTRUMENTATION
     APPSIGNAL_RUNNING_IN_CONTAINER APPSIGNAL_ENABLE_HOST_METRICS
     APPSIGNAL_SEND_PARAMS APPSIGNAL_ENABLE_MINUTELY_PROBES
     APPSIGNAL_FILES_WORLD_ACCESSIBLE].each do |var|
    env_var = ENV[var]
    next unless env_var
    config[ENV_TO_KEY_MAPPING[var]] = env_var.casecmp("true").zero?
  end
  # Configuration with array of strings type
  %w[APPSIGNAL_IGNORE_ACTIONS APPSIGNAL_IGNORE_ERRORS
     APPSIGNAL_IGNORE_NAMESPACES APPSIGNAL_FILTER_PARAMETERS
     APPSIGNAL_FILTER_SESSION_DATA APPSIGNAL_REQUEST_HEADERS].each do |var|
    env_var = ENV[var]
    next unless env_var
    config[ENV_TO_KEY_MAPPING[var]] = env_var.split(",")
  end
  config
end

def log_file_path

def log_file_path
  path = config_hash[:log_path] || root_path && File.join(root_path, "log")
  if path && File.writable?(path)
    return File.join(File.realpath(path), "appsignal.log")
  end
  system_tmp_dir = self.class.system_tmp_dir
  if File.writable? system_tmp_dir
    $stdout.puts "appsignal: Unable to log to '#{path}'. Logging to "\
      "'#{system_tmp_dir}' instead. Please check the "\
      "permissions for the application's (log) directory."
    File.join(system_tmp_dir, "appsignal.log")
  else
    $stdout.puts "appsignal: Unable to log to '#{path}' or the "\
      "'#{system_tmp_dir}' fallback. Please check the permissions "\
      "for the application's (log) directory."
  end
end

def maintain_backwards_compatibility(configuration)

Used by {#load_from_disk}. No compatibility for env variables or initial config currently.

versions of the gem
Maintain backwards compatibility with config files generated by earlier
def maintain_backwards_compatibility(configuration)
  configuration.tap do |config|
    DEPRECATED_CONFIG_KEY_MAPPING.each do |old_key, new_key|
      old_config_value = config.delete(old_key)
      next unless old_config_value
      deprecation_message \
        "Old configuration key found. Please update the "\
        "'#{old_key}' to '#{new_key}'.",
        logger
      next if config[new_key] # Skip if new key is already in use
      config[new_key] = old_config_value
    end
    if config.include?(:working_dir_path)
      deprecation_message \
        "'working_dir_path' is deprecated, please use " \
        "'working_directory_path' instead and specify the " \
        "full path to the working directory",
        logger
    end
  end
end

def merge(new_config)

def merge(new_config)
  new_config.each do |key, value|
    unless config_hash[key].nil?
      @logger.debug("Config key '#{key}' is being overwritten")
    end
    config_hash[key] = value
  end
end

def valid?

def valid?
  @valid
end

def validate

def validate
  # Strip path from endpoint so we're backwards compatible with
  # earlier versions of the gem.
  # TODO: Move to its own method, maybe in `#[]=`?
  endpoint_uri = URI(config_hash[:endpoint])
  config_hash[:endpoint] =
    if endpoint_uri.port == 443
      "#{endpoint_uri.scheme}://#{endpoint_uri.host}"
    else
      "#{endpoint_uri.scheme}://#{endpoint_uri.host}:#{endpoint_uri.port}"
    end
  if config_hash[:push_api_key]
    @valid = true
  else
    @valid = false
    @logger.error "Push api key not set after loading config"
  end
end

def write_to_environment # rubocop:disable Metrics/AbcSize

rubocop:disable Metrics/AbcSize
def write_to_environment # rubocop:disable Metrics/AbcSize
  ENV["_APPSIGNAL_ACTIVE"]                       = active?.to_s
  ENV["_APPSIGNAL_APP_PATH"]                     = root_path.to_s
  ENV["_APPSIGNAL_AGENT_PATH"]                   = File.expand_path("../../../ext", __FILE__).to_s
  ENV["_APPSIGNAL_ENVIRONMENT"]                  = env
  ENV["_APPSIGNAL_LANGUAGE_INTEGRATION_VERSION"] = "ruby-#{Appsignal::VERSION}"
  ENV["_APPSIGNAL_DEBUG_LOGGING"]                = config_hash[:debug].to_s
  ENV["_APPSIGNAL_LOG"]                          = config_hash[:log]
  ENV["_APPSIGNAL_LOG_FILE_PATH"]                = log_file_path.to_s if log_file_path
  ENV["_APPSIGNAL_PUSH_API_ENDPOINT"]            = config_hash[:endpoint]
  ENV["_APPSIGNAL_PUSH_API_KEY"]                 = config_hash[:push_api_key]
  ENV["_APPSIGNAL_APP_NAME"]                     = config_hash[:name]
  ENV["_APPSIGNAL_HTTP_PROXY"]                   = config_hash[:http_proxy]
  ENV["_APPSIGNAL_IGNORE_ACTIONS"]               = config_hash[:ignore_actions].join(",")
  ENV["_APPSIGNAL_IGNORE_ERRORS"]                = config_hash[:ignore_errors].join(",")
  ENV["_APPSIGNAL_IGNORE_NAMESPACES"]            = config_hash[:ignore_namespaces].join(",")
  ENV["_APPSIGNAL_RUNNING_IN_CONTAINER"]         = config_hash[:running_in_container].to_s
  ENV["_APPSIGNAL_WORKING_DIR_PATH"]             = config_hash[:working_dir_path] if config_hash[:working_dir_path]
  ENV["_APPSIGNAL_WORKING_DIRECTORY_PATH"]       = config_hash[:working_directory_path] if config_hash[:working_directory_path]
  ENV["_APPSIGNAL_ENABLE_HOST_METRICS"]          = config_hash[:enable_host_metrics].to_s
  ENV["_APPSIGNAL_HOSTNAME"]                     = config_hash[:hostname].to_s
  ENV["_APPSIGNAL_PROCESS_NAME"]                 = $PROGRAM_NAME
  ENV["_APPSIGNAL_CA_FILE_PATH"]                 = config_hash[:ca_file_path].to_s
  ENV["_APPSIGNAL_DNS_SERVERS"]                  = config_hash[:dns_servers].join(",")
  ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"]       = config_hash[:files_world_accessible].to_s
  ENV["_APP_REVISION"]                           = config_hash[:revision].to_s
end