class Beaker::Logger

The Beaker Logger class This class handles message reporting for Beaker, it reports based upon a provided log level to a given destination (be it a string or file)

Constants

BLACK
BLUE
BRIGHT_BLUE
BRIGHT_CYAN
BRIGHT_GREEN
BRIGHT_MAGENTA
BRIGHT_NORMAL
BRIGHT_RED
BRIGHT_WHITE
BRIGHT_YELLOW
CYAN
GREEN
GREY
LOG_LEVELS

The defined log levels. Each log level also reports messages at levels lower than itself

MAGENTA
NONE
NORMAL
RED
WHITE
YELLOW

Attributes

color[RW]
destinations[RW]
last_result[RW]

The results of the most recently run command

line_prefix[RW]

Determines the spacing that happens before an output line

log_colors[RW]
log_level[RW]

Public Class Methods

generate_dated_log_folder(base_dir, log_prefix, timestamp) click to toggle source

Utility method to centralize dated log folder generation

@param [String] base_dir Path of the directory for the dated log folder to live in @param [String] log_prefix Prefix to use for the log files @param [Time] timestamp Timestamp that should be used to generate the dated log folder

@example base_dir = ‘junit’, log_prefix = ‘pants’, timestamp = ‘2015-03-04 10:35:37 -0800’

returns 'junit/pants/2015-03-04_10_35_37'

@note since this uses ‘mkdir -p’, log_prefix can be a number of nested directories

@return [String] the path of the dated log folder generated

# File lib/beaker/logger.rb, line 393
def self.generate_dated_log_folder(base_dir, log_prefix, timestamp)
  log_dir = File.join(base_dir, log_prefix, timestamp.strftime("%F_%H_%M_%S"))
  FileUtils.mkdir_p(log_dir) unless File.directory?(log_dir)
  log_dir
end
new(*args) click to toggle source

Initialization of the Logger class @overload initialize(dests)

Initialize a Logger object that reports to the provided destinations, use default options
@param [Array<String, IO>] Array of IO and strings (assumed to be file paths) to be reported to

@overload initialize(dests, options)

Initialize a Logger object that reports to the provided destinations, use options from provided option hash
@param [Array<String, IO>] Array of IO and strings (assumed to be file paths) to be reported to
@param [Hash] options Hash of options
@option options [Boolean] :color (true) Print color code before log messages
@option options [Boolean] :quiet (false) Do not log messages to STDOUT
@option options [String] :log_level ("info") Log level (one of "debug" - highest level, "verbose", "info",
                       "notify" and "warn" - lowest level (see {LOG_LEVELS}))  The log level indicates that messages at that
                       log_level and lower will be reported.
