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