Modifying logging message format based on message logging level in Python3

后端 未结 5 1191
天命终不由人
天命终不由人 2021-02-01 06:42

I asked this question for python 2 here, but bumped into the issue again when the the answer no longer worked for Python 3.2.3.

Here\'s code that works on Python 2.7.3:<

相关标签:
5条回答
  • 2021-02-01 07:03

    I am quiet late for this question but here is my solution. It follows the original python 2 syntax style. In general there are three new classes that you should be using due to the addition of styles support. Those are: PercentStyle, StrFormatStyle, and StringTemplateStyle.

    from logging import Formatter, PercentStyle, ERROR, WARNING, INFO, DEBUG
    class SrvLogFormat(Formatter):
        def __init__(self):
            super().__init__(fmt=env.fmt_log, datefmt=env.fmt_log_date)
    
        def format(self, record):
            original_style = self._style
    
            if record.levelno == DEBUG:
                self._style = PercentStyle(env.fmt_dflt)
            if record.levelno == INFO:
                self._style = PercentStyle(env.fmt_dflt)
            if record.levelno == WARNING:
                self._style = PercentStyle(env.fmt_dflt)
            if record.levelno == ERROR:
                self._style = PercentStyle(env.fmt_err)
    
            result = Formatter.format(self, record)
            self._style = original_style
            return result
    
    0 讨论(0)
  • 2021-02-01 07:06

    For some weird reasons, the solutions of @JS and @Evpok were raising some errors (I am using Python 3.7 and that might be why).

    This solution worked for me:

    class CustomFormatter(logging.Formatter):
        """Logging Formatter to add colors and count warning / errors"""
    
        FORMATS = {
            logging.ERROR: "ERROR: %(msg)s",
            logging.WARNING: "WARNING: %(msg)s",
            logging.DEBUG: "DBG: %(module)s: %(lineno)d: %(msg)s",
            "DEFAULT": "%(msg)s",
        }
    
        def format(self, record):
            log_fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
            formatter = logging.Formatter(log_fmt)
            return formatter.format(record)
    
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    logger_ch = logging.StreamHandler()
    logger_ch.setLevel(logging.INFO)
    logger_ch.setFormatter(CustomFormatter())
    logger.addHandler(logger_ch)
    
    0 讨论(0)
  • 2021-02-01 07:08

    I prefer this because it's shorter, simpler and does not require strings like 'ERROR' to be hard coded. No need to reset ._fmt, because else: can handle that just fine.

    Also, using "%(msg)s" doesn't work with lazy logging!

    class Formatter(logging.Formatter):
        def format(self, record):
            if record.levelno == logging.INFO:
                self._style._fmt = "%(message)s"
            else:
                self._style._fmt = "%(levelname)s: %(message)s"
            return super().format(record)
    

    Usage example:

    import logging
    
    logger = logging.getLogger()
    handler = logging.StreamHandler()
    handler.setFormatter(Formatter())
    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)
    
    logger.debug('foo')
    logger.info('bar %d', 4)
    
    DEBUG: foo
    bar 4
    

    In case you want to have the levelname colored:

    class Formatter(logging.Formatter):
        def format(self, record):
            if record.levelno == logging.INFO:
                self._style._fmt = "%(message)s"
            else:
                color = {
                    logging.WARNING: 33,
                    logging.ERROR: 31,
                    logging.FATAL: 31,
                    logging.DEBUG: 36
                }.get(record.levelno, 0)
                self._style._fmt = f"\033[{color}m%(levelname)s\033[0m: %(message)s"
            return super().format(record)
    

    see https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit for color numbers

    0 讨论(0)
  • 2021-02-01 07:09

    Cross-posting of another answer. It doesn't work because of the new (3.2+, 3.4 as of now) implementation of logging.Formatter which now relies on formatting styles. This relies on the '{' style format, but it can be adapted. Could be refined to be more general and allow selection of formatting style and custom messages as arguments to __init__, too.

    class SpecialFormatter(logging.Formatter):
        FORMATS = {logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),
               logging.ERROR : logging._STYLES['{']("{module} ERROR: {message}"),
               logging.INFO : logging._STYLES['{']("{module}: {message}"),
               'DEFAULT' : logging._STYLES['{']("{module}: {message}")}
    
        def format(self, record):
            # Ugly. Should be better
            self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
            return logging.Formatter.format(self, record)
    
    hdlr = logging.StreamHandler(sys.stderr)
    hdlr.setFormatter(SpecialFormatter())
    logging.root.addHandler(hdlr)
    logging.root.setLevel(logging.INFO)
    
    0 讨论(0)
  • 2021-02-01 07:12

    With a bit of digging, I was able to modify the Python 2 solution to work with Python 3. In Python2, it was necessary to temporarily overwrite Formatter._fmt. In Python3, support for multiple format string types requires us to temporarily overwrite Formatter._style._fmt instead.

    # Custom formatter
    class MyFormatter(logging.Formatter):
    
        err_fmt  = "ERROR: %(msg)s"
        dbg_fmt  = "DBG: %(module)s: %(lineno)d: %(msg)s"
        info_fmt = "%(msg)s"
    
        def __init__(self):
            super().__init__(fmt="%(levelno)d: %(msg)s", datefmt=None, style='%')  
        
        def format(self, record):
    
            # Save the original format configured by the user
            # when the logger formatter was instantiated
            format_orig = self._style._fmt
    
            # Replace the original format with one customized by logging level
            if record.levelno == logging.DEBUG:
                self._style._fmt = MyFormatter.dbg_fmt
    
            elif record.levelno == logging.INFO:
                self._style._fmt = MyFormatter.info_fmt
    
            elif record.levelno == logging.ERROR:
                self._style._fmt = MyFormatter.err_fmt
    
            # Call the original formatter class to do the grunt work
            result = logging.Formatter.format(self, record)
    
            # Restore the original format configured by the user
            self._style._fmt = format_orig
    
            return result
    

    And here is Halloleo's example of how to use the above in your script (from the Python2 version of this question):

    fmt = MyFormatter()
    hdlr = logging.StreamHandler(sys.stdout)
    
    hdlr.setFormatter(fmt)
    logging.root.addHandler(hdlr)
    logging.root.setLevel(logging.DEBUG)
    
    0 讨论(0)
提交回复
热议问题