# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Utils # Method utility used by Contrast::Logger::log module LogUtils DEFAULT_NAME = 'contrast.log' DEFAULT_LEVEL = ::Ougai::Logging::Severity::INFO VALID_LEVELS = ::Ougai::Logging::Severity::SEV_LABEL STDOUT_STR = 'STDOUT' STDERR_STR = 'STDERR' PROGNAME = 'Contrast Agent' DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%L%z' private def build path: STDOUT_STR, level_const: DEFAULT_LEVEL logger = case path when STDOUT_STR, STDERR_STR ::Ougai::Logger.new(Object.cs__const_get(path)) else ::Ougai::Logger.new(path) end add_contrast_loggers(logger) logger.progname = PROGNAME logger.level = level_const logger.formatter = Contrast::Logger::Format.new logger.formatter.datetime_format = DATE_TIME_FORMAT logger end def add_contrast_loggers logger logger.extend(Contrast::Logger::Application) logger.extend(Contrast::Logger::Request) logger.extend(Contrast::Logger::Time) end # Determine the valid path to which to log, given the precedence of config > settings > default. # # @param log_file [String, nil] the file to which to log as provided by the settings retrieved from the # TeamServer. # @return [String] the path to which to log or STDOUT / STDERR if one of those values provided. def find_valid_path log_file config = ::Contrast::CONFIG.root.agent.logger config_path = config&.path&.length.to_i.positive? ? config.path : nil valid_path(config_path || log_file) end def valid_path path path = path.nil? ? Contrast::Utils::ObjectShare::EMPTY_STRING : path return path if path == STDOUT_STR return path if path == STDERR_STR path = DEFAULT_NAME if path.empty? if write_permission?(path) path elsif write_permission?(DEFAULT_NAME) # Log once when the path is invalid. We'll change to this path, so no # need to log again. if previous_path != DEFAULT_NAME $stdout.puts "[!] Unable to write to '#{ path }'. Writing to default log '#{ DEFAULT_NAME }' instead." end DEFAULT_NAME else # Log once when the path is invalid. We'll change to this path, so no # need to log again. $stdout.puts "[!] Unable to write to '#{ path }'. Writing to standard out instead." STDOUT_STR end end # Determine the valid level to which to log, given the precedence of config > settings > default. # # @param log_level [String, nil] the level at which to log as provided by the settings retrieved from the # TeamServer. # @return [::Ougai::Logging::Severity] the level at which to log def find_valid_level log_level config = ::Contrast::CONFIG.root.agent.logger config_level = config&.level&.length&.positive? ? config.level : nil valid_level(config_level || log_level) end def valid_level level level ||= DEFAULT_LEVEL level = level.upcase if VALID_LEVELS.include?(level) Object.cs__const_get("::Ougai::Logging::Severity::#{ level }") else DEFAULT_LEVEL end rescue StandardError DEFAULT_LEVEL end # Log that the Agent log has changed and include some default information at the start of the log. def log_update logger.debug('Initialized new contrast agent logger') logger.debug_with_time('middleware: log environment') do logger.application_environment logger.application_configuration logger.application_libraries end end end end end