# frozen_string_literal: true module Prefab class LoggerClient < Logger SEP = "." BASE_KEY = "log-level" UNKNOWN_PATH = "unknown." INTERNAL_PREFIX = "cloud.prefab.client" LOG_LEVEL_LOOKUPS = { Prefab::LogLevel::NOT_SET_LOG_LEVEL => Logger::DEBUG, Prefab::LogLevel::TRACE => Logger::DEBUG, Prefab::LogLevel::DEBUG => Logger::DEBUG, Prefab::LogLevel::INFO => Logger::INFO, Prefab::LogLevel::WARN => Logger::WARN, Prefab::LogLevel::ERROR => Logger::ERROR, Prefab::LogLevel::FATAL => Logger::FATAL } def initialize(logdev, formatter: nil, prefix: nil) super(logdev) self.formatter = formatter @config_client = BootstrappingConfigClient.new @silences = Concurrent::Map.new(initial_capacity: 2) @prefix = prefix end def add(severity, message = nil, progname = nil, loc, &block) path = get_loc_path(loc) path = "#{@prefix}#{@prefix && '.'}#{path}" log(message, path, progname, severity, &block) end def log_internal(message, path = nil, progname, severity, &block) if path path = "#{INTERNAL_PREFIX}.#{path}" else path = INTERNAL_PREFIX end log(message, path, progname, severity, &block) end def log(message, path, progname, severity, &block) level = level_of(path) progname = "#{path}: #{progname}" severity ||= Logger::UNKNOWN if @logdev.nil? || severity < level || @silences[local_log_id] return true end if progname.nil? progname = @progname end if message.nil? if block_given? message = yield else message = progname progname = @progname end end @logdev.write( format_message(format_severity(severity), Time.now, progname, message)) true end def debug(progname = nil, &block) add(DEBUG, nil, progname, caller_locations(1, 1)[0], &block) end def info(progname = nil, &block) add(INFO, nil, progname, caller_locations(1, 1)[0], &block) end def warn(progname = nil, &block) add(WARN, nil, progname, caller_locations(1, 1)[0], &block) end def error(progname = nil, &block) add(ERROR, nil, progname, caller_locations(1, 1)[0], &block) end def fatal(progname = nil, &block) add(FATAL, nil, progname, caller_locations(1, 1)[0], &block) end def debug? true; end def info? true; end def warn? true; end def error? true; end def fatal? true; end def level DEBUG end def set_config_client(config_client) @config_client = config_client end def local_log_id Thread.current.__id__ end def silence @silences[local_log_id] = true yield self ensure @silences[local_log_id] = false end private # Find the closest match to 'log_level.path' in config def level_of(path) closest_log_level_match = @config_client.get(BASE_KEY, :WARN) path.split(SEP).inject([BASE_KEY]) do |memo, n| memo << n val = @config_client.get(memo.join(SEP), nil) unless val.nil? closest_log_level_match = val end memo end closest_log_level_match_int = Prefab::LogLevel.resolve(closest_log_level_match) LOG_LEVEL_LOOKUPS[closest_log_level_match_int] end def get_loc_path(loc) loc_path = loc.absolute_path || loc.to_s get_path(loc_path, loc.base_label) end # sanitize & clean the path of the caller so the key # looks like log_level.app.models.user def get_path(absolute_path, base_label) path = (absolute_path || UNKNOWN_PATH).dup path.slice! Dir.pwd path.gsub!(/(.*)?(?=\/lib)/im, "") # replace everything before first lib path = path.gsub("/", SEP).gsub(/.rb.*/, "") + SEP + base_label path.slice! ".lib" path.slice! SEP path end end # StubConfigClient to be used while config client initializes # since it may log class BootstrappingConfigClient def get(key, default = nil) ENV["PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL"] ? ENV["PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL"].upcase.to_sym : default end end end