summaryrefslogtreecommitdiff
path: root/lib/leap_cli/log.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/leap_cli/log.rb')
-rw-r--r--lib/leap_cli/log.rb271
1 files changed, 216 insertions, 55 deletions
diff --git a/lib/leap_cli/log.rb b/lib/leap_cli/log.rb
index 25f7b74..03789c3 100644
--- a/lib/leap_cli/log.rb
+++ b/lib/leap_cli/log.rb
@@ -21,7 +21,7 @@ module LeapCli
# thread safe logger
def new_logger
- logger.dup #LeapCli::LeapLogger.new
+ logger.dup
end
# deprecated
@@ -39,6 +39,12 @@ module LeapCli
#
FILE_TITLES = [:updated, :created, :removed, :missing, :nochange, :loading]
+ # TODO: use these
+ IMPORTANT = 0
+ INFO = 1
+ DEBUG = 2
+ TRACE = 3
+
attr_reader :log_output_stream, :log_file
attr_accessor :indent_level, :log_level, :log_in_color
@@ -69,17 +75,20 @@ module LeapCli
# * Integer: the log level (0, 1, 2)
# * Symbol: the prefix title to colorize. may be one of
# [:error, :warning, :info, :updated, :created, :removed, :no_change, :missing]
- # * Hash: a hash of options. so far, only :indent is supported.
+ # * Hash: a hash of options.
+ # :wrap -- if true, appy intend to each line in message.
+ # :color -- apply color to message or prefix
+ # :style -- apply style to message or prefix
#
def log(*args)
level = args.grep(Integer).first || 1
title = args.grep(Symbol).first
message = args.grep(String).first
options = args.grep(Hash).first || {}
+ host = options[:host]
unless message && @log_level >= level
return
end
- clear_prefix, colored_prefix = calculate_prefix(title, options)
#
# transform absolute path names
@@ -89,26 +98,50 @@ module LeapCli
end
#
- # log to the log file, always
+ # apply filters
+ #
+ if title
+ title, filter_flags = LogFilter.apply_title_filters(title.to_s)
+ else
+ message, filter_flags = LogFilter.apply_message_filters(message)
+ return if message.nil?
+ end
+ options = options.merge(filter_flags)
+
+ #
+ # set line prefix
#
- log_raw(:log, nil, clear_prefix) { message }
+ prefix = ""
+ prefix += "[" + options[:host] + "] " if options[:host]
+ prefix += title + " " if title
+
+ #
+ # write to the log file, always
+ #
+ log_raw(:log, nil, prefix) { message }
#
# log to stdout, maybe in color
#
if @log_in_color
- prefix = colored_prefix
if options[:wrap]
message = message.split("\n")
end
- if options[:color] && prefix.empty?
- message = colorize(message, options[:color], options[:style])
+ if options[:color]
+ if host
+ host = "[" + colorize(host, options[:color], options[:style]) + "] "
+ elsif title
+ title = colorize(title, options[:color], options[:style]) + " "
+ else
+ message = colorize(message, options[:color], options[:style])
+ end
+ elsif title
+ title = colorize(title, :cyan, :bold) + " "
end
- else
- prefix = clear_prefix
+ # new colorized prefix:
+ prefix = [host, title].compact.join(' ')
end
- indent = options[:indent]
- log_raw(:stdout, indent, prefix) { message }
+ log_raw(:stdout, options[:indent], prefix) { message }
#
# run block indented, if given
@@ -141,7 +174,7 @@ module LeapCli
if messages.any?
timestamp = Time.now.strftime("%b %d %H:%M:%S")
messages.each do |message|
- message = message.strip
+ message = message.rstrip
next if message.empty?
@log_output_stream.print("#{timestamp} #{prefix} #{message}\n")
end
@@ -161,7 +194,7 @@ module LeapCli
end
indent_str += prefix if prefix
messages.each do |message|
- message = message.strip
+ message = message.rstrip
next if message.empty?
STDOUT.print("#{indent_str}#{message}\n")
end
@@ -209,49 +242,177 @@ module LeapCli
:default => 49,
}
- def calculate_prefix(title, options)
- clear_prefix = colored_prefix = ""
- if title
- prefix_options = case title
- when :error then ['error', :red, :bold]
- when :fatal_error then ['fatal error:', :red, :bold]
- when :warning then ['warning:', :yellow, :bold]
- when :info then ['info', :cyan, :bold]
- when :note then ['NOTE:', :cyan, :bold]
- when :updated then ['updated', :cyan, :bold]
- when :updating then ['updating', :cyan, :bold]
- when :created then ['created', :green, :bold]
- when :removed then ['removed', :red, :bold]
- when :nochange then ['no change', :magenta]
- when :loading then ['loading', :magenta]
- when :missing then ['missing', :yellow, :bold]
- when :skipping then ['skipping', :yellow, :bold]
- when :run then ['run', :cyan, :bold]
- when :running then ['running', :cyan, :bold]
- when :failed then ['FAILED', :red, :bold]
- when :completed then ['completed', :green, :bold]
- when :ran then ['ran', :green, :bold]
- when :bail then ['bailing out', :red, :bold]
- when :invalid then ['invalid', :red, :bold]
- else [title.to_s, :cyan, :bold]
- end
- if options[:host]
- clear_prefix = "[%s] %s " % [options[:host], prefix_options[0]]
- colored_prefix = "[%s] %s " % [colorize(options[:host], prefix_options[1], prefix_options[2]), prefix_options[0]]
- else
- clear_prefix = "%s " % prefix_options[0]
- colored_prefix = "%s " % colorize(prefix_options[0], prefix_options[1], prefix_options[2])
- end
- elsif options[:host]
- clear_prefix = "[%s] " % options[:host]
- if options[:color]
- colored_prefix = "[%s] " % colorize(options[:host], options[:color])
- else
- colored_prefix = clear_prefix
+ end
+end
+
+#
+# A module to hide, modify, and colorize log entries.
+#
+
+module LeapCli
+ module LogFilter
+ #
+ # options for formatters:
+ #
+ # :match => regexp for matching a log line
+ # :color => what color the line should be
+ # :style => what style the line should be
+ # :priority => what order the formatters are applied in. higher numbers first.
+ # :match_level => only apply filter at the specified log level
+ # :level => make this line visible at this log level or higher
+ # :replace => replace the matched text
+ # :prepend => insert text at start of message
+ # :append => append text to end of message
+ # :exit => force the exit code to be this (does not interrupt program, just
+ # ensures a specific exit code when the program eventually exits)
+ #
+ FORMATTERS = [
+ # TRACE
+ { :match => /command finished/, :color => :white, :style => :dim, :match_level => 3, :priority => -10 },
+ { :match => /executing locally/, :color => :yellow, :match_level => 3, :priority => -20 },
+
+ # DEBUG
+ #{ :match => /executing .*/, :color => :green, :match_level => 2, :priority => -10, :timestamp => true },
+ #{ :match => /.*/, :color => :yellow, :match_level => 2, :priority => -30 },
+ { :match => /^transaction:/, :level => 3 },
+
+ # INFO
+ { :match => /.*out\] (fatal:|ERROR:).*/, :color => :red, :match_level => 1, :priority => -10 },
+ { :match => /Permission denied/, :color => :red, :match_level => 1, :priority => -20 },
+ { :match => /sh: .+: command not found/, :color => :magenta, :match_level => 1, :priority => -30 },
+
+ # IMPORTANT
+ { :match => /^(E|e)rr ::/, :color => :red, :match_level => 0, :priority => -10, :exit => 1},
+ { :match => /^ERROR:/, :color => :red, :priority => -10, :exit => 1},
+ #{ :match => /.*/, :color => :blue, :match_level => 0, :priority => -20 },
+
+ # CLEANUP
+ #{ :match => /\s+$/, :replace => '', :priority => 0},
+
+ # DEBIAN PACKAGES
+ { :match => /^(Hit|Ign) /, :color => :green, :priority => -20},
+ { :match => /^Err /, :color => :red, :priority => -20},
+ { :match => /^W(ARNING)?: /, :color => :yellow, :priority => -20},
+ { :match => /^E: /, :color => :red, :priority => -20},
+ { :match => /already the newest version/, :color => :green, :priority => -20},
+ { :match => /WARNING: The following packages cannot be authenticated!/, :color => :red, :level => 0, :priority => -10},
+
+ # PUPPET
+ { :match => /^(W|w)arning: Not collecting exported resources without storeconfigs/, :level => 2, :color => :yellow, :priority => -10},
+ { :match => /^(W|w)arning: Found multiple default providers for vcsrepo:/, :level => 2, :color => :yellow, :priority => -10},
+ { :match => /^(W|w)arning: .*is deprecated.*$/, :level => 2, :color => :yellow, :priority => -10},
+ { :match => /^(W|w)arning: Scope.*$/, :level => 2, :color => :yellow, :priority => -10},
+ #{ :match => /^(N|n)otice:/, :level => 1, :color => :cyan, :priority => -20},
+ #{ :match => /^(N|n)otice:.*executed successfully$/, :level => 2, :color => :cyan, :priority => -15},
+ { :match => /^(W|w)arning:/, :level => 0, :color => :yellow, :priority => -20},
+ { :match => /^Duplicate declaration:/, :level => 0, :color => :red, :priority => -20},
+ #{ :match => /Finished catalog run/, :level => 0, :color => :green, :priority => -10},
+ { :match => /^APPLY COMPLETE \(changes made\)/, :level => 0, :color => :green, :style => :bold, :priority => -10},
+ { :match => /^APPLY COMPLETE \(no changes\)/, :level => 0, :color => :green, :style => :bold, :priority => -10},
+
+ # PUPPET FATAL ERRORS
+ { :match => /^(E|e)rr(or|):/, :level => 0, :color => :red, :priority => -1, :exit => 1},
+ { :match => /^Wrapped exception:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
+ { :match => /^Failed to parse template/, :level => 0, :color => :red, :priority => -1, :exit => 1},
+ { :match => /^Execution of.*returned/, :level => 0, :color => :red, :priority => -1, :exit => 1},
+ { :match => /^Parameter matches failed:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
+ { :match => /^Syntax error/, :level => 0, :color => :red, :priority => -1, :exit => 1},
+ { :match => /^Cannot reassign variable/, :level => 0, :color => :red, :priority => -1, :exit => 1},
+ { :match => /^Could not find template/, :level => 0, :color => :red, :priority => -1, :exit => 1},
+ { :match => /^APPLY COMPLETE.*fail/, :level => 0, :color => :red, :style => :bold, :priority => -1, :exit => 1},
+
+ # TESTS
+ { :match => /^PASS: /, :color => :green, :priority => -20},
+ { :match => /^(FAIL|ERROR): /, :color => :red, :priority => -20},
+ { :match => /^(SKIP|WARN): /, :color => :yellow, :priority => -20},
+ { :match => /\d+ tests: \d+ passes, \d+ skips, 0 warnings, 0 failures, 0 errors/,
+ :color => :green, :style => :bold, :priority => -20 },
+ { :match => /\d+ tests: \d+ passes, \d+ skips, [1-9][0-9]* warnings, 0 failures, 0 errors/,
+ :color => :yellow, :style => :bold, :priority => -20 },
+ { :match => /\d+ tests: \d+ passes, \d+ skips, \d+ warnings, \d+ failures, [1-9][0-9]* errors/,
+ :color => :red, :style => :bold, :priority => -20 },
+ { :match => /\d+ tests: \d+ passes, \d+ skips, \d+ warnings, [1-9][0-9]* failures, \d+ errors/,
+ :color => :red, :style => :bold, :priority => -20 },
+
+ # LOG SUPPRESSION
+ { :match => /^(W|w)arning: You cannot collect without storeconfigs being set/, :level => 2, :priority => 10},
+ { :match => /^(W|w)arning: You cannot collect exported resources without storeconfigs being set/, :level => 2, :priority => 10}
+ ]
+
+ SORTED_FORMATTERS = FORMATTERS.sort_by { |i| -(i[:priority] || i[:prio] || 0) }
+
+ #
+ # same as normal formatters, but only applies to the title, not the message.
+ #
+ TITLE_FORMATTERS = [
+ # red
+ { :match => /error/, :color => :red, :style => :bold },
+ { :match => /fatal_error/, :replace => 'fatal error:', :color => :red, :style => :bold },
+ { :match => /removed/, :color => :red, :style => :bold },
+ { :match => /failed/, :replace => 'FAILED', :color => :red, :style => :bold },
+ { :match => /bail/, :replace => 'bailing out', :color => :red, :style => :bold },
+ { :match => /invalid/, :color => :red, :style => :bold },
+
+ # yellow
+ { :match => /warning/, :replace => 'warning:', :color => :yellow, :style => :bold },
+ { :match => /missing/, :color => :yellow, :style => :bold },
+ { :match => /skipping/, :color => :yellow, :style => :bold },
+
+ # green
+ { :match => /created/, :color => :green, :style => :bold },
+ { :match => /completed/, :color => :green, :style => :bold },
+ { :match => /ran/, :color => :green, :style => :bold },
+
+ # cyan
+ { :match => /note/, :replace => 'NOTE:', :color => :cyan, :style => :bold },
+
+ # magenta
+ { :match => /nochange/, :replace => 'no change', :color => :magenta },
+ { :match => /loading/, :color => :magenta },
+ ]
+
+ def self.apply_message_filters(message)
+ return self.apply_filters(SORTED_FORMATTERS, message)
+ end
+
+ def self.apply_title_filters(title)
+ return self.apply_filters(TITLE_FORMATTERS, title)
+ end
+
+ private
+
+ def self.apply_filters(formatters, message)
+ level = LeapCli.logger.log_level
+ result = {}
+ formatters.each do |formatter|
+ if (formatter[:match_level] == level || formatter[:match_level].nil?)
+ if message =~ formatter[:match]
+ # puts "applying formatter #{formatter.inspect}"
+ result[:level] = formatter[:level] if formatter[:level]
+ result[:color] = formatter[:color] if formatter[:color]
+ result[:style] = formatter[:style] || formatter[:attribute] # (support original cap colors)
+
+ message.gsub!(formatter[:match], formatter[:replace]) if formatter[:replace]
+ message.replace(formatter[:prepend] + message) unless formatter[:prepend].nil?
+ message.replace(message + formatter[:append]) unless formatter[:append].nil?
+ message.replace(Time.now.strftime('%Y-%m-%d %T') + ' ' + message) if formatter[:timestamp]
+
+ if formatter[:exit]
+ LeapCli::Util.exit_status(formatter[:exit])
+ end
+
+ # stop formatting, unless formatter was just for string replacement
+ break unless formatter[:replace]
+ end
end
end
- return [clear_prefix, colored_prefix]
+
+ if result[:color] == :hide
+ return [nil, {}]
+ else
+ return [message, result]
+ end
end
end
-end \ No newline at end of file
+end