From: Christian Herdtweck Date: Thu, 10 Sep 2015 15:55:58 +0000 (+0200) Subject: added (yet untested) I2nLogger to log_helpers X-Git-Tag: v1.2~104 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=72fc3d56cbb4eb9123908afedf4e5c56e683e69d;p=pyi2ncommon added (yet untested) I2nLogger to log_helpers --- diff --git a/log_helpers.py b/log_helpers.py index 373e11f..0b925cc 100644 --- a/log_helpers.py +++ b/log_helpers.py @@ -21,19 +21,39 @@ ShortLevelFormatter: provide a 4-char-sized field "shortlevel" for message urgency (dbug/info/warn/err /crit) -Further ideas +I2nLogger: logger that provides a notice(), allows omission for str.format -* create logger sublcass with logger.notice() +Further ideas: :: * allow milliseconds in dateformat field (see test_short_level_format) * create own basicConfig-like function that uses our classes as default .. codeauthor:: Christian Herdtweck, christian.herdtweck@intra2net.com """ -from logging import Formatter, DEBUG, INFO, WARNING, ERROR, CRITICAL, NOTSET +import logging +from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL, NOTSET +#: log level half-way between INFO and WARNING +NOTICE = (INFO + WARNING)/2 +logging.addLevelName(NOTICE, 'NOTICE') -class ShortLevelFormatter(Formatter): +#: default formatting string for ShortLevelFormatter +DEFAULT_SHORT_LEVEL_FORMAT = '%(asctime)s:%(msecs)03d %(shortlevel)s| %(msg)s' + +#: default formatting string for date/time in ShortLevelFormatter +DEFAULT_SHORT_LEVEL_DATE_FORMAT = '%H:%M:%S' + +#: mapping from level name to level int for I2nLogger's set_level +LEVEL_DICT = dict(notset=NOTSET, debug=DEBUG, info=INFO, notice=NOTICE, + warning=WARNING, error=ERROR, critical=CRITICAL) + +#: min level allowed in I2nLogger +MIN_LEVEL = NOTSET + +#: max level allowed in I2nLogger +MAX_LEVEL = CRITICAL + +class ShortLevelFormatter(logging.Formatter): """ Formatter for logging handlers that allows use of format field "shortlevel" @@ -98,6 +118,95 @@ class ShortLevelFormatter(Formatter): self._shortlevel_dict[levelno] = shortlevel_str +class I2nLogger: + """ a more convenient logger + + Features:: + * can be used as follows:: + + logger.info('the result is {0}', result) # more convenient than... + logger.info('the result is {0}'.format(result)) # ... the original + + * Has a :py:meth:`Logger.notice` function + * Has by default as only handler a :py:class:`logging.StreamHandler` to + stdout with a :py:class:`ShortLevelFormatter` + * Simplifies setting of logger's logging level using + :py:meth:`Logger.set_level`. The level is kept in sync between logger and + handlers and can be queried using :py:meth:`get_level` + * can specify name and level and [date]fmt all in constructor + + ..note:: Creating multiple instances with the same name is possible but + will internally use the same logger. Each instance overwrites at + construction time the previous instances' handlers and levels! + """ + + def __init__(self, name, level=INFO, fmt=DEFAULT_SHORT_LEVEL_FORMAT, + datefmt=DEFAULT_SHORT_LEVEL_DATE_FORMAT) + """ creates a I2nLogger; forwards args to logging.getLogger + + ..note:: You should use a different name in each constructor call! + """ + self._level = min(MAX_LEVEL, max(MIN_LEVEL, level)) + self._log = logging.getLogger(name) + self._log.setLevel(self.level) + + # remove handlers (sometimes there are mutliple by default) + for handler in self._log.handlers: + self._log.removeHandler(handler) + + # create new handler and formatter + formatter = ShortLevelFormatter(fmt=fmt, datefmt=datefmt) + formatter.add_level(notice, 'note') + stdout_handler = logging.StreamHandler(formatter) + stdout_handler.setLevel(self.level) + self._log.addHandler(stdout_handler) + + def debug(self, message, *args, **kwargs): + self.log(DEBUG, message, *args, **kwargs) + + def info(self, message, *args, **kwargs): + self.log(INFO, message, *args, **kwargs) + + def notice(self, message, *args, **kwargs): + self.log(NOTICE, message, *args, **kwargs) + + def warning(self, message, *args, **kwargs): + self.log(WARNING, message, *args, **kwargs) + + def error(self, message, *args, **kwargs): + self.log(ERROR, message, *args, **kwargs) + + def critical(self, message, *args, **kwargs): + self.log(CRITICAL, message, *args, **kwargs) + + def log(self, level, message, *args, **kwargs): + if level >= self._level: + self._log.log(level, message.format(*args), **kwargs) + + def get_level(self): + """ return int level of this logger """ + return self._level + + def get_level_str(self): + """ returns :py:func:`logging.getLevelName` on :py:meth:`get_level` """ + return logging.getLevelName(self._level) + + def set_level(self, new_level): + """ set level given an int or a str + + :arg new_level: int or str (str is converted to lower case) + :raises: KeyError if new_level is a string that is not in + :py:data:`LEVEL_DICT` + """ + if isstr(new_level): + self._level = LEVEL_DICT[new_level.lower()] + else: + self._level = min(MAX_LEVEL, max(MIN_LEVEL, level)) + self._log.setLevel(self._level) + for handler in self._log.handlers: + handler.setLevel(self._level) + + def test_short_level_format(): """ quick test of :py:class:`ShortLevelFormatter` """