From 334e37c6e3686d17d9d7d47c2cefd1bbfe78cf47 Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 29 Jun 2016 13:48:49 -0700 Subject: remove capistrano logger, new log filtering --- lib/leap_cli.rb | 2 - lib/leap_cli/log.rb | 271 +++++++++++++++++++++++++++++++++++++++---------- lib/leap_cli/logger.rb | 237 ------------------------------------------ 3 files changed, 216 insertions(+), 294 deletions(-) delete mode 100644 lib/leap_cli/logger.rb diff --git a/lib/leap_cli.rb b/lib/leap_cli.rb index b74f7e6..36718e3 100644 --- a/lib/leap_cli.rb +++ b/lib/leap_cli.rb @@ -11,7 +11,6 @@ $:.unshift(File.expand_path('../leap_cli/override',__FILE__)) # for a few gems, things will break if using earlier versions. # enforce the compatible versions here: require 'rubygems' -gem 'net-ssh', '~> 2.7' gem 'gli', '~> 2.12', '>= 2.12.0' require 'leap/platform' @@ -34,7 +33,6 @@ require 'leap_cli/path' require 'leap_cli/util' require 'leap_cli/util/secret' require 'leap_cli/util/x509' -require 'leap_cli/logger' require 'leap_cli/bootstrap' require 'leap_cli/config/object' 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 diff --git a/lib/leap_cli/logger.rb b/lib/leap_cli/logger.rb deleted file mode 100644 index 058322c..0000000 --- a/lib/leap_cli/logger.rb +++ /dev/null @@ -1,237 +0,0 @@ -# -# A drop in replacement for Capistrano::Logger that integrates better with LEAP CLI. -# - -require 'capistrano/logger' - -# -# from Capistrano::Logger -# ========================= -# -# IMPORTANT = 0 -# INFO = 1 -# DEBUG = 2 -# TRACE = 3 -# MAX_LEVEL = 3 -# COLORS = { -# :none => "0", -# :black => "30", -# :red => "31", -# :green => "32", -# :yellow => "33", -# :blue => "34", -# :magenta => "35", -# :cyan => "36", -# :white => "37" -# } -# STYLES = { -# :bright => 1, -# :dim => 2, -# :underscore => 4, -# :blink => 5, -# :reverse => 7, -# :hidden => 8 -# } -# - -module LeapCli - class Logger < Capistrano::Logger - - def initialize(options={}) - @options = options - @level = options[:level] || 0 - @message_buffer = nil - end - - def log(level, message, line_prefix=nil, options={}) - if message !~ /\n$/ && level <= 2 && line_prefix.is_a?(String) - # in some cases, when the message doesn't end with a return, we buffer it and - # wait until we encounter the return before we log the message out. - @message_buffer ||= "" - @message_buffer += message - return - elsif @message_buffer - message = @message_buffer + message - @message_buffer = nil - end - - options[:level] ||= level - [:stdout, :log].each do |mode| - LeapCli::log_raw(mode) do - message_lines(mode, message, line_prefix, options) - end - end - end - - private - - def message_lines(mode, message, line_prefix, options) - formatted_message, formatted_prefix, message_options = apply_formatting(mode, message, line_prefix, options) - if message_options[:level] <= self.level && formatted_message && formatted_message.chars.any? - if formatted_prefix - formatted_message.lines.collect { |line| - "[#{formatted_prefix}] #{line.sub(/\s+$/, '')}" - } - else - formatted_message.lines.collect {|line| line.sub(/\s+$/, '')} - end - else - nil - end - end - - ## - ## FORMATTING - ## - - # - # 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 - # :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, :priority => -10}, - { :match => /^APPLY COMPLETE \(no changes\)/, :level => 0, :color => :green, :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, :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 => :blue, :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} - ] - - def self.sorted_formatters - # Sort matchers in reverse order so we can break if we found a match. - @sorted_formatters ||= @formatters.sort_by { |i| -(i[:priority] || i[:prio] || 0) } - end - - @prefix_formatters = [ - { :match => /(err|out) :: /, :replace => '', :priority => 0}, - { :match => /\s+$/, :replace => '', :priority => 0} - ] - def self.prefix_formatters; @prefix_formatters; end - - def apply_formatting(mode, message, line_prefix = nil, options={}) - message = message.dup - options = options.dup - if !line_prefix.nil? - if !line_prefix.is_a?(String) - line_prefix = line_prefix.to_s.dup - else - line_prefix = line_prefix.dup - end - end - color = options[:color] || :none - style = options[:style] - - if line_prefix - self.class.prefix_formatters.each do |formatter| - if line_prefix =~ formatter[:match] && formatter[:replace] - line_prefix.gsub!(formatter[:match], formatter[:replace]) - end - end - end - - self.class.sorted_formatters.each do |formatter| - if (formatter[:match_level] == level || formatter[:match_level].nil?) - if message =~ formatter[:match] - options[:level] = formatter[:level] if formatter[:level] - color = formatter[:color] if formatter[:color] - 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 - - if color == :hide - return nil - elsif mode == :log || (color == :none && style.nil?) || !LeapCli.logger.log_in_color - return [message, line_prefix, options] - else - term_color = COLORS[color] - term_style = STYLES[style] - if line_prefix.nil? - message.replace format(message, term_color, term_style) - else - line_prefix.replace format(line_prefix, term_color, term_style).strip # format() appends a \n - end - return [message, line_prefix, options] - end - end - - end -end -- cgit v1.2.3