# File lib/beaker/logger.rb, line 58
def initialize(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  @color = options[:color]
  @sublog = nil
  @log_level = case options[:log_level]
               when /trace/i, :trace
                 :trace
               when /debug/i, :debug
                 :debug
               when /info/i, :info
                 :info
               when /notify/i, :notify
                 :notify
               when /warn/i, :warn
                 :warn
               else
                 :verbose
               end

  @last_result = nil
  @line_prefix = ''

  @destinations = []

  @log_colors = {
    :error => RED,
    :warn => BRIGHT_RED,
    :success => MAGENTA,
    :notify => BLUE,
    :info => GREEN,
    :debug => WHITE,
    :trace => BRIGHT_YELLOW,
    :perf => BRIGHT_MAGENTA,
    :host => YELLOW,
  }

  @log_colors.merge!(options[:log_colors]) if options[:log_colors]

  # if a user overrides any of the log_colors, we will no longer
  # override the colors at all on a CI build. This is b/c it is
  # assumed that if a user is overriding the colors, they know
  # what they are doing. We could potentially add an additional
  # option a user can pass to be explicit about still allowing
  # the override.
  unless options[:log_colors]
    # Jenkins exposed variable - should be present on the slave directing
    # the beaker run
    ci_build = ENV['BUILD_NUMBER'] != nil

    @log_colors[:notify] = NORMAL if ci_build
    @log_colors[:info] = NORMAL if ci_build
  end

  dests = args
  dests << STDOUT unless options[:quiet]
  dests.uniq!
  dests.each { |dest| add_destination(dest) }
end
strip_color_codes(text) click to toggle source

Remove color codes from provided string. Color codes are of the format /(e[dd;ddm)+/. @param [String] text The string to remove color codes from @return [String] The text without color codes

# File lib/beaker/logger.rb, line 402
def self.strip_color_codes(text)
  text.gsub(/(\e|\^\[)\[(\d*;)*\d*m/, '')
end

Public Instance Methods

add_destination(dest) click to toggle source

Construct an array of open steams for printing log messages to @param [Array<IO, String>] dest Array of strings (each used as a file path) and IO steams that messages will be printed to

# File lib/beaker/logger.rb, line 130
def add_destination(dest)
  case dest
  when IO, StringIO
    @destinations << dest
  when String
    @destinations << File.open(dest, 'w')
  else
    raise "Unsuitable log destination #{dest.inspect}"
  end
end
color_host_output(*args) click to toggle source

Custom reporting for messages generated by host SUTs - to preserve output Will not print unless we are at {LOG_LEVELS} ‘verbose’ or higher. Preserves outout by not stripping out colour codes @param args Strings to be reported

# File lib/beaker/logger.rb, line 253
def color_host_output *args
  return unless is_verbose?

  string = args.join
  optionally_color NONE, string, false
end
convert(string) click to toggle source

Remove invalid UTF-8 codes from provided string(s) @param [String, Array<String>] string The string(s) to remove invalid codes from

# File lib/beaker/logger.rb, line 192
def convert string
  if string.is_a?(Array)
    string.map do |s|
      convert s
    end
  else
    # Remove invalid and undefined UTF-8 character encodings
    string = string.to_s.dup.force_encoding('UTF-8')
    return string.to_s.chars.select { |i| i.valid_encoding? }.join
  end
end
debug(*args) click to toggle source

Report a debug message. Will not print unless we are at {LOG_LEVELS} ‘debug’ or higher. @param args Strings to be reported

# File lib/beaker/logger.rb, line 281
def debug *args
  return unless is_verbose?

  optionally_color @log_colors[:debug], *args
end
error(*args) click to toggle source

Report an error message. Will always be reported. @param args Strings to be reported

# File lib/beaker/logger.rb, line 326
def error *args
  optionally_color @log_colors[:error], *args
end
get_sublog() click to toggle source

Return the contents of the sublog

# File lib/beaker/logger.rb, line 376
def get_sublog
  @sublog.rewind
  @sublog.read
end
host_output(*args) click to toggle source

Custom reporting for messages generated by host SUTs. Will not print unless we are at {LOG_LEVELS} ‘verbose’ or higher. Strips any color codes already in the provided messages, then adds logger color codes before reporting @param args Strings to be reported

# File lib/beaker/logger.rb, line 241
def host_output *args
  return unless is_verbose?

  strings = strip_colors_from args
  string = strings.join
  optionally_color @log_colors[:host], string, false
end
info(*args) click to toggle source

Report an info message. Will not print unless we are at {LOG_LEVELS} ‘info’ or higher. @param args Strings to be reported

# File lib/beaker/logger.rb, line 301
def info *args
  return unless is_info?

  optionally_color @log_colors[:info], *args
end
is_debug?() click to toggle source

Are we at {LOG_LEVELS} debug? @return [Boolean] true if ‘debug’ or higher, false if not ‘debug’ {LOG_LEVELS} or lower

# File lib/beaker/logger.rb, line 162
def is_debug?
  LOG_LEVELS[@log_level] >= LOG_LEVELS[:debug]
end
is_info?() click to toggle source

Are we at {LOG_LEVELS} info? @return [Boolean] true if ‘info’ or higher, false if not ‘info’ {LOG_LEVELS} or lower

# File lib/beaker/logger.rb, line 180
def is_info?
  LOG_LEVELS[@log_level] >= LOG_LEVELS[:info]
end
is_notify?() click to toggle source

Are we at {LOG_LEVELS} notify? @return [Boolean] true if ‘notify’ or higher, false if not ‘notify’ {LOG_LEVELS} or lower

# File lib/beaker/logger.rb, line 186
def is_notify?
  LOG_LEVELS[@log_level] >= LOG_LEVELS[:notify]
end
is_trace?() click to toggle source

Are we at {LOG_LEVELS} trace? @return [Boolean] true if ‘trace’ or higher, false if not ‘trace’ {LOG_LEVELS} or lower

# File lib/beaker/logger.rb, line 156
def is_trace?
  LOG_LEVELS[@log_level] >= LOG_LEVELS[:trace]
end
is_verbose?() click to toggle source

Are we at {LOG_LEVELS} verbose? @return [Boolean] true if ‘verbose’ or higher, false if not ‘verbose’ {LOG_LEVELS} or lower

# File lib/beaker/logger.rb, line 168
def is_verbose?
  LOG_LEVELS[@log_level] >= LOG_LEVELS[:verbose]
end
is_warn?() click to toggle source

Are we at {LOG_LEVELS} warn? @return [Boolean] true if ‘warn’ or higher, false if not ‘warn’ {LOG_LEVELS} or lower

# File lib/beaker/logger.rb, line 174
def is_warn?
  LOG_LEVELS[@log_level] >= LOG_LEVELS[:warn]
end
notify(*args) click to toggle source

Report a notify message. Will not print unless we are at {LOG_LEVELS} ‘notify’ or higher. @param args Strings to be reported

# File lib/beaker/logger.rb, line 317
def notify *args
  return unless is_notify?

  optionally_color @log_colors[:notify], *args
end
optionally_color(color_code, msg, add_newline = true) click to toggle source

Print the provided message to the set destination streams, using color codes if appropriate @param [String] color_code The color code to pre-pend to the message @param [String] msg The message to be reported @param [Boolean] add_newline (true) Add newlines between the color codes and the message

# File lib/beaker/logger.rb, line 343
def optionally_color color_code, msg, add_newline = true
  print_statement = add_newline ? :puts : :print
  msg = convert(msg)
  msg = prefix_log_line(msg)
  @destinations.each do |to|
    to.print color_code if @color
    to.send print_statement, msg
    unless color_code == NONE
      to.print NORMAL if @color
    end
    to.flush
  end
end
perf_output(*args) click to toggle source

Custom reporting for performance/sysstat messages Will not print unless we are at {LOG_LEVELS} ‘debug’ or higher. @param args Strings to be reported

# File lib/beaker/logger.rb, line 263
def perf_output *args
  return unless is_debug?

  optionally_color @log_colors[:perf], *args
end
prefix_log_line(line) click to toggle source

Prefixes a log line with the appropriate amount of whitespace for the level of test that’s running.

@param [String] line the line to prefix

@return [String] the prefixed line

# File lib/beaker/logger.rb, line 210
def prefix_log_line line
  if line.is_a?(Array)
    line.map do |s|
      prefix_log_line s
    end
  else
    line.delete!("\r")
    has_ending_newline = line.end_with?("\n")
    actual_lines = line.split("\n")
    actual_lines.map! do |actual_line|
      @line_prefix + actual_line
    end
    new_line = actual_lines.join("\n")
    new_line << "\n" if has_ending_newline
    new_line
  end
end
pretty_backtrace(backtrace = caller(1)) click to toggle source

Utility method to get the current call stack and format it to a human-readable string (which some IDEs/editors will recognize as links to the line numbers in the trace). Beaker associated files will be purged from backtrace unless log level is ‘debug’ or higher @param [String] backtrace (caller(1)) The backtrace to format @return [String] The formatted backtrace

# File lib/beaker/logger.rb, line 363
def pretty_backtrace backtrace = caller(1)
  trace = is_debug? ? backtrace : purge_harness_files_from(backtrace)
  expand_symlinks(trace).join "\n"
end
quiet(off = true) click to toggle source

Turn on/off STDOUT logging @param [Boolean] off If true, disable STDOUT logging, if false enable STDOUT logging

# File lib/beaker/logger.rb, line 119
def quiet(off = true)
  if off
    remove_destination(STDOUT) # turn off the noise!
  else
    remove_destination(STDOUT) # in case we are calling this in error and we are already noisy
    add_destination(STDOUT)
  end
end
remove_destination(dest) click to toggle source

Remove a steam from the destinations array based upon it’s name or file path @param [String, IO] dest String representing a file path or IO stream

# File lib/beaker/logger.rb, line 143
def remove_destination(dest)
  case dest
  when IO, StringIO
    @destinations.delete(dest)
  when String
    @destinations.delete_if { |d| d.respond_to?(:path) and d.path == dest }
  else
    raise "Unsuitable log destination #{dest.inspect}"
  end
end
start_sublog() click to toggle source

Create a new StringIO log to track the current output

# File lib/beaker/logger.rb, line 369
def start_sublog
  remove_destination(@sublog) if @sublog
  @sublog = StringIO.new
  add_destination(@sublog)
end
strip_colors_from(lines) click to toggle source

Strip any color codes from provided string(s) @param [String] lines A single or array of lines to removed color codes from @return [Array<String>] An array of strings that do not have color codes

# File lib/beaker/logger.rb, line 333
def strip_colors_from lines
  Array(lines).map do |line|
    Logger.strip_color_codes(convert(line))
  end
end
success(*args) click to toggle source

Report a success message. Will always be reported. @param args Strings to be reported

# File lib/beaker/logger.rb, line 310
def success *args
  optionally_color @log_colors[:success], *args
end
trace(*args) click to toggle source

Report a trace message. Will not print unless we are at {LOG_LEVELS} ‘trace’ or higher. @param args Strings to be reported

# File lib/beaker/logger.rb, line 272
def trace *args
  return unless is_trace?

  optionally_color @log_colors[:trace], *args
end
warn(*args) click to toggle source

Report a warning message. Will not print unless we are at {LOG_LEVELS} ‘warn’ or higher. Will pre-pend the message with “Warning: ”. @param args Strings to be reported

# File lib/beaker/logger.rb, line 291
def warn *args
  return unless is_warn?

  strings = args.map { |msg| "Warning: #{msg}" }
  optionally_color @log_colors[:warn], strings
end
with_indent() { || ... } click to toggle source

Indent the step level for the duration of block.

# File lib/beaker/logger.rb, line 229
def with_indent
  old_line_prefix = self.line_prefix.dup
  self.line_prefix << '  '
  yield
ensure
  self.line_prefix = old_line_prefix
end

Private Instance Methods

purge_harness_files_from(backtrace) click to toggle source

Remove Beaker associated lines from a given String @param [String] backtrace The string to remove Beaker associated lines from @return [String] The cleaned backtrace

# File lib/beaker/logger.rb, line 423
def purge_harness_files_from backtrace
  mostly_purged = backtrace.reject do |line|
    # LOADED_FEATURES is an array of anything `require`d, i.e. everything
    # but the test in question
    $LOADED_FEATURES.any? do |require_path|
      line.include? require_path
    end
  end

  # And remove lines that contain our program name in them
  mostly_purged.reject { |line| line.include? $0 }
end