pycolib/coloredformatter.py
author Radek Brich <radek.brich@devl.cz>
Mon, 19 Aug 2013 16:32:33 +0200
changeset 10 51b7e98e1f41
parent 5 055f7dfb3e4f
permissions -rw-r--r--
Fix ColoredFormatted: Colored record could affect other formatters.

import logging
from copy import copy

from pycolib.ansicolor import highlight, BOLD, WHITE, YELLOW, RED


class ColoredFormatter(logging.Formatter):

    def __init__(self, fmt, datefmt):
        logging.Formatter.__init__(self, fmt, datefmt)
        self._colors = {
            'message': {
                'default': (None, None),
                'exception': (BOLD + YELLOW, RED),
                },
            'levelname': {
                'default': (BOLD + WHITE, None),
                'debug': (None, None),
                },
            'time': {
                'default': (BOLD + WHITE, None),
                'debug': (None, None),
                },
            }


    def formatTime(self, record, datefmt=None):
        """Override, add color as specified."""
        formatted = logging.Formatter.formatTime(self, record, datefmt)
        fg, bg = self.get_color('time', record)
        if fg or bg:
            formatted = highlight(1, fg, bg) + formatted + highlight(0)
        return formatted


    def format(self, record):
        """Override, add color as specified."""
        # work on copy, do not alter original record, which is processed by other formatters too
        record = copy(record)

        ### message color
        fg, bg = self.get_color('message', record)
        if fg or bg:
            record.msg = highlight(1, fg, bg) + record.msg + highlight(0)

        ### levelname color
        fg, bg = self.get_color('levelname', record)
        if fg or bg:
            record.levelname = highlight(1, fg, bg) + record.levelname + highlight(0)

        return logging.Formatter.format(self, record)


    def set_color(self, fieldname, levelname, fg=None, bg=None):
        """Specify color for parts of log message.

        Args:
            fieldname: Which field to apply: 'message' | 'levelname' | 'time'
            levelname: Log level to apply, case insensitive: 'debug' | 'info' etc.
                Custom levels are supported too.
                Special name 'default' is used for unspecified levels.

        """
        self._colors[fieldname][levelname.lower()] = (fg, bg)


    def get_color(self, fieldname, record):
        """Get specified color for field levelname (from record)."""
        colormap = self._colors[fieldname]
        levelname = record.levelname.lower()
        fg, bg = colormap['default']
        if levelname in colormap:
            fg, bg = colormap[levelname]
        # exception is not real level, detect it using exc_info
        if record.levelno == logging.ERROR and record.exc_info is not None \
        and 'exception' in colormap:
            fg, bg = colormap['exception']
        return fg, bg