spriteCloud/teelogger

View on GitHub
lib/teelogger/formatter.rb

Summary

Maintainability
A
0 mins
Test Coverage
#
# TeeLogger
# https://github.com/spriteCloud/teelogger
#
# Copyright (c) 2014-2015 spriteCloud B.V. and other TeeLogger contributors.
# All rights reserved.
#
require 'tai64'

module TeeLogger
  ##
  # Placeholders for the formatter take a single argument, and convert it to
  # a string argument using placeholder specific rules.
  module FormatterPlaceholders
    def severity(severity, time, progname, message)
      severity.to_s.upcase
    end

    def short_severity(severity, time, progname, message)
      severity.to_s.upcase[0..0]
    end

    def logger_timestamp(severity, time, progname, message)
      time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d" % time.usec
    end

    def iso8601_timestamp(severity, time, progname, message)
      time.strftime("%Y-%m-%dT%H:%M:%S%z")
    end

    def iso8601_timestamp_utc(severity, time, progname, message)
      time.dup.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
    end

    def tai64n_timestamp(severity, time, progname, message)
      Tai64::Time.new(time).to_label.to_s
    end

    def logger(severity, time, progname, message)
      progname.to_s
    end

    def message(severity, time, progname, message)
      message.to_s
    end

    def pid(severity, time, progname, message)
      $$.to_s
    end

    extend self
  end # module FormatterPlaceholders

  ##
  # The formatter class accepts a format string, but in a different format from
  # Kernel#sprintf. Instead, placeholders enclosed in {} (but without the Ruby-
  # typical #, so not #{}) will get replaced with the output of the functions
  # defined in FormatterPlaceholders.
  #
  # The class also defines a few example format strings as constants.
  class Formatter
    # Valid placeholder to use in the format string
    PLACEHOLDERS = ::TeeLogger::FormatterPlaceholders.instance_methods

    ##
    # Some format strings defined

    # Format string most similar to the Ruby logger
    FORMAT_LOGGER = "{short_severity}, [{logger_timestamp} \#{pid}] {severity} -- {logger}: {message}\n"

    # Default format string
    FORMAT_DEFAULT = "{short_severity}, [{iso8601_timestamp} \#{pid}] {logger}: {message}\n"

    # Shorter format string
    FORMAT_SHORT = "{short_severity}, [{iso8601_timestamp}] {message}\n"

    # DJB format using Tai64N labels
    FORMAT_DJB = "{tai64n_timestamp} {severity}: {message}\n"

    ##
    # Implementation
    def initialize(format = FORMAT_DEFAULT)
      @format = format
    end

    def call(*args) # shortern *args; the same pattern as placeholders is used
      # Formatting the message means replacing each placeholder with results
      # from the placeholder function. We're caching results to save some time.
      cache = {}
      message = @format.dup

      PLACEHOLDERS.each do |placeholder|
        value = nil
        begin
          value = cache.fetch(placeholder,
                ::TeeLogger::FormatterPlaceholders.send(placeholder.to_sym, *args))
          cache[placeholder] = value
        rescue NoMethodError
          raise "Invalid formatter placeholder used in format string: #{placeholder}"
        end

        message.gsub!(/{#{placeholder}}/, value)
      end

      return message
    end
  end # class Formatter
end # module TeeLogger