5

Как окрасить вывод логирования Python?

12

Заголовок: Как вывести цветной лог в Python с помощью модуля logging?

Некоторое время назад я наткнулся на приложение на Mono, которое имело цветной вывод, предположительно благодаря своей системе логирования (все сообщения были стандартизированы).

Теперь в Python есть модуль logging, который позволяет настраивать множество параметров для вывода. Я предполагаю, что нечто подобное можно реализовать и в Python, но не могу найти информации о том, как это сделать.

Существует ли способ настроить вывод модуля logging в цвете?

Я хочу, например, чтобы ошибки отображались красным, сообщения отладки — синим или желтым и так далее.

Конечно, это, вероятно, потребует совместимого терминала (но большинство современных терминалов поддерживают это); в противном случае я мог бы вернуться к оригинальному выводу logging, если цвет не поддерживается.

Есть ли идеи, как получить цветной вывод с помощью модуля logging?

5 ответ(ов)

1

Чтобы быстро изменить стилизацию логов для предопределённых уровней логирования без создания нового класса, вы можете использовать функцию addLevelName из модуля logging. Это позволяет изменить отображаемые названия уровней и, например, добавить ANSI-коды для цветового оформления.

Вот пример, который задаёт красный текст для уровня WARNING и красный фон для уровня ERROR:

import logging

logging.addLevelName(logging.WARNING, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.WARNING))
logging.addLevelName(logging.ERROR, "\033[1;41m%s\033[1;0m" % logging.getLevelName(logging.ERROR))

После выполнения этого кода, при выводе логов уровня WARNING текст будет красным, а для ERROR фон будет красным.

Это быстрейший и грязный способ добиться желаемого эффекта без создания новых классов. Обратите внимание, что ANSI-коды могут не работать в некоторых средах, таких как Windows консоль, без дополнительной настройки.

0

Вот решение, которое должно работать на любой платформе. Если оно не сработает, просто дайте знать, и я обновлю его.

Как это работает: На платформах, поддерживающих ANSI-экраны, используются соответствующие команды (не Windows), а на Windows применяются API вызовы для изменения цветов консоли.

Скрипт перехватывает метод emit класса logging.StreamHandler из стандартной библиотеки, добавляя к нему оболочку.

TestColorer.py

# Использование: добавьте Colorer.py рядом с вашим скриптом и импортируйте его.
import logging
import Colorer

logging.warn("предупреждение")
logging.error("произошла ошибка")
logging.info("некоторая информация")

Colorer.py

#!/usr/bin/env python
# кодировка: utf-8
import logging

# Теперь мы добавляем поддержку цвета для logging.StreamHandler
def add_coloring_to_emit_windows(fn):
    def _out_handle(self):
        import ctypes
        return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
    out_handle = property(_out_handle)

    def _set_color(self, code):
        import ctypes
        self.STD_OUTPUT_HANDLE = -11
        hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
        ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code)

    setattr(logging.StreamHandler, '_set_color', _set_color)

    def new(*args):
        FOREGROUND_BLUE      = 0x0001
        FOREGROUND_GREEN     = 0x0002
        FOREGROUND_RED       = 0x0004
        FOREGROUND_INTENSITY = 0x0008
        FOREGROUND_WHITE     = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
        
        levelno = args[1].levelno
        if levelno >= 50:
            color = FOREGROUND_RED | FOREGROUND_INTENSITY
        elif levelno >= 40:
            color = FOREGROUND_RED | FOREGROUND_INTENSITY
        elif levelno >= 30:
            color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY
        elif levelno >= 20:
            color = FOREGROUND_GREEN
        elif levelno >= 10:
            color = FOREGROUND_MAGENTA
        else:
            color = FOREGROUND_WHITE

        args[0]._set_color(color)
        ret = fn(*args)
        args[0]._set_color(FOREGROUND_WHITE)
        return ret
    return new

def add_coloring_to_emit_ansi(fn):
    def new(*args):
        levelno = args[1].levelno
        if levelno >= 50:
            color = '\x1b[31m'  # красный
        elif levelno >= 40:
            color = '\x1b[31m'  # красный
        elif levelno >= 30:
            color = '\x1b[33m'  # желтый
        elif levelno >= 20:
            color = '\x1b[32m'  # зеленый
        elif levelno >= 10:
            color = '\x1b[35m'  # розовый
        else:
            color = '\x1b[0m'   # нормальный
        args[1].msg = color + args[1].msg + '\x1b[0m'  # нормальный
        return fn(*args)
    return new

import platform
if platform.system() == 'Windows':
    logging.StreamHandler.emit = add_coloring_to_emit_windows(logging.StreamHandler.emit)
else:
    logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)

Если у вас возникнут вопросы или проблемы с этим кодом, не стесняйтесь задавать их!

0

Вы обновили пример кода для airmind, добавив поддержку тегов для переднего и заднего фонов. Чтобы использовать цвета, вы можете воспользоваться переменными цвета $BLACK - $WHITE в строке форматирования вашего логгера. Для установки фона используйте $BG-BLACK - $BG-WHITE.

Вот как это выглядит на практике:

import logging

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

COLORS = {
    'WARNING'  : YELLOW,
    'INFO'     : WHITE,
    'DEBUG'    : BLUE,
    'CRITICAL' : YELLOW,
    'ERROR'    : RED,
    'RED'      : RED,
    'GREEN'    : GREEN,
    'YELLOW'   : YELLOW,
    'BLUE'     : BLUE,
    'MAGENTA'  : MAGENTA,
    'CYAN'     : CYAN,
    'WHITE'    : WHITE,
}

RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ  = "\033[1m"

class ColorFormatter(logging.Formatter):

    def __init__(self, *args, **kwargs):
        # нельзя использовать super(...) здесь, так как Formatter - это старая школа
        logging.Formatter.__init__(self, *args, **kwargs)

    def format(self, record):
        levelname = record.levelname
        color     = COLOR_SEQ % (30 + COLORS[levelname])
        message   = logging.Formatter.format(self, record)
        message   = message.replace("$RESET", RESET_SEQ)\
                           .replace("$BOLD",  BOLD_SEQ)\
                           .replace("$COLOR", color)
        for k,v in COLORS.items():
            message = message.replace("$" + k,    COLOR_SEQ % (v+30))\
                             .replace("$BG" + k,  COLOR_SEQ % (v+40))\
                             .replace("$BG-" + k, COLOR_SEQ % (v+40))
        return message + RESET_SEQ

logging.ColorFormatter = ColorFormatter

Теперь вы можете просто сделать следующее в вашем конфигурационном файле:

[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s

Таким образом, вы сможете легко менять цвета логов, используя переменные.

0

Рассмотрим следующее решение. Обработчик потока должен заниматься окрашиванием, тогда у вас будет возможность окрашивать слова, а не только целые строки (с помощью Formatter).

Вы можете ознакомиться с более подробной информацией по этой теме в блоге Plumberjack.

0

Сейчас доступен модуль PyPi для настраиваемого цветного логирования:

Этот модуль:

  • Поддерживает Windows
  • Поддерживает Django
  • Позволяет настраивать цвета

Поскольку он распространяется в виде Python egg, установить его просто для любого Python-приложения.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь