require 'logger' require 'stringio' module Immunio # Subclass global Logger class to add TRACE level class Logger < ::Logger module Severity TRACE = -1 end include Severity def trace?; @level <= TRACE; end def trace(progname = nil, &block) add(TRACE, nil, progname, &block) end def format_severity(severity) SEV_LABEL[severity] || 'ANY' end SEV_LABEL = Array.new(::Logger::SEV_LABEL) SEV_LABEL[-1] = 'TRACE' end attr_reader :logger def self.create_startup_logger @startup_messages = StringIO.new @logger = Logger.new @startup_messages setup_logger_formatter end def self.setup_logger_formatter logger.formatter = proc do |severity, datetime, _progname, msg| "[#{datetime}] #{severity}: #{msg}\n" end end def self.switch_to_real_logger(log_file, log_level) # Have we already switched to real logger? return if !defined?(@startup_messages) if log_file == "STDOUT" @logger = Logger.new $stdout elsif log_file == "STDERR" @logger = Logger.new $stderr else path = Pathname.new(log_file) begin FileUtils.mkdir_p path.dirname unless File.exist? path.dirname file = File.open path, 'a' file.binmode file.sync = true @logger = Logger.new file log_file = path.realpath rescue StandardError => e logger.warn "Failed to open #{log_file} (#{path.realdirpath}) for logging (#{e.message})" @logger = Logger.new $stderr log_file = "STDERR" end end # Dump saved log messages during startup to real log logger << @startup_messages.string remove_instance_variable(:@startup_messages) setup_logger_formatter begin logger.level = Logger.const_get(log_level.to_s.upcase) rescue logger.level = Logger::DEBUG logger.debug "Failed to interpret log level #{log_level}, falling back to debug" end logger.debug "Logging to #{log_file}" end def self.logger @logger end end