Application of monkey patching, monkey patching to change the log.

With this monkey patch, all console logs of all py files in the project can be colored and clickable without modifying any code.

# -*- coding: utf-8 -*-
# @Author : ydf
# @Time : 2019/8/1 0001 17:54
"""
If the old project is useless to use Logmanager, you can apply this monkey patch, which will automatically change color and jump.

"""


import sys
import os
import logging


class ColorHandler(logging.Handler):
    """
    A handler class which writes logging records, appropriately formatted,
    to a stream. Note that this class does not close the stream, as
    sys.stdout or sys.stderr may be used.
    """
    os_name = os.name
    terminator = '\
'
    bule = 96 if os_name == 'nt' else 36
    yellow = 93 if os_name == 'nt' else 33

    def __init__(self, stream=None,):
        """
        Initialize the handler.

        If stream is not specified, sys.stderr is used.
        """
        logging.Handler.__init__(self)
        self. formatter = logging. Formatter(
            '%(asctime)s - %(name)s - "%(pathname)s:%(lineno)d" - %(funcName)s - %(levelname)s - %(message)s',
            "%Y-%m-%d %H:%M:%S")
        if stream is None:
            stream = sys.stdout # stderr colorless.
        self. stream = stream
        self._display_method = 7 if self.os_name == 'posix' else 0

    def setFormatter(self, fmt):
        pass # It is forbidden to set the log template privately. Fixed use of jumpable templates.

    def flush(self):
        """
        Flushes the stream.
        """
        self. acquire()
        try:
            if self. stream and hasattr(self. stream, "flush"):
                self. stream. flush()
        finally:
            self. release()

    def emit0(self, record):
        """
        Emit a record.

        If a formatter is specified, it is used to format the record.
        The record is then written to the stream with a trailing newline.
        exception information is present, it is formatted using
        traceback. print_exception and appended to the stream. If the stream
        has an 'encoding' attribute, it is used to determine how to do the
        output to the stream.
        """
        # noinspection PyBroadException
        try:
            msg = self. format(record)
            stream = self. stream
            if record.levelno == 10:
                # msg_color = ('\033[0;32m%s\033[0m' % msg) # green
                msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, 34 if self._is_pycharm_2019 else 32, msg)) # green
            elif record.levelno == 20:
                msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, self.bule, msg)) # blue 36 96
            elif record.levelno == 30:
                msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, self.yellow, msg))
            elif record.levelno == 40:
                msg_color = ('\033[%s;35m%s\033[0m' % (self._display_method, msg)) # purple
            elif record.levelno == 50:
                msg_color = ('\033[%s;31m%s\033[0m' % (self._display_method, msg)) # blood red
            else:
                msg_color = msg
            # print(msg_color,'****************')
            stream.write(msg_color)
            stream.write(self.terminator)
            self. flush()
        except Exception:
            self. handleError(record)

    def emit(self, record):
        """
        Emit a record.

        If a formatter is specified, it is used to format the record.
        The record is then written to the stream with a trailing newline.
        exception information is present, it is formatted using
        traceback. print_exception and appended to the stream. If the stream
        has an 'encoding' attribute, it is used to determine how to do the
        output to the stream.
        """
        # noinspection PyBroadException
        try:
            msg = self. format(record)
            stream = self. stream
            msg1, msg2 = self.__spilt_msg(record.levelno, msg)
            if record.levelno == 10:
                # msg_color = ('\033[0;32m%s\033[0m' % msg) # green
                msg_color = f'\033[0;32m{msg1}\033[0m \033[7;32m{msg2}\033[0m' # green
            elif record.levelno == 20:
                # msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, self.bule, msg)) # Cyan blue 36 96
                msg_color = f'\033[0;{self.bule}m{msg1}\033[0m \033[7;{self.bule}m{msg2}\033[0m'
            elif record.levelno == 30:
                # msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, self.yellow, msg))
                msg_color = f'\033[0;{self.yellow}m{msg1}\033[0m \033[7;{self.yellow}m{msg2}\033[0m'
            elif record.levelno == 40:
                # msg_color = ('\033[%s;35m%s\033[0m' % (self._display_method, msg)) # purple
                msg_color = f'\033[0;35m{msg1}\033[0m \033[7;35m{msg2}\033[0m'
            elif record.levelno == 50:
                # msg_color = ('\033[%s;31m%s\033[0m' % (self._display_method, msg)) # blood red
                msg_color = f'\033[0;31m{msg1}\033[0m \033[7;31m{msg2}\033[0m'
            else:
                msg_color = msg
            # print(msg_color,'****************')
            stream.write(msg_color)
            stream.write(self.terminator)
            self. flush()
        except Exception:
            self. handleError(record)

    @staticmethod
    def __spilt_msg(log_level, msg: str):
        split_text = '-level-'
        if log_level == 10:
            split_text = '- DEBUG -'
        elif log_level == 20:
            split_text = '-INFO-'
        elif log_level == 30:
            split_text = '-WARNING-'
        elif log_level == 40:
            split_text = '-ERROR-'
        elif log_level == 50:
            split_text = '-CRITICAL-'
        msg_split = msg.split(split_text, maxsplit=1)
        return msg_split[0] + split_text, msg_split[-1]

    def __repr__(self):
        level = logging. getLevelName(self. level)
        name = getattr(self. stream, 'name', '')
        if name:
            name += ' '
        return '<%s %s(%s)>' % (self.__class__.__name__, name, level)


logging.StreamHandler = ColorHandler # This line is a monkey patch, you can try to comment out this line for comparison.
"""
Here is monkey patching, monkey patching should be done at the very beginning of the script, the sooner the better.
Otherwise, the from logging import StreamHandler used in the original script will not become a colored handler.
Only import logging, this usage of logging.StreamHandler will be colorful. Therefore, the monkey patch should be applied as soon as possible.
"""

from logging import StreamHandler

logger = logging. getLogger('abc')
print(logger. handlers)
print(StreamHandler(). formatter)
logger. addHandler(StreamHandler())
logger. setLevel(10)


def my_func():
    logger.debug('a debug level log' * 5)
    logger.info('A log with info level' * 5)
    logger.warning('A warning level log' * 5)
    logger.error('A log of error level' * 5)
    logger.critical('a critical level log' * 5)
    print(logger. handlers)


if __name__ == '__main__':
    my_func()

turn out to be

After applying the Gouzi patch, that is, executing the following sentence, the log will be colored and jumpable.

logging. StreamHandler = ColorHandler