created log_helpers with 4-char-level formatter ShortLevelFormatter
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Thu, 27 Aug 2015 09:04:02 +0000 (11:04 +0200)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Thu, 27 Aug 2015 09:04:02 +0000 (11:04 +0200)
log_helpers.py [new file with mode: 0644]

diff --git a/log_helpers.py b/log_helpers.py
new file mode 100644 (file)
index 0000000..798301c
--- /dev/null
@@ -0,0 +1,141 @@
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+
+""" Helpers for logging
+
+ShortLevelFormatter: provide a 4-char-sized field "levl" for messge urgency
+(dbug/info/warn/err /crit)
+
+Further ideas
+
+* create logger sublcass with logger.notice()
+* 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
+
+
+class ShortLevelFormatter(Formatter):
+    """
+    Formatter for logging handlers that allows use of log format field "levl"
+
+    using this formatter, you can specify in the log message format string the
+    field "levl" which will introduce in your log messages a 4-char field for
+    the log record urgency: "DEBUG" --> "dbug", "INFO" --> "info",
+    "WARNING" --> "warn", "ERROR" --> "err ", "CRITICAL" --> "crit"
+
+    All other functionality (like other format fields) is inherited from base
+    class Formatter
+
+    Usage example::
+
+        logger = logging.getLogger(__name__)
+        logger.setLevel(logging.DEBUG)
+        handler = logging.StreamHandler()
+        handler.setLevel(logging.DEBUG)
+        formatter = ShortLevelFormatter('%(levl)s| %(msg)s')
+        handler.setFormatter(formatter)
+        logger.addHandler(handler)
+
+    You can even add new levels::
+
+        notice = (logging.INFO + logging.WARNING)/2
+        formatter.add_level(notice, 'note')
+        logger.log(notice, 'more important than info but no warning nor error')
+
+    .. seealso:: testing funcion :py:func:`test_short_level_format`
+    """
+
+    def __init__(self, *args, **kwargs):
+        """ creates a new ShortLevelFormatter
+
+        forwards all args to super class Formatter and initializes dict with
+        levelno and short string representations
+        """
+        self.supr = super(ShortLevelFormatter, self)
+        self.supr.__init__(*args, **kwargs)
+        self._levl_dict = {DEBUG: 'dbug', INFO: 'info', WARNING: 'warn',
+                           ERROR: 'err ', CRITICAL: 'crit', NOTSET: '----'}
+
+    def format(self, record):
+        """ create string representation of given log record """
+        try:
+            record.levl = self._levl_dict[record.levelno]
+        except KeyError:
+            record.levl = '????'
+
+        return self.supr.format(record)
+
+    def add_level(self, levelno, levl_str):
+        """ add a new message urgency level
+
+        :param int levelno: numeric urgency level
+        :param str levl_str: string representation of message urgency; should
+                             be of length 4
+        :returns: nothing
+        """
+
+        self._levl_dict[levelno] = levl_str
+
+
+def test_short_level_format():
+    """ quick test of :py:class:`ShortLevelFormatter` """
+
+    import logging
+    logger = logging.getLogger('logtest')
+    logger.setLevel(DEBUG)
+    handler = logging.StreamHandler()
+    handler.setLevel(DEBUG)
+    formatter = ShortLevelFormatter('%(asctime)s:%(msecs)03d %(levl)s| %(msg)s'
+                                    '   [regular levelname=%(levelname)s]',
+                                    datefmt='%H:%M:%S')
+    handler.setFormatter(formatter)
+    logger.addHandler(handler)
+
+    # 'application' code
+    logger.debug('debug message')
+    logger.info('info message')
+    logger.warn('warn message')
+    logger.error('error message')
+    logger.critical('critical message')
+    logger.log(15, 'unknown level')
+    logger.log(NOTSET, 'level not set')
+
+    # add notice level
+    notice = (logging.INFO + logging.WARNING)/2
+    formatter.add_level(notice, 'note')
+    logger.log(notice, 'more important than info but no warning nor error')
+
+    # try if exception formatting still works:
+    try:
+        logger.info('this is what an exception looks like:')
+        impossible_result = 1/0
+        logger.critical('just divided 1/0! The result is {0}'
+                        .format(impossible_result))
+    except ZeroDivisionError:
+        logger.exception('1/0 still does not work!', exc_info=True)
+
+    # done
+    logger.info('done testing')
+
+
+if __name__ == '__main__':
+    test_short_level_format()