egonbraun/logmsg

View on GitHub
lib/logmsg/logfile.rb

Summary

Maintainability
A
25 mins
Test Coverage
require 'logger'

module Logmsg
  # Manage a destination stream or file that will be used to output log messages
  # @author Egon Braun
  class LogFile
    # Log messages usually contain a timestamp associated with them, this is the
    # default date and time formats that logmsg will use when printing out logs
    # @private
    DEFAULT_DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'

    # The default severity used to print log messages
    # @see http://ruby-doc.org/stdlib-2.2.4/libdoc/logger/rdoc/Logger.html
    # @private
    DEFAULT_SEVERITY = Logger::INFO

    # The default severity threshold used to print log messages
    # @see http://ruby-doc.org/stdlib-2.2.4/libdoc/logger/rdoc/Logger.html
    # @private
    DEFAULT_SEVERITY_THRESHOLD = Logger::WARN

    # The default format of the log message
    # @private
    DEFAULT_FORMAT = '%{severity}: %{msg}'

    # Constants to define the standard output and error streams and set them out
    # from normal files
    # @private
    STDOUT_STREAM = 1
    STDERR_STREAM = 2

    attr_reader :name
    attr_reader :registered
    attr_reader :path
    attr_reader :threshold
    attr_reader :severities
    attr_accessor :datetime_format
    attr_accessor :format

    def initialize(name, path = STDOUT_STREAM)
      @name = name
      @registered = false
      @path = path
      @threshold = DEFAULT_SEVERITY_THRESHOLD
      @severities = []
      @datetime_format = DEFAULT_DATETIME_FORMAT
      @format = DEFAULT_FORMAT
    end

    # Adds a logger definition linked to a file descriptor
    # @private
    def register
      unregister if registered
      init_formatter
      init_logger
      @registered = true
    end

    # Remove the logger and output stream definitions closing all open files
    # @private
    def unregister
      unless @logger.nil?
        @logger.close unless @path == STDOUT_STREAM || @path == STDERR_STREAM
        @logger = nil
      end

      @registered = false
    end

    # Log a debug message
    # @param [String] message the log message
    # @example
    # => debug('This is a log message')
    # @private
    def debug(message)
      log(message, Logger::DEBUG)
    end

    # Log an informational message
    # @param [String] message the log message
    # @example
    # => info('This is a log message')
    # @private
    def info(message)
      log(message, Logger::INFO)
    end

    # Log a warning message
    # @param [String] message the log message
    # @example
    # => warn('This is a log message')
    # @private
    def warn(message)
      log(message, Logger::WARN)
    end

    # Log an error message
    # @param [String] message the log message
    # @example
    # => error('This is a log message')
    # @private
    def error(message)
      log(message, Logger::ERROR)
    end

    # Log a fatal error message
    # @param [String] message the log message
    # @example
    # => fatal('This is a log message')
    # @private
    def fatal(message)
      log(message, Logger::FATAL)
    end

    # Log an unknown message
    # @param [String] message the log message
    # @example
    # => debug('This is a log message')
    # @private
    def unknown(message)
      log(message, Logger::UNKNOWN)
    end

    # Setter for the threshold instance variable
    # @param [String] value the severity identifier
    # @private
    def threshold=(value)
      @threshold = get_logger_severity(value)
    end

    # Setter for the severities instance variable
    # @param [Array] value list of severities
    # @private
    def severities=(value)
      @severities = value.map { |v| get_logger_severity(v) }
    end

    # Setter for the path instance variable
    # @param [String] value of the path
    # @private
    def path=(value)
      @path = case value.downcase
              when 'stdout' then STDOUT_STREAM
              when 'stderr' then STDERR_STREAM
              else value
              end
    end

    protected

    # Initializes the logger's formatter proc based on the value of format
    # instance variable. This method should only be called inside the register
    # method
    # @private
    def init_formatter
      @formatter = proc do |s, d, p, m|
        "#{@format}\n" % { severity: s, datetime: d, progname: p, msg: m }
      end
    end

    # Initializes the logger object from the standard library. This method
    # should only be called inside the register method
    # @private
    def init_logger
      @logger = case @path
                when STDOUT_STREAM then Logger.new(STDOUT)
                when STDERR_STREAM then Logger.new(STDERR)
                else Logger.new(File.open(@path, 'a'))
                end
      @logger.datetime_format = @datetime_format
      @logger.sev_threshold = @threshold unless @severities.any?
      @logger.formatter = @formatter unless @formatter.nil?
    end

    private

    # Log a message to the configured logger using the severity and program name
    # as specified
    # @param [String] message message to be logged
    # @param [Number] severity message's severity
    # @see http://ruby-doc.org/stdlib-2.2.4/libdoc/logger/rdoc/Logger.html
    # @private
    def log(message, severity = DEFAULT_SEVERITY)
      fail 'Logfile not registered' unless @registered
      return if message.nil?
      @logger.add(severity, message, @name) if should_log?(severity)
    end

    # Translate a string or symbol to its respective severity identifier from
    # the Logger standard library
    # @param [String, Symbol] severity the severity name
    # @private
    def get_logger_severity(severity)
      Logger.const_get(severity.upcase)
    rescue NameError
      Logger::UNKNOWN
    end

    # Check if the logger should output the message
    # @private
    def should_log?(severity)
      return true if @severities.include?(severity)
      severity >= @threshold && @severities.length == 0
    end
  end
end