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
The results of the most recently run command
Determines the spacing that happens before an output line
Public Class Methods
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
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
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
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
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
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
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
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
Return the contents of the sublog
# File lib/beaker/logger.rb, line 376 def get_sublog @sublog.rewind @sublog.read end
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
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
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
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
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
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
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
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
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
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
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
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
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
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 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
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 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
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
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
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
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
Utility method that takes a path as input, checks each component of the path to see if it is a symlink, and expands it if it is. @param [String] file_path The path to be examined @return [String] The fully expanded file_path
# File lib/beaker/logger.rb, line 441 def expand_symlink file_path file_path.split("/").inject do |full_path, next_dir| next_path = full_path + "/" + next_dir if File.symlink? next_path link = File.readlink next_path next_path = case link when /^\// then link else File.expand_path(full_path + "/" + link) end end next_path end end
Expand each symlink found to its full path Lines are assumed to be in the format “String : Integer” @param [String] backtrace The string to search and expand symlinks in @return [String] The backtrace with symlinks expanded
# File lib/beaker/logger.rb, line 412 def expand_symlinks backtrace backtrace.collect do |line| file_path, line_num = line.split(":") expanded_path = expand_symlink File.expand_path(file_path) expanded_path.to_s + ":" + line_num.to_s end end
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