public inbox for gentoo-catalyst@lists.gentoo.org
 help / color / mirror / Atom feed
From: Brian Dolbec <dolsen@gentoo.org>
To: gentoo-catalyst@lists.gentoo.org
Subject: Re: [gentoo-catalyst] [PATCH 2/3] log: new logging module to standardize catalyst output
Date: Fri, 9 Oct 2015 09:30:36 -0700	[thread overview]
Message-ID: <20151009093036.75026bc4.dolsen@gentoo.org> (raw)
In-Reply-To: <1444370248-13159-2-git-send-email-vapier@gentoo.org>

On Fri,  9 Oct 2015 01:57:27 -0400
Mike Frysinger <vapier@gentoo.org> wrote:

> This has everything you could ever want:
> - control where output is sent (stdout or a file)
> - control over log level
> - automatic exit when CRITICAL is used
> - automatic colorization of critical/error/warning messages
> - explicit control over use of colors
> - automatic handling of embedded newlines
> - standardized output format
> - all logging routed through a single logger (not the root)
> - additional log level between warning & info: notice
> 
> This just lays the groundwork -- no code is actually converted over
> to using this.  That will be done in follow up commit(s).
> 
> Note: The default output level has changed from "info" to "notice".
> That means the default display won't spam w/irrelevant details.
> 
> Example output (only the main.py module is converted):
> $ ./bin/wrapper.py -s test
> 09 Oct 2015 01:34:56 EDT: NOTICE  : Using default Catalyst
> configuration file: /etc/catalyst/catalyst.conf ContentsMap:
> __init__(), search_order = ['pixz', 'lbzip2', 'isoinfo_l',
> 'squashfs', 'gzip', 'xz', 'bzip2', 'tar', 'isoinfo_f'] Creating
> Portage tree snapshot test from /usr/portage... ^C
> 
> $ ./bin/wrapper.py -s test --debug
> 09 Oct 2015 01:35:43 EDT: INFO    : main.py:version: Catalyst git
> 09 Oct 2015 01:35:43 EDT: INFO    : main.py:version: vcs version
> 4440e8908c677d8764e29b0f127e2dd6c02b7621, date Fri Oct 9 01:28:19
> 2015 -0400 09 Oct 2015 01:35:43 EDT: INFO    : main.py:version:
> Copyright 2003-2015 Gentoo Foundation 09 Oct 2015 01:35:43 EDT:
> INFO    : main.py:version: Copyright 2008-2012 various authors 09 Oct
> 2015 01:35:43 EDT: INFO    : main.py:version: Distributed under the
> GNU General Public License version 2.1 09 Oct 2015 01:35:43 EDT:
> NOTICE  : log.py:notice: Using default Catalyst configuration
> file: /etc/catalyst/catalyst.conf 09 Oct 2015 01:35:43 EDT: INFO    :
> main.py:parse_config: Snapshot cache support enabled. 09 Oct 2015
> 01:35:43 EDT: INFO    : main.py:parse_config: Kernel cache support
> enabled. 09 Oct 2015 01:35:43 EDT: INFO    : main.py:parse_config:
> Autoresuming support enabled. 09 Oct 2015 01:35:43 EDT: INFO    :
> main.py:parse_config: Package cache support enabled. 09 Oct 2015
> 01:35:43 EDT: INFO    : main.py:parse_config: Seed cache support
> enabled. 09 Oct 2015 01:35:43 EDT: INFO    : main.py:parse_config:
> Envscript support enabled. 09 Oct 2015 01:35:43 EDT: DEBUG   :
> main.py:main: conf_values[options] = set(['snapcache', 'kerncache',
> 'autoresume', 'pkgcache', 'bindist', 'seedcache']) ^C
> 
> $ ./bin/wrapper.py -s test -C /asdf
> 09 Oct 2015 01:36:59 EDT: NOTICE  : Using default Catalyst
> configuration file: /etc/catalyst/catalyst.conf ContentsMap:
> __init__(), search_order = ['pixz', 'lbzip2', 'isoinfo_l',
> 'squashfs', 'gzip', 'xz', 'bzip2', 'tar', 'isoinfo_f']
> 
> !!! catalyst: Syntax error: 0
> 
> 09 Oct 2015 01:36:59 EDT: CRITICAL: Could not parse commandline
> ---
>  catalyst/log.py  | 130
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> catalyst/main.py |  30 ++++++++++++- 2 files changed, 158
> insertions(+), 2 deletions(-) create mode 100644 catalyst/log.py
> 
> diff --git a/catalyst/log.py b/catalyst/log.py
> new file mode 100644
> index 0000000..bf39116
> --- /dev/null
> +++ b/catalyst/log.py
> @@ -0,0 +1,130 @@
> +# Copyright 2003-2015 Gentoo Foundation
> +# Distributed under the terms of the GNU General Public License v2
> +
> +"""Logging related code
> +
> +This largely exposes the same interface as the logging module except
> we add +another level "notice" between warning & info, and all output
> goes through +the "catalyst" logger.
> +"""
> +
> +from __future__ import print_function
> +
> +import logging
> +import logging.handlers
> +import os
> +import sys
> +import time
> +
> +
> +class CatalystLogger(logging.Logger):
> +	"""Override the _log member to autosplit on new lines"""
> +
> +	def _log(self, level, msg, args, **kwargs):
> +		"""If given a multiline message, split it"""
> +		# We have to interpolate it first in case they
> spread things out
> +		# over multiple lines like: Bad Thing:\n%s\nGoodbye!
> +		msg %= args
> +		for line in msg.splitlines():
> +			super(CatalystLogger, self)._log(level,
> line, (), **kwargs) +
> +
> +# The logger that all output should go through.
> +# This is ugly because we want to not perturb the logging module
> state. +_klass = logging.getLoggerClass()
> +logging.setLoggerClass(CatalystLogger)
> +logger = logging.getLogger('catalyst')
> +logging.setLoggerClass(_klass)
> +del _klass
> +
> +
> +# Set the notice level between warning and info.
> +NOTICE = (logging.WARNING + logging.INFO) / 2
> +logging.addLevelName(NOTICE, 'NOTICE')
> +
> +
> +# The API we expose to consumers.
> +def notice(msg, *args, **kwargs):
> +	"""Log a notice message"""
> +	logger.log(NOTICE, msg, *args, **kwargs)
> +
> +def critical(msg, *args, **kwargs):
> +	"""Log a critical message and then exit"""
> +	status = kwargs.pop('status', 1)
> +	logger.critical(msg, *args, **kwargs)
> +	sys.exit(status)
> +
> +error = logger.error
> +warning = logger.warning
> +info = logger.info
> +debug = logger.debug
> +
> +
> +class CatalystFormatter(logging.Formatter):
> +	"""Mark bad messages with colors automatically"""
> +
> +	_COLORS = {
> +		'CRITICAL':	'\033[1;35m',
> +		'ERROR':	'\033[1;31m',
> +		'WARNING':	'\033[1;33m',
> +		'DEBUG':	'\033[1;34m',
> +	}
> +	_NORMAL = '\033[0m'
> +
> +	@staticmethod
> +	def detect_color():
> +		"""Figure out whether the runtime env wants color"""
> +		if 'NOCOLOR' is os.environ:
> +			return False
> +		return os.isatty(sys.stdout.fileno())
> +
> +	def __init__(self, *args, **kwargs):
> +		"""Initialize"""
> +		color = kwargs.pop('color', None)
> +		if color is None:
> +			color = self.detect_color()
> +		if not color:
> +			self._COLORS = {}
> +
> +		super(CatalystFormatter, self).__init__(*args,
> **kwargs) +
> +	def format(self, record, **kwargs):
> +		"""Format the |record| with our color settings"""
> +		#print(dir(record))
> +		#print(record.getMessage())
> +		msg = super(CatalystFormatter, self).format(record,
> **kwargs)
> +		#print('{', msg, '}')
> +		color = self._COLORS.get(record.levelname)
> +		if color:
> +			return color + msg + self._NORMAL
> +		else:
> +			return msg
> +
> +
> +def setup_logging(level, output=None, debug=False, color=None):
> +	"""Initialize the logging module using the |level| level"""
> +	# The incoming level will be things like "info", but
> setLevel wants
> +	# the numeric constant.  Convert it here.
> +	level = logging.getLevelName(level.upper())
> +
> +	# The good stuff.
> +	fmt = '%(asctime)s: %(levelname)-8s: '
> +	if debug:
> +		fmt += '%(filename)s:%(funcName)s: '
> +	fmt += '%(message)s'
> +
> +	# Figure out where to send the log output.
> +	if output is None:
> +		handler = logging.StreamHandler(stream=sys.stdout)
> +	else:
> +		handler = logging.FileHandler(output)
> +
> +	# Use a date format that is readable by humans & machines.
> +	# Think e-mail/RFC 2822: 05 Oct 2013 18:58:50 EST
> +	tzname = time.strftime('%Z', time.localtime())
> +	datefmt = '%d %b %Y %H:%M:%S ' + tzname
> +	formatter = CatalystFormatter(fmt, datefmt, color=color)
> +	handler.setFormatter(formatter)
> +
> +	logger.addHandler(handler)
> +	logger.setLevel(level)
> diff --git a/catalyst/main.py b/catalyst/main.py
> index e6b6447..c9a2219 100644
> --- a/catalyst/main.py
> +++ b/catalyst/main.py
> @@ -18,6 +18,7 @@ from DeComp.definitions import
> (COMPRESS_DEFINITIONS, DECOMPRESS_DEFINITIONS, CONTENTS_DEFINITIONS)
>  from DeComp.contents import ContentsMap
>  
> +from catalyst import log
>  import catalyst.config
>  import catalyst.util
>  from catalyst.defaults import confdefaults, option_messages
> @@ -176,10 +177,23 @@ $ catalyst -f stage1-specfile.spec"""
>  	group = parser.add_argument_group('Program output options')
>  	group.add_argument('-d', '--debug',
>  		default=False, action='store_true',
> -		help='enable debugging')
> +		help='enable debugging (and default --log-level
> debug)') group.add_argument('-v', '--verbose',
>  		default=False, action='store_true',
> -		help='verbose output')
> +		help='verbose output (and default --log-level info)')
> +	group.add_argument('--log-level',
> +		default=None,
> +		choices=('critical', 'error', 'warning', 'notice',
> 'info', 'debug'),
> +		help='set verbosity of output')
> +	group.add_argument('--log-file',
> +		type=FilePath(exists=False),
> +		help='write all output to this file (instead of
> stdout)')
> +	group.add_argument('--color',
> +		default=None, action='store_true',
> +		help='colorize output all the time (default:
> detect)')
> +	group.add_argument('--nocolor',
> +		dest='color', action='store_false',
> +		help='never colorize output all the time (default:
> detect)') 
>  	group = parser.add_argument_group('Temporary file
> management') group.add_argument('-a', '--clear-autoresume',
> @@ -218,6 +232,18 @@ def main():
>  	parser = get_parser()
>  	opts = parser.parse_args(sys.argv[1:])
>  
> +	# Initialize the logger before anything else.
> +	log_level = opts.log_level
> +	if log_level is None:
> +		if opts.debug:
> +			log_level = 'debug'
> +		elif opts.verbose:
> +			log_level = 'info'
> +		else:
> +			log_level = 'notice'
> +	log.setup_logging(log_level, output=opts.log_file,
> debug=opts.debug,
> +		color=opts.color)
> +
>  	# Parse the command line options.
>  	myconfig = opts.config
>  	myspecfile = opts.file

Very nice. :)   I learned a bit in here ;)

In gkeys I had it log to a file always, but didn't add color to the
console output.

-- 
Brian Dolbec <dolsen>



  reply	other threads:[~2015-10-09 16:31 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-10-09  5:57 [gentoo-catalyst] [PATCH 1/3] main: group related command line flags Mike Frysinger
2015-10-09  5:57 ` [gentoo-catalyst] [PATCH 2/3] log: new logging module to standardize catalyst output Mike Frysinger
2015-10-09 16:30   ` Brian Dolbec [this message]
2015-10-09  5:57 ` [gentoo-catalyst] [PATCH 3/3] main: convert to new logging module Mike Frysinger
2015-10-09 16:32   ` Brian Dolbec
2015-10-09 16:19 ` [gentoo-catalyst] [PATCH 1/3] main: group related command line flags Brian Dolbec

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20151009093036.75026bc4.dolsen@gentoo.org \
    --to=dolsen@gentoo.org \
    --cc=gentoo-catalyst@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox