class Logsly::Logging182::Layouts::Pattern::FormatMethodBuilder

This class is used to build the `format` method for the Pattern layout. It parses the user defined pattern and emits Ruby source code (as a string) that can be `eval`d in the context of the Pattern layout instance.

Constants

COLOR_ALIAS_TABLE

Human name aliases for directives - used for colorization of tokens

DIRECTIVE_RGXP

Matches the first directive encountered and the stuff around it.

  • $1 is the stuff before directive or “” if not applicable

  • $2 is the %#.# match within directive group

  • $3 is the directive letter

  • $4 is the precision specifier for the logger name

  • $5 is the stuff after the directive or “” if not applicable

DIRECTIVE_TABLE

Arguments to sprintf keyed to directive letters

Attributes

color_scheme[R]
format_string[R]
layout[R]
name_map_count[RW]
pattern[RW]
sprintf_args[R]

Public Class Methods

new( pattern_layout ) click to toggle source

Creates the format method builder and initializes some variables from the given Patter layout instance.

pattern_layout - The Pattern Layout instance

# File lib/logsly/logging182/layouts/pattern.rb, line 350
def initialize( pattern_layout )
  @layout         = pattern_layout
  @pattern        = layout.pattern.dup
  @color_scheme   = layout.color_scheme

  @sprintf_args   = []
  @format_string  = '"'
  @name_map_count = 0
end

Public Instance Methods

build_code() click to toggle source

This method returns a String which can be `eval`d in the context of the Pattern layout. When it is `eval`d, a `format` method is defined in the Pattern layout.

At the heart of the format method is `sprintf`. The conversion pattern specified in the Pattern layout is parsed and converted into a format string and corresponding arguments list. The format string and arguments are then processed by `sprintf` to format log events.

Returns a Ruby code as a String.

# File lib/logsly/logging182/layouts/pattern.rb, line 385
def build_code
  build_format_string

  sprintf = "sprintf("
  sprintf << format_string
  sprintf << ', ' + sprintf_args.join(', ') unless sprintf_args.empty?
  sprintf << ")"

  if colorize_lines?
    sprintf = "color_scheme.color(#{sprintf}, Logsly::Logging182::LNAMES[event.level])"
  end

  code = "undef :format if method_defined? :format\n"
  code << "def format( event )\n#{sprintf}\nend\n"
end
build_format_string() click to toggle source

This method builds the format string used by `sprintf` to format log events. The conversion pattern given by the user is iteratively parsed by a regular expression into separate format directives. Each directive builds up the format string and the corresponding arguments list that will be formatted.

The actual building of the format string is handled by separate directive specific methods. Those handlers also populate the arguments list passed to `sprintf`.

Returns the format String.

# File lib/logsly/logging182/layouts/pattern.rb, line 412
def build_format_string
  while true
    match = DIRECTIVE_RGXP.match(pattern)
    _, pre, format, directive, precision, post = *match
    format_string << pre unless pre.empty?

    case directive
    when '%'; format_string << '%%'
    when 'c'; handle_logger( format, directive, precision )
    when 'l'; handle_level(  format, directive, precision )
    when 'X'; handle_mdc(    format, directive, precision )
    when 'x'; handle_ndc(    format, directive, precision )

    when *DIRECTIVE_TABLE.keys
      handle_directives(format, directive, precision)

    when nil; break
    else
      raise ArgumentError, "illegal format character - '#{directive}'"
    end

    break if post.empty?
    self.pattern = post
  end

  format_string << '"'
end
colorize?() click to toggle source

Returns `true` if the log messages should be colorized.

# File lib/logsly/logging182/layouts/pattern.rb, line 361
def colorize?
  color_scheme && !color_scheme.lines?
end
colorize_levels?() click to toggle source

Returns `true` if the log levels have special colorization defined.

# File lib/logsly/logging182/layouts/pattern.rb, line 371
def colorize_levels?
  color_scheme && color_scheme.levels?
end
colorize_lines?() click to toggle source

Returns `true` if the log messages should be colorized by line.

# File lib/logsly/logging182/layouts/pattern.rb, line 366
def colorize_lines?
  color_scheme && color_scheme.lines?
end
handle_directives( format, directive, precision ) click to toggle source

Handles the rest of the directives; none of these need any special handling.

format - format String directive - the directive character precision - added back to the format string

Returns nil

# File lib/logsly/logging182/layouts/pattern.rb, line 553
def handle_directives( format, directive, precision )
  fmt = format + 's'
  fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[directive]) if colorize?

  format_string << fmt
  format_string << "{#{precision}}" if precision
  sprintf_args << DIRECTIVE_TABLE[directive]

  nil
end
handle_level( format, directive, precision ) click to toggle source

Add the log event level to the `format_string` and the `sprintf_args`. The color scheme is taken into account when formatting the log event level.

format - format String directive - the directive character ('l') precision - added back to the format string

Returns nil

# File lib/logsly/logging182/layouts/pattern.rb, line 482
def handle_level( format, directive, precision )
  if colorize_levels?
    name_map = Logsly::Logging182::LNAMES.map do |name|
      color_scheme.color(("#{format}s" % name), name)
    end
    var = "@name_map_#{name_map_count}"
    layout.instance_variable_set(var.to_sym, name_map)
    self.name_map_count += 1

    format_string << '%s'
    format_string << "{#{precision}}" if precision
    sprintf_args << "#{var}[event.level]"
  else
    format_string << format + 's'
    format_string << "{#{precision}}" if precision
    sprintf_args << DIRECTIVE_TABLE[directive]
  end

  nil
end
handle_logger( format, directive, slice ) click to toggle source

Add the logger name to the `format_string` and the `sprintf_args`. The `slice` argument is a little interesting - this is the number of logger name segments to keep. If we have a logger named “Foo::Bar::Baz” and our `slice` is 2, then “Bar::Baz” will appear in the generated log message. So the `slice` selects the last two parts of the logger name.

format - format String directive - the directive character ('c') slice - the number of name segments to keep

Returns nil

# File lib/logsly/logging182/layouts/pattern.rb, line 451
def handle_logger( format, directive, slice )
  fmt = format + 's'
  fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[directive]) if colorize?

  format_string << fmt
  sprintf_args << DIRECTIVE_TABLE[directive].dup

  if slice
    numeric = Integer(slice) rescue nil
    if numeric
      raise ArgumentError, "logger name slice must be an integer greater than zero: #{numeric}" unless numeric > 0
      sprintf_args.last <<
          ".split(Logsly::Logging182::Repository::PATH_DELIMITER)" \
          ".last(#{slice}).join(Logsly::Logging182::Repository::PATH_DELIMITER)"
    else
      format_string << "{#{slice}}"
    end
  end

  nil
end
handle_mdc( format, directive, key ) click to toggle source

Add a Mapped Diagnostic Context to the `format_string` and the `sprintf_args`. Only one MDC value is added at a time, so this directive can appear multiple times using various keys.

format - format String directive - the directive character ('X') key - which MDC value to add to the log message

Returns nil

# File lib/logsly/logging182/layouts/pattern.rb, line 512
def handle_mdc( format, directive, key )
  raise ArgumentError, "MDC must have a key reference" unless key
  fmt = format + 's'
  fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[directive]) if colorize?

  format_string << fmt
  sprintf_args << "Logsly::Logging182.mdc['#{key}']"

  nil
end
handle_ndc( format, directive, separator ) click to toggle source

Add a Nested Diagnostic Context to the `format_string` and the `sprintf_args`. Since the NDC is an Array of values, the directive will appear only once in the conversion pattern. A `separator` is inserted between the values in generated log message.

format - format String directive - the directive character ('x') separator - used to separate the values in the NDC array

Returns nil

# File lib/logsly/logging182/layouts/pattern.rb, line 533
def handle_ndc( format, directive, separator )
  fmt = format + 's'
  fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[directive]) if colorize?

  format_string << fmt
  separator = separator.to_s
  separator = ' ' if separator.empty?
  sprintf_args << "Logsly::Logging182.ndc.context.join('#{separator}')"

  nil
end