Search code examples
pythonloggingansi-escape

Using ANSI color codes with multiple logging handlers in Python producing strange results


I am working on logging for a program I have. I am setting up different formats for the log message depending on the level. This includes colors. I am have two handlers, one for the console and one for the file. I was working on the format because the ANSI codes leave characters behind in the log when I came across an issue by mistake. Here is the code:

import logging


# Define a custom formatter that can colorize log messages
class Formatter(logging.Formatter):
    def format(self, record):
        for handler in logging.getLogger().handlers:
            if handler.name != "file_handler":
                COLORS = {
                    'RESET': '',
                    'BLACK': '',
                    'RED': '',
                    'GREEN': '',
                    'YELLOW': '',
                    'BLUE': '',
                    'MAGENTA': '',
                    'CYAN': '',
                    'WHITE': '',
                    'BOLD': '',
                    'UNDERLINE': ''
                }
            else:
                COLORS = {
                    'RESET': '\x1b[0m',
                    'BLACK': '\x1b[30m',
                    'RED': '\x1b[31m',
                    'GREEN': '\x1b[32m',
                    'YELLOW': '\x1b[33m',
                    'BLUE': '\x1b[34m',
                    'MAGENTA': '\x1b[35m',
                    'CYAN': '\x1b[36m',
                    'WHITE': '\x1b[37m',
                    'BOLD': '\x1b[1m',
                    'UNDERLINE': '\x1b[4m'
                }

        self.FORMATS = {
            logging.DEBUG: f'{COLORS["BLUE"]} - {record.name} - {record.levelno} - {record.getMessage()}{COLORS["RESET"]}',
            logging.INFO: f'{COLORS["GREEN"]}INFO{COLORS["RESET"]} - {record.getMessage()}{COLORS["RESET"]}',
            logging.WARNING: f'{COLORS["YELLOW"]}WARNING{COLORS["RESET"]} - {record.getMessage()}',
            logging.ERROR: f'{COLORS["RED"]}ERROR{COLORS["RESET"]} - {record.getMessage()}',
            logging.CRITICAL: f'{COLORS["RED"]}CRITICAL{COLORS["RESET"]} - {record.getMessage()}',
        }
        # Override the default format based on log level
        log_fmt = self.FORMATS.get(record.levelno, self._fmt)
        return log_fmt.format(record)

file_handler = logging.FileHandler("log.log")
console_handler = logging.StreamHandler()
def log():
    global file_handler
    file_handler.name = "file_handler"
    file_handler.setFormatter(Formatter(style='{'))
    global console_handler
    console_handler.name = "stream_handler"
    console_handler.setFormatter(Formatter(style='{'))
    logging.basicConfig(
        level=logging.INFO,
        handlers=[file_handler, console_handler],
    )

if __name__ == "__main__":
    log()
    logging.debug("This is a debug message")
    logging.info("This is an info message")
    logging.warning("This is a warning message")
    logging.error("This is an error message")
    logging.critical("This is a critical message")

I immediately noticed that I got the logic wrong and if handler.name != "file_handler" should be if handler.name == "file_handler". But oddly, the console output was all red and the log file was actually in color. I am using PyCharm as my IDE and Python 3.10. Previously, the color codes in the log file were showing as characters like [31mERROR[0m - This is an error message. Now, in the same IDE, it is showing colors in the log file when it shouldn't, according to the code. I thought maybe I was just tired and my logic was backwards but I thought since I now know the log can be in color, I just got rid of the if-else and set COLORS with the color codes. But when I did that, the console is in color and the log file is back to having the characters. So I'm very confused as to what is going on. I think it might have something to do with the color reset. The lines in the log file are all in color but most of them should reset before the message. Additionally, I've noticed that in some of my trials, the INFO in the log is white instead of green. It's really perplexing and quite frankly, the more I work on it, the more confused I get. I'd appreciate any insights.


Solution

  • I think your issue is more related to PyCharm quirkiness than any logic in your code. Check to see if "Emulate terminal in output console" is enabled. I believe it is located int "Editor > Color Scheme Settings".