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


354

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

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

Есть ли способ сделать loggingвывод модуля Python в цвете?

Что я хочу (например) ошибки в красном, отладочные сообщения в синем или желтом, и так далее.

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

Любые идеи, как я могу получить цветной вывод с модулем регистрации?


1
Вы должны указать, что вы хотите мультиплатформенное решение - как Linux, так и Windows.
Сорин

1
Связано, если вы используете Eclipse / PyDev: раскрасить логи в консоли Eclipse
Тобиас Кинцлер,

5
Возможно, вы также можете использовать colorlog
Ehtesh Choudhury

5
Вы также можете попробовать chromalog, который я написал для поддержки всех операционных систем и версий Python (2.7 и 3. *)
ereOn

1
Решения, которые на самом деле сбрасывают коды ANSI в файл журнала, являются плохой идеей, они поймают вас, когда вы что-то ищете в течение шести месяцев, но забудете разрешить символы ANSI в вашем шаблоне регулярных выражений. Ниже приведены некоторые решения, которые добавляют цвет при просмотре журнала, а не при его записи ...
Джонатан Хартли,

Ответы:


192

Я уже знал о побегах цветов, я использовал их в моем приглашении bash некоторое время назад. Спасибо, в любом случае.
Я хотел интегрировать его с модулем регистрации, что я и сделал после нескольких попыток и ошибок.
Вот что я получаю в итоге:

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

#The background is set with 40 plus the number of the color, and the foreground with 30

#These are the sequences need to get colored ouput
RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ = "\033[1m"

def formatter_message(message, use_color = True):
    if use_color:
        message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
    else:
        message = message.replace("$RESET", "").replace("$BOLD", "")
    return message

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

class ColoredFormatter(logging.Formatter):
    def __init__(self, msg, use_color = True):
        logging.Formatter.__init__(self, msg)
        self.use_color = use_color

    def format(self, record):
        levelname = record.levelname
        if self.use_color and levelname in COLORS:
            levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
            record.levelname = levelname_color
        return logging.Formatter.format(self, record)

И чтобы использовать его, создайте свой собственный Logger:

# Custom logger class with multiple destinations
class ColoredLogger(logging.Logger):
    FORMAT = "[$BOLD%(name)-20s$RESET][%(levelname)-18s]  %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)"
    COLOR_FORMAT = formatter_message(FORMAT, True)
    def __init__(self, name):
        logging.Logger.__init__(self, name, logging.DEBUG)                

        color_formatter = ColoredFormatter(self.COLOR_FORMAT)

        console = logging.StreamHandler()
        console.setFormatter(color_formatter)

        self.addHandler(console)
        return


logging.setLoggerClass(ColoredLogger)

На всякий случай кому-то еще это нужно.

Будьте осторожны, если вы используете более одного регистратора или обработчика: ColoredFormatterизменяет объект записи, который передается другим обработчикам или распространяется другим регистраторам. Если вы настроили регистраторы файлов и т. Д., Вы, вероятно, не хотите иметь цвета в файлах журналов. Чтобы избежать этого, вероятно, лучше просто создать копию recordс copy.copy()перед тем, как манипулировать атрибутом namename, или сбросить имя уровня на предыдущее значение, прежде чем возвращать отформатированную строку Майклу в комментариях).


Где определены ЖЕЛТЫЙ, БЕЛЫЙ, СИНИЙ и т. Д.?
Swaroop CH

1
@Swaroop - это экранирующие коды ANSI, которые вы можете прочитать в Google или найти здесь: en.wikipedia.org/wiki/ANSI_escape_code или альтернативно pueblo.sourceforge.net/doc/manual/ansi_color_codes.html
Брайан М. Охота

53
Я не верю, что вы должны создать подкласс logger только для этого - ваш ответ хорош, если вы создадите специализированный Formatterи определите его использование для a StreamHandler. Но нет необходимости в подклассе регистратора. Фактически использование класса logger добавляет обработчик для каждого созданного logger, что обычно не то, что вы хотите.
Vinay Sajip


6
Одна сторона примечание ColoredFormatter. Это изменяет объект записи, который передается другим обработчикам или распространяется другим регистраторам. Если вы настроили регистраторы файлов и т. Д., Вы, вероятно, не хотите иметь цвета в файлах журналов. Чтобы избежать этого, вероятно, лучше всего просто создать копию recordс copy.copy()перед тем, как манипулировать атрибутом имени уровня, или сбросить имя уровня до предыдущего значения перед возвратом отформатированной строки.
Майкл

149

Несколько лет назад я написал обработчик цветного потока для собственного использования. Затем я наткнулся на эту страницу и обнаружил коллекцию фрагментов кода, которые люди копируют / вставляют :-(. Мой обработчик потока в настоящее время работает только в UNIX (Linux, Mac OS X), но преимущество в том, что он доступен в PyPIGitHub). ) и он очень прост в использовании. Он также имеет режим синтаксиса Vim :-). В будущем я мог бы расширить его для работы на Windows.

Чтобы установить пакет:

$ pip install coloredlogs

Чтобы подтвердить, что это работает:

$ coloredlogs --demo

Чтобы начать работу с собственным кодом:

$ python
> import coloredlogs, logging
> coloredlogs.install()
> logging.info("It works!")
2014-07-30 21:21:26 peter-macbook root[7471] INFO It works!

Формат журнала по умолчанию, показанный в приведенном выше примере, содержит дату, время, имя хоста, имя регистратора, PID, уровень журнала и сообщение журнала. Вот как это выглядит на практике:

Снимок экрана вывода цветных журналов

ПРИМЕЧАНИЕ: при использовании Git Bash с MinTTY

У Git Bash в Windows есть несколько задокументированных особенностей: Winpty и Git Bash

Который для управляющих кодов ANSI, а также для переписывания символов и анимации в стиле ncurses, вы должны использовать префикс команды winpty.

$ winpty coloredlogs --demo
$ winpty python your_colored_logs_script.py

2
достаточно забавно, я только собирался добавить ссылку на " pypi.python.org/pypi/coloredlogs/0.4.7 " в этой теме!
Иосу С.

1
По какой-то причине я продолжаю получать AttributeError: 'module' object has no attribute 'install'при использовании coloredlogs.install(). Можете ли вы подтвердить это последней версией.
con-f-use

11
Это выглядит красиво. К сожалению, это ломает многие вещи; в частности, он отменяет вызовы logging.basicConfig. Это делает невозможным использование пользовательского форматера, например.
Климент

@ Clément: два (накладывающихся друг на друга?) Вопроса: (1) что вы имеете в виду именно под "пустыми звонками в logging.basicConfig" и (2) какой будет альтернатива? И то, logging.basicConfig()и другое coloredlogs.install()установите потоковый обработчик, который регистрирует данные на консоли, поэтому без «аннулирования» вы получите дублирующиеся сообщения ...
xolox

Я ожидал либо волшебство для (1), либо (более разумно) способ сказать, coloredlogs.installкакой формат использовать, как в colorlogпакете.
Климент

74

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

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

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

TestColorer.py

# Usage: add Colorer.py near you script and import it.
import logging
import Colorer

logging.warn("a warning")
logging.error("some error")
logging.info("some info")

Colorer.py

#!/usr/bin/env python
# encoding: utf-8
import logging
# now we patch Python code to add color support to logging.StreamHandler
def add_coloring_to_emit_windows(fn):
        # add methods we need to the class
    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
        # Constants from the Windows API
        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 # text color contains blue.
        FOREGROUND_GREEN     = 0x0002 # text color contains green.
        FOREGROUND_RED       = 0x0004 # text color contains red.
        FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
        FOREGROUND_WHITE     = FOREGROUND_BLUE|FOREGROUND_GREEN |FOREGROUND_RED
       # winbase.h
        STD_INPUT_HANDLE = -10
        STD_OUTPUT_HANDLE = -11
        STD_ERROR_HANDLE = -12

        # wincon.h
        FOREGROUND_BLACK     = 0x0000
        FOREGROUND_BLUE      = 0x0001
        FOREGROUND_GREEN     = 0x0002
        FOREGROUND_CYAN      = 0x0003
        FOREGROUND_RED       = 0x0004
        FOREGROUND_MAGENTA   = 0x0005
        FOREGROUND_YELLOW    = 0x0006
        FOREGROUND_GREY      = 0x0007
        FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.

        BACKGROUND_BLACK     = 0x0000
        BACKGROUND_BLUE      = 0x0010
        BACKGROUND_GREEN     = 0x0020
        BACKGROUND_CYAN      = 0x0030
        BACKGROUND_RED       = 0x0040
        BACKGROUND_MAGENTA   = 0x0050
        BACKGROUND_YELLOW    = 0x0060
        BACKGROUND_GREY      = 0x0070
        BACKGROUND_INTENSITY = 0x0080 # background color is intensified.     

        levelno = args[1].levelno
        if(levelno>=50):
            color = BACKGROUND_YELLOW | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_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 )
        #print "after"
        return ret
    return new

def add_coloring_to_emit_ansi(fn):
    # add methods we need to the class
    def new(*args):
        levelno = args[1].levelno
        if(levelno>=50):
            color = '\x1b[31m' # red
        elif(levelno>=40):
            color = '\x1b[31m' # red
        elif(levelno>=30):
            color = '\x1b[33m' # yellow
        elif(levelno>=20):
            color = '\x1b[32m' # green 
        elif(levelno>=10):
            color = '\x1b[35m' # pink
        else:
            color = '\x1b[0m' # normal
        args[1].msg = color + args[1].msg +  '\x1b[0m'  # normal
        #print "after"
        return fn(*args)
    return new

import platform
if platform.system()=='Windows':
    # Windows does not support ANSI escapes and we are using API calls to set the console color
    logging.StreamHandler.emit = add_coloring_to_emit_windows(logging.StreamHandler.emit)
else:
    # all non-Windows platforms are supporting ANSI escapes so we use them
    logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)
    #log = logging.getLogger()
    #log.addFilter(log_filter())
    #//hdlr = logging.StreamHandler()
    #//hdlr.setFormatter(formatter())

3
Я написал класс StreamHandler, основанный на этом, см. Gist.github.com/mooware/a1ed40987b6cc9ab9c65 .
Mooware

2
это сработало для меня! строка 90: должно быть args[1].msg = color + str(args[1].msg) + '\x1b[0m' # normal.
Расика Перера

Мне нравится это решение. используя это в настоящее время. Я вижу, что есть атрибут _set_color, есть ли способ сделать это для конкретного сообщения журнала? отредактируйте , о, видите, это просто патч для машин с Windows. было бы неплохо добавить кастом для разных вариантов использования.
Бриз

+1 за цвет ANSI. В xterm вы даже можете получить 256 цветов за раз, и вы можете определять палитру динамически! Однако обратите внимание, что все вызовы функций ведения журнала должны быть в определении функции, чтобы избежать потенциальных проблем с блокировкой импорта при ведении журнала вне определения функции . Ваш код выглядит в основном хорошо; только это немного TestColorer.pyкасается меня.
personal_cloud

Это приводит к цветовым кодам в начале и в конце сообщений журнала в реальных файлах журнала.
MehmedB

74

Обновление : так как это зуд, о котором я так долго думал, я решил написать библиотеку для таких ленивых людей, как я, которые просто хотят делать простые вещи: zenlog

Colorlog отлично подходит для этого. Он доступен на PyPI (и, следовательно, устанавливается черезpip install colorlog ) и активно поддерживается .

Вот быстрый фрагмент, который можно скопировать и вставить, чтобы настроить ведение журнала и распечатать прилично выглядящие сообщения журнала:

import logging
LOG_LEVEL = logging.DEBUG
LOGFORMAT = "  %(log_color)s%(levelname)-8s%(reset)s | %(log_color)s%(message)s%(reset)s"
from colorlog import ColoredFormatter
logging.root.setLevel(LOG_LEVEL)
formatter = ColoredFormatter(LOGFORMAT)
stream = logging.StreamHandler()
stream.setLevel(LOG_LEVEL)
stream.setFormatter(formatter)
log = logging.getLogger('pythonConfig')
log.setLevel(LOG_LEVEL)
log.addHandler(stream)

log.debug("A quirky message only developers care about")
log.info("Curious users might want to know this")
log.warn("Something is wrong and any user should be informed")
log.error("Serious stuff, this is red for a reason")
log.critical("OH NO everything is on fire")

Вывод:

Colorlog выход


4
Отличный ответ; +1. Хотя пример кода можно урезать ( setLevelдействительно ли нужны три вызова ?)
Clément

1
Я надеялся, что найду ответ, подобный этому, если бы достаточно долго пролистывал ответы. ☺ Я надеюсь, что @airmind рассмотрит вопрос о том, чтобы сделать этот ответ приемлемым, чтобы будущие умные люди могли найти лучшую библиотеку с оптимальной ленью. Michael
Майкл Шепер

Я только что проголосовал за примеры сообщений ВЫХОДА ^^
Агустин Баррачина

69

Быстрое и грязное решение для предопределенных уровней журнала и без определения нового класса.

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))

@ spiderplant0 регистрация импорта; # вставьте код из @ABC; попробуйте это с logging.warning («это тест»). Вы увидите заглавную часть «ПРЕДУПРЕЖДЕНИЕ: это тест» в цвете. Это работает только на Linux, между прочим
Риккардо Галли

3
Так как только имя уровня логирования окрашено, вы должны убедиться, что имя уровня лога вообще напечатано на консоли. Это не происходит из коробки для меня. Что-то в этом роде поможет: logging.basicConfig(format='%(asctime)s [%(name)s] [%(levelname)s] %(message)s')где, конечно, %(levelnames)sважно.
Себастьян

4
Самое простое и чистое решение для применения и понимания.
Ф. Сантьяго

1
Просто попробуйте в консоли Linux. echo -e "Normal texst \033[1;31mred bold text\033[0m normal text again", Параметр echo -eинтерпретирует "\ 033" как восьмеричную форму символа Escape ASCII. Этот специальный символ заставляет некоторые совместимые терминалы интерпретировать последующие символы ( mвключая символы ) как специальные команды. en.wikipedia.org/wiki/ANSI_escape_code
eugene-bright

1
Незначительное улучшение: поместите этот код внутрь if sys.sdterr.isatty():. В этом случае, если вы перенаправите вывод в файл, файл не будет содержать эти escape-символы.
Лесник

36

Код 2020, дополнительные пакеты не требуются, Python 3

Определить класс

import logging

class CustomFormatter(logging.Formatter):
    """Logging Formatter to add colors and count warning / errors"""

    grey = "\x1b[38;21m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    reset = "\x1b[0m"
    format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"

    FORMATS = {
        logging.DEBUG: grey + format + reset,
        logging.INFO: grey + format + reset,
        logging.WARNING: yellow + format + reset,
        logging.ERROR: red + format + reset,
        logging.CRITICAL: bold_red + format + reset
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt)
        return formatter.format(record)

Создайте логгер

# create logger with 'spam_application'
logger = logging.getLogger("My_app")
logger.setLevel(logging.DEBUG)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

ch.setFormatter(CustomFormatter())

logger.addHandler(ch)

И использовать!

logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")

Результат введите описание изображения здесь

Полная цветовая схема введите описание изображения здесь

Для окон

Это решение работает на Mac OS, терминалах IDE. Похоже, командная строка окна по умолчанию вообще не имеет цветов. Вот инструкции по их включению, которые я не пробовал https://www.howtogeek.com/322432/how-to-customize-your-command-prompts-color-scheme-with-microsofts-colortool/


1
Я запускаю тест (python 3.7, windows), но при ведении журнала не отображаются цвета:←[38;21m2019-11-12 19:29:50,994 - My_app - DEBUG - debug message (test_colored_log.py:43)←[0m ←[38;21m2019-11-12 19:29:50,994 - My_app - INFO - info message (test_colored_log.py:44)←[0m ←[33;21m2019-11-12 19:29:50,994 - My_app - WARNING - warning message (test_colored_log.py:45)←[0m ←[31;21m2019-11-12 19:29:50,994 - My_app - ERROR - error message (test_colored_log.py:46)←[0m ←[31;1m2019-11-12 19:29:50,994 - My_app - CRITICAL - critical message (test_colored_log.py:47)←[0m
конструктор

Это не работает, к сожалению.
Джо

2
Мне так понравился этот ответ, что я сделал для него репо с несколькими приращениями и шпаргалкой цветов ANSI.
Теодоро

@constructor, где вы его запускаете? Консоль IDE? оконный терминал?
Сергей Плешаков

@ Джо, что именно не работает? какова ваша среда и какие ошибки вы получаете? Я хотел бы пересмотреть решение, чтобы оно работало на разных платформах
Сергей Плешаков

17

Ну, я думаю, я мог бы также добавить свой вариант цветного регистратора.

В этом нет ничего особенного, но он очень прост в использовании и не меняет объект записи, тем самым избегая записи escape-последовательностей ANSI в файл журнала, если используется обработчик файла. Это не влияет на форматирование сообщения журнала.

Если вы уже используете Formatter модуля ведения журнала , все, что вам нужно сделать, чтобы получить цветные имена уровней, это заменить ваши обработчики адвокатов Formatter на ColoredFormatter. Если вы регистрируете приложение целиком, вам нужно сделать это только для регистратора верхнего уровня.

colored_log.py

#!/usr/bin/env python

from copy import copy
from logging import Formatter

MAPPING = {
    'DEBUG'   : 37, # white
    'INFO'    : 36, # cyan
    'WARNING' : 33, # yellow
    'ERROR'   : 31, # red
    'CRITICAL': 41, # white on red bg
}

PREFIX = '\033['
SUFFIX = '\033[0m'

class ColoredFormatter(Formatter):

    def __init__(self, patern):
        Formatter.__init__(self, patern)

    def format(self, record):
        colored_record = copy(record)
        levelname = colored_record.levelname
        seq = MAPPING.get(levelname, 37) # default white
        colored_levelname = ('{0}{1}m{2}{3}') \
            .format(PREFIX, seq, levelname, SUFFIX)
        colored_record.levelname = colored_levelname
        return Formatter.format(self, colored_record)

Пример использования

app.py

#!/usr/bin/env python

import logging
from colored_log import ColoredFormatter

# Create top level logger
log = logging.getLogger("main")

# Add console handler using our custom ColoredFormatter
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
cf = ColoredFormatter("[%(name)s][%(levelname)s]  %(message)s (%(filename)s:%(lineno)d)")
ch.setFormatter(cf)
log.addHandler(ch)

# Add file handler
fh = logging.FileHandler('app.log')
fh.setLevel(logging.DEBUG)
ff = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(ff)
log.addHandler(fh)

# Set log level
log.setLevel(logging.DEBUG)

# Log some stuff
log.debug("app has started")
log.info("Logging to 'app.log' in the script dir")
log.warning("This is my last warning, take heed")
log.error("This is an error")
log.critical("He's dead, Jim")

# Import a sub-module 
import sub_module

sub_module.py

#!/usr/bin/env python

import logging
log = logging.getLogger('main.sub_module')

log.debug("Hello from the sub module")

Результаты

Терминальный выход

Терминальный выход

содержание app.log

2017-09-29 00:32:23,434 - main - DEBUG - app has started
2017-09-29 00:32:23,434 - main - INFO - Logging to 'app.log' in the script dir
2017-09-29 00:32:23,435 - main - WARNING - This is my last warning, take heed
2017-09-29 00:32:23,435 - main - ERROR - This is an error
2017-09-29 00:32:23,435 - main - CRITICAL - He's dead, Jim
2017-09-29 00:32:23,435 - main.sub_module - DEBUG - Hello from the sub module

Конечно, вы можете получить все, что захотите, с форматированием терминала и выводом файла журнала. Только уровень журнала будет раскрашен.

Я надеюсь, что кто-то найдет это полезным, и это не слишком много того же самого. :)

Файлы примеров Python можно загрузить из этого GitHub Gist: https://gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd


2
Кстати, чтобы добавить цвета к самому сообщению, просто добавьте эту строку перед return:colored_record.msg = ('{0}{1}m{2}{3}').format(self.PREFIX, seq, colored_record.getMessage(), self.SUFFIX)
Крестный отец

15

Я обновил пример поддержки тегов 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):
        # can't do super(...) here because Formatter is an old school class
        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

Большое улучшение Однако, superя думаю, что комментарий относится только к какой-то древней версии Python? Так как этот ответ с 2010 года. Он отлично работал для меня с Python 2.7
Иоаким

14

Вы можете импортировать модуль colorlog и использовать его ColoredFormatterдля раскрашивания сообщений журнала.

пример

Бойлер для основного модуля:

import logging
import os
import sys
try:
    import colorlog
except ImportError:
    pass

def setup_logging():
    root = logging.getLogger()
    root.setLevel(logging.DEBUG)
    format      = '%(asctime)s - %(levelname)-8s - %(message)s'
    date_format = '%Y-%m-%d %H:%M:%S'
    if 'colorlog' in sys.modules and os.isatty(2):
        cformat = '%(log_color)s' + format
        f = colorlog.ColoredFormatter(cformat, date_format,
              log_colors = { 'DEBUG'   : 'reset',       'INFO' : 'reset',
                             'WARNING' : 'bold_yellow', 'ERROR': 'bold_red',
                             'CRITICAL': 'bold_red' })
    else:
        f = logging.Formatter(format, date_format)
    ch = logging.StreamHandler()
    ch.setFormatter(f)
    root.addHandler(ch)

setup_logging()
log = logging.getLogger(__name__)

Код включает цвета только в сообщениях журнала, если установлен модуль colorlog и если выходные данные действительно передаются на терминал. Это позволяет избежать записи escape-последовательностей в файл при перенаправлении вывода журнала.

Также настраивается индивидуальная цветовая схема, которая лучше подходит для терминалов с темным фоном.

Некоторые примеры регистрации звонков:

log.debug   ('Hello Debug')
log.info    ('Hello Info')
log.warn    ('Hello Warn')
log.error   ('Hello Error')
log.critical('Hello Critical')

Вывод:

введите описание изображения здесь


2
Также можно использовать colorlog.basicConfigвместо logging.basicConfigкоторых есть некоторые хорошие значения по умолчанию
MarSoft

1
Для записи colorlog не всегда работает напрямую на платформах Windows (как указано, требуется зависимость от цвета). Несмотря на это, у меня были проблемы с тем, чтобы заставить его работать в Enaconda / Spyder env. Вам может потребоваться указать colorama.init (strip = False), например, в escape_code.py (как указано в этой теме github.com/spyder-ide/spyder/issues/1917 )
Matt-Mac-Muffin

11

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

http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html


Вы можете найти обновленную реализацию в этой сути (поддерживается автором блога). Я использую это и работает просто отлично. Спасибо, что поделился.
шумное

11

Я изменил исходный пример, предоставленный Сорином, и подклассифицированный StreamHandler в ColorizedConsoleHandler.

Недостатком их решения является то, что оно модифицирует сообщение, и поскольку оно изменяет фактическое сообщение журнала, любые другие обработчики также получат измененное сообщение.

Это привело к лог-файлам с цветными кодами в нашем случае, потому что мы используем несколько регистраторов.

Класс ниже работает только на платформах, которые поддерживают ANSI, но добавить к нему цветовые коды Windows должно быть тривиально.

import copy
import logging


class ColoredConsoleHandler(logging.StreamHandler):
    def emit(self, record):
        # Need to make a actual copy of the record
        # to prevent altering the message for other loggers
        myrecord = copy.copy(record)
        levelno = myrecord.levelno
        if(levelno >= 50):  # CRITICAL / FATAL
            color = '\x1b[31m'  # red
        elif(levelno >= 40):  # ERROR
            color = '\x1b[31m'  # red
        elif(levelno >= 30):  # WARNING
            color = '\x1b[33m'  # yellow
        elif(levelno >= 20):  # INFO
            color = '\x1b[32m'  # green
        elif(levelno >= 10):  # DEBUG
            color = '\x1b[35m'  # pink
        else:  # NOTSET and anything else
            color = '\x1b[0m'  # normal
        myrecord.msg = color + str(myrecord.msg) + '\x1b[0m'  # normal
        logging.StreamHandler.emit(self, myrecord)

10

Теперь есть выпущенный модуль PyPi для настраиваемого вывода цветных журналов:

https://pypi.python.org/pypi/rainbow_logging_handler/

а также

https://github.com/laysakura/rainbow_logging_handler

  • Поддерживает Windows

  • Поддерживает Джанго

  • Настраиваемые цвета

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


7

Есть тонны ответов. Но никто не говорит о декораторах. Так вот мой.

Потому что это намного проще.

Нет необходимости ни импортировать, ни писать подклассы:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import logging


NO_COLOR = "\33[m"
RED, GREEN, ORANGE, BLUE, PURPLE, LBLUE, GREY = \
    map("\33[%dm".__mod__, range(31, 38))

logging.basicConfig(format="%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)

# the decorator to apply on the logger methods info, warn, ...
def add_color(logger_method, color):
  def wrapper(message, *args, **kwargs):
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

for level, color in zip((
  "info", "warn", "error", "debug"), (
  GREEN, ORANGE, RED, BLUE
)):
  setattr(logger, level, add_color(getattr(logger, level), color))

# this is displayed in red.
logger.error("Launching %s." % __file__)

Это устанавливает ошибки красным, отладочные сообщения синим и так далее. Как и в вопросе.

Мы могли бы даже адаптировать обертку, чтобы взять color аргумент для динамической установки цвета сообщения, используяlogger.debug("message", color=GREY)

РЕДАКТИРОВАТЬ: Итак, вот адаптированный декоратор для установки цветов во время выполнения:

def add_color(logger_method, _color):
  def wrapper(message, *args, **kwargs):
    color = kwargs.pop("color", _color)
    if isinstance(color, int):
      color = "\33[%dm" % color
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

# blah blah, apply the decorator...

# this is displayed in red.
logger.error("Launching %s." % __file__)
# this is displayed in blue
logger.error("Launching %s." % __file__, color=34)
# and this, in grey
logger.error("Launching %s." % __file__, color=GREY)

6

Еще один незначительный ремикс подхода Airmind, который держит все в одном классе:

class ColorFormatter(logging.Formatter):
  FORMAT = ("[$BOLD%(name)-20s$RESET][%(levelname)-18s]  "
            "%(message)s "
            "($BOLD%(filename)s$RESET:%(lineno)d)")

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

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

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

  def formatter_msg(self, msg, use_color = True):
    if use_color:
      msg = msg.replace("$RESET", self.RESET_SEQ).replace("$BOLD", self.BOLD_SEQ)
    else:
      msg = msg.replace("$RESET", "").replace("$BOLD", "")
    return msg

  def __init__(self, use_color=True):
    msg = self.formatter_msg(self.FORMAT, use_color)
    logging.Formatter.__init__(self, msg)
    self.use_color = use_color

  def format(self, record):
    levelname = record.levelname
    if self.use_color and levelname in self.COLORS:
      fore_color = 30 + self.COLORS[levelname]
      levelname_color = self.COLOR_SEQ % fore_color + levelname + self.RESET_SEQ
      record.levelname = levelname_color
    return logging.Formatter.format(self, record)

Чтобы использовать присоединение форматера к обработчику, что-то вроде:

handler.setFormatter(ColorFormatter())
logger.addHandler(handler)

5

Простой, но очень гибкий инструмент для окраски ЛЮБОГО терминального текста - « colout ».

pip install colout
myprocess | colout REGEX_WITH_GROUPS color1,color2...

Где любой текст в выводе «myprocess», который соответствует группе 1 регулярного выражения, будет окрашен в color1, группа 2 в color2 и т. Д.

Например:

tail -f /var/log/mylogfile | colout '^(\w+ \d+ [\d:]+)|(\w+\.py:\d+ .+\(\)): (.+)$' white,black,cyan bold,bold,normal

т.е. первая группа регулярных выражений (parens) соответствует начальной дате в файле журнала, вторая группа соответствует имени файла python, номеру строки и имени функции, а третья группа соответствует сообщению журнала, которое следует после этого. Я также использую параллельную последовательность «жирный / нормальный», а также последовательность цветов. Это выглядит так:

лог-файл с цветным форматированием

Обратите внимание, что строки или части строк, которые не соответствуют ни одному из моих регулярных выражений, все еще отражаются, так что это не похоже на 'grep --color' - ничего не отфильтровано из вывода.

Очевидно, что это достаточно гибко, так что вы можете использовать его с любым процессом, а не только с файлами журналов. Обычно я просто запускаю новое регулярное выражение на лету, когда хочу что-то раскрасить. По этой причине я предпочитаю colout любому пользовательскому инструменту окраски лог-файлов, потому что мне нужно изучить только один инструмент, независимо от того, что я раскрашиваю: ведение журнала, тестовый вывод, подсветка синтаксиса, фрагменты кода в терминале и т. Д.

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


4
import logging
import sys

colors = {'pink': '\033[95m', 'blue': '\033[94m', 'green': '\033[92m', 'yellow': '\033[93m', 'red': '\033[91m',
      'ENDC': '\033[0m', 'bold': '\033[1m', 'underline': '\033[4m'}

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)


def str_color(color, data):
    return colors[color] + str(data) + colors['ENDC']

params = {'param1': id1, 'param2': id2}

logging.info('\nParams:' + str_color("blue", str(params)))`

+1 Хороший пример с [9*mкодами для «ярких» цветов ANSI! PS Ваша последняя строка немного беспокоит меня, потому что еще не известно, безопасна ли регистрация в журнале вне определения функции в Python .
personal_cloud

2

Вот мое решение:

class ColouredFormatter(logging.Formatter):
    RESET = '\x1B[0m'
    RED = '\x1B[31m'
    YELLOW = '\x1B[33m'
    BRGREEN = '\x1B[01;32m'  # grey in solarized for terminals

    def format(self, record, colour=False):
        message = super().format(record)

        if not colour:
            return message

        level_no = record.levelno
        if level_no >= logging.CRITICAL:
            colour = self.RED
        elif level_no >= logging.ERROR:
            colour = self.RED
        elif level_no >= logging.WARNING:
            colour = self.YELLOW
        elif level_no >= logging.INFO:
            colour = self.RESET
        elif level_no >= logging.DEBUG:
            colour = self.BRGREEN
        else:
            colour = self.RESET

        message = colour + message + self.RESET

        return message


class ColouredHandler(logging.StreamHandler):
    def __init__(self, stream=sys.stdout):
        super().__init__(stream)

    def format(self, record, colour=False):
        if not isinstance(self.formatter, ColouredFormatter):
            self.formatter = ColouredFormatter()

        return self.formatter.format(record, colour)

    def emit(self, record):
        stream = self.stream
        try:
            msg = self.format(record, stream.isatty())
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)


h = ColouredHandler()
h.formatter = ColouredFormatter('{asctime} {levelname:8} {message}', '%Y-%m-%d %H:%M:%S', '{')
logging.basicConfig(level=logging.DEBUG, handlers=[h])

1

У меня были проблемы с правильной настройкой форматера:

class ColouredFormatter(logging.Formatter):    
    def __init__(self, msg):
        logging.Formatter.__init__(self, msg)
        self._init_colour = _get_colour()

    def close(self):
        # restore the colour information to what it was
        _set_colour(self._init_colour)

    def format(self, record):        
        # Add your own colourer based on the other examples
        _set_colour( LOG_LEVEL_COLOUR[record.levelno] )
        return logging.Formatter.format(self, record)         

def init():
    # Set up the formatter. Needs to be first thing done.
    rootLogger = logging.getLogger()
    hdlr = logging.StreamHandler()
    fmt = ColouredFormatter('%(message)s')
    hdlr.setFormatter(fmt)
    rootLogger.addHandler(hdlr)

А потом использовать:

import coloured_log
import logging

coloured_log.init()
logging.info("info")    
logging.debug("debug")    

coloured_log.close()    # restore colours

Предполагалось, что это псевдокод (также отсутствует _set_colour), но что-то добавили. Больше всего было проблем с умением правильно подключить форматтер.
Ник

Смотрите решение "Домкрат сантехника". Я думаю, что это лучший способ решить проблему (то есть обработчик должен выполнить колоризацию). stackoverflow.com/questions/384076/…
Ник

1

Хотя другие решения кажутся хорошими, у них есть некоторые проблемы. Некоторые действительно окрашивают целые строки, которые иногда не нужны, а некоторые пропускают любую конфигурацию, которую вы могли бы иметь вместе. Решение ниже не влияет ни на что, кроме самого сообщения.

Код

class ColoredFormatter(logging.Formatter):
    def format(self, record):
        if record.levelno == logging.WARNING:
            record.msg = '\033[93m%s\033[0m' % record.msg
        elif record.levelno == logging.ERROR:
            record.msg = '\033[91m%s\033[0m' % record.msg
        return logging.Formatter.format(self, record)

пример

logger = logging.getLogger('mylogger')
handler = logging.StreamHandler()

log_format = '[%(asctime)s]:%(levelname)-7s:%(message)s'
time_format = '%H:%M:%S'
formatter = ColoredFormatter(log_format, datefmt=time_format)
handler.setFormatter(formatter)
logger.addHandler(handler)

logger.warn('this should be yellow')
logger.error('this should be red')

Вывод

[17:01:36]:WARNING:this should be yellow
[17:01:37]:ERROR  :this should be red

Как видите, все остальное все еще выводится и остается в своем первоначальном цвете. Если вы хотите изменить что-либо кроме сообщения, вы можете просто передать цветовые коды log_formatв примере.


когда я его использую, сообщения печатаются дважды. ты знаешь почему?
Validus Oculus

@ не могли бы вы уточнить? А именно ты имеешь в виду что-то вроде [17:01:36]:WARNING:this should be yellowthis should be yellowили полная строка печатается дважды?
Питикос

Извините за краткость комментария. Произошло первое: [17:01:36]: ВНИМАНИЕ: это должен быть желтый цвет \ nэто должен быть желтый цвет. Однако я хочу, чтобы отображался только отформатированный файл, иначе он выглядит как мусор из-за избыточных журналов.
Validus Oculus

@ MuratKarakuş не уверен, почему это происходит, не имея полного представления о реализации. Если вы используете собственный регистратор, может быть, вы вмешиваетесь в какой-то момент? Быстрое решение может быть удалить 7s:%(message)sиз log_format.
Питикос,

1

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

Некоторая информация была получена (с изменениями) из: поста выше и http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html .

Раскрашивает только сообщение:

class ColoredFormatter(logging.Formatter):
    """Special custom formatter for colorizing log messages!"""

    BLACK = '\033[0;30m'
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    BROWN = '\033[0;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    GREY = '\033[0;37m'

    DARK_GREY = '\033[1;30m'
    LIGHT_RED = '\033[1;31m'
    LIGHT_GREEN = '\033[1;32m'
    YELLOW = '\033[1;33m'
    LIGHT_BLUE = '\033[1;34m'
    LIGHT_PURPLE = '\033[1;35m'
    LIGHT_CYAN = '\033[1;36m'
    WHITE = '\033[1;37m'

    RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.DARK_GREY,
                        logging.INFO: self.RESET,
                        logging.WARNING: self.BROWN,
                        logging.ERROR: self.RED,
                        logging.CRITICAL: self.LIGHT_RED}
        super(ColoredFormatter, self).__init__(*args, **kwargs)

    def format(self, record):
        """Applies the color formats"""
        record.msg = self._colors[record.levelno] + record.msg + self.RESET
        return logging.Formatter.format(self, record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code

Раскрашивает всю линию:

class ColorizingStreamHandler(logging.StreamHandler):

    BLACK = '\033[0;30m'
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    BROWN = '\033[0;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    GREY = '\033[0;37m'

    DARK_GREY = '\033[1;30m'
    LIGHT_RED = '\033[1;31m'
    LIGHT_GREEN = '\033[1;32m'
    YELLOW = '\033[1;33m'
    LIGHT_BLUE = '\033[1;34m'
    LIGHT_PURPLE = '\033[1;35m'
    LIGHT_CYAN = '\033[1;36m'
    WHITE = '\033[1;37m'

    RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.DARK_GREY,
                        logging.INFO: self.RESET,
                        logging.WARNING: self.BROWN,
                        logging.ERROR: self.RED,
                        logging.CRITICAL: self.LIGHT_RED}
        super(ColorizingStreamHandler, self).__init__(*args, **kwargs)

    @property
    def is_tty(self):
        isatty = getattr(self.stream, 'isatty', None)
        return isatty and isatty()

    def emit(self, record):
        try:
            message = self.format(record)
            stream = self.stream
            if not self.is_tty:
                stream.write(message)
            else:
                message = self._colors[record.levelno] + message + self.RESET
                stream.write(message)
            stream.write(getattr(self, 'terminator', '\n'))
            self.flush()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code


1

Это Enum, содержащий цветовые коды:

class TerminalColour:
    """
    Terminal colour formatting codes
    """
    # /programming/287871/print-in-terminal-with-colors
    MAGENTA = '\033[95m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    GREY = '\033[0m'  # normal
    WHITE = '\033[1m'  # bright white
    UNDERLINE = '\033[4m'

Это может быть применено к именам каждого уровня журнала. Имейте в виду, что это чудовищный взлом.

logging.addLevelName(logging.INFO, "{}{}{}".format(TerminalColour.WHITE, logging.getLevelName(logging.INFO), TerminalColour.GREY))
logging.addLevelName(logging.WARNING, "{}{}{}".format(TerminalColour.YELLOW, logging.getLevelName(logging.WARNING), TerminalColour.GREY))
logging.addLevelName(logging.ERROR, "{}{}{}".format(TerminalColour.RED, logging.getLevelName(logging.ERROR), TerminalColour.GREY))
logging.addLevelName(logging.CRITICAL, "{}{}{}".format(TerminalColour.MAGENTA, logging.getLevelName(logging.CRITICAL), .GREY))

Обратите внимание, что ваш форматер журнала должен содержать имя уровня журнала

%(levelname)

например:

    LOGGING = {
...
        'verbose': {
            'format': '%(asctime)s %(levelname)s %(name)s:%(lineno)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '[%(asctime)s] %(levelname)s %(name)s %(message)s'
        },

1

FriendlyLog - это еще одна альтернатива. Он работает с Python 2 и 3 под Linux, Windows и MacOS.


В ожидании нового PR, чтобы уменьшить беспорядок пути модуля
mbspark

1

А как насчет выделения также регистрировать аргументы сообщения чередующимися цветами, в дополнение к раскраске по уровню? Я недавно написал простой код для этого. Другое преимущество заключается в том, что вызов журнала выполняется с использованием форматирования в скобках в стиле Python 3. ( "{}").

Смотрите последний код и примеры здесь: https://github.com/davidohana/colargulog

Пример регистрационного кода:

root_logger = logging.getLogger()
console_handler = logging.StreamHandler(stream=sys.stdout)
console_format = "%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s"
colored_formatter = ColorizedArgsFormatter(console_format)
console_handler.setFormatter(colored_formatter)
root_logger.addHandler(console_handler)

logger = logging.getLogger(__name__)
logger.info("Hello World")
logger.info("Request from {} handled in {:.3f} ms", socket.gethostname(), 11)
logger.info("Request from {} handled in {:.3f} ms", "127.0.0.1", 33.1)
logger.info("My favorite drinks are {}, {}, {}, {}", "milk", "wine", "tea", "beer")
logger.debug("this is a {} message", logging.getLevelName(logging.DEBUG))
logger.info("this is a {} message", logging.getLevelName(logging.INFO))
logger.warning("this is a {} message", logging.getLevelName(logging.WARNING))
logger.error("this is a {} message", logging.getLevelName(logging.ERROR))
logger.critical("this is a {} message", logging.getLevelName(logging.CRITICAL))
logger.info("Does old-style formatting also work? %s it is, but no colors (yet)", True)

Вывод:

введите описание изображения здесь

Реализация:

"""
colargulog - Python3 Logging with Colored Arguments and new string formatting style

Written by david.ohana@ibm.com
License: Apache-2.0
"""

import logging
import logging.handlers
import re


class ColorCodes:
    grey = "\x1b[38;21m"
    green = "\x1b[1;32m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    blue = "\x1b[1;34m"
    light_blue = "\x1b[1;36m"
    purple = "\x1b[1;35m"
    reset = "\x1b[0m"


class ColorizedArgsFormatter(logging.Formatter):
    arg_colors = [ColorCodes.purple, ColorCodes.light_blue]
    level_fields = ["levelname", "levelno"]
    level_to_color = {
        logging.DEBUG: ColorCodes.grey,
        logging.INFO: ColorCodes.green,
        logging.WARNING: ColorCodes.yellow,
        logging.ERROR: ColorCodes.red,
        logging.CRITICAL: ColorCodes.bold_red,
    }

    def __init__(self, fmt: str):
        super().__init__()
        self.level_to_formatter = {}

        def add_color_format(level: int):
            color = ColorizedArgsFormatter.level_to_color[level]
            _format = fmt
            for fld in ColorizedArgsFormatter.level_fields:
                search = "(%\(" + fld + "\).*?s)"
                _format = re.sub(search, f"{color}\\1{ColorCodes.reset}", _format)
            formatter = logging.Formatter(_format)
            self.level_to_formatter[level] = formatter

        add_color_format(logging.DEBUG)
        add_color_format(logging.INFO)
        add_color_format(logging.WARNING)
        add_color_format(logging.ERROR)
        add_color_format(logging.CRITICAL)

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        msg = record.msg
        msg = msg.replace("{", "_{{")
        msg = msg.replace("}", "_}}")
        placeholder_count = 0
        # add ANSI escape code for next alternating color before each formatting parameter
        # and reset color after it.
        while True:
            if "_{{" not in msg:
                break
            color_index = placeholder_count % len(ColorizedArgsFormatter.arg_colors)
            color = ColorizedArgsFormatter.arg_colors[color_index]
            msg = msg.replace("_{{", color + "{", 1)
            msg = msg.replace("_}}", "}" + ColorCodes.reset, 1)
            placeholder_count += 1

        record.msg = msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        formatter = self.level_to_formatter.get(record.levelno)
        self.rewrite_record(record)
        formatted = formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted


class BraceFormatStyleFormatter(logging.Formatter):
    def __init__(self, fmt: str):
        super().__init__()
        self.formatter = logging.Formatter(fmt)

    @staticmethod
    def is_brace_format_style(record: logging.LogRecord):
        if len(record.args) == 0:
            return False

        msg = record.msg
        if '%' in msg:
            return False

        count_of_start_param = msg.count("{")
        count_of_end_param = msg.count("}")

        if count_of_start_param != count_of_end_param:
            return False

        if count_of_start_param != len(record.args):
            return False

        return True

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        record.msg = record.msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        self.rewrite_record(record)
        formatted = self.formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted

0

Используйте pyfancy .

Пример:

print(pyfancy.RED + "Hello Red!" + pyfancy.END)

Вопрос состоял в том, чтобы настроить loggingфункциональность для использования отдельной библиотеки раскрасок.
Зараженный Дрейк

0

Просто еще одно решение, с цветами ZetaSyanthis:

def config_log(log_level):

    def set_color(level, code):
        level_fmt = "\033[1;" + str(code) + "m%s\033[1;0m" 
        logging.addLevelName( level, level_fmt % logging.getLevelName(level) )

    std_stream = sys.stdout
    isatty = getattr(std_stream, 'isatty', None)
    if isatty and isatty():
        levels = [logging.DEBUG, logging.CRITICAL, logging.WARNING, logging.ERROR]
        for idx, level in enumerate(levels):
            set_color(level, 30 + idx )
        set_color(logging.DEBUG, 0)
    logging.basicConfig(stream=std_stream, level=log_level)

позвоните один раз из вашей __main__функции. У меня там что-то вроде этого:

options, arguments = p.parse_args()
log_level = logging.DEBUG if options.verbose else logging.WARNING
config_log(log_level)

он также проверяет, является ли вывод консолью, иначе цвета не используются.


0
import logging

logging.basicConfig(filename="f.log" filemode='w', level=logging.INFO,
                    format = "%(logger_name)s %(color)s  %(message)s %(endColor)s")


class Logger(object):
    __GREEN = "\033[92m"
    __RED = '\033[91m'
    __ENDC = '\033[0m'

    def __init__(self, name):
        self.logger = logging.getLogger(name)
        self.extra={'logger_name': name, 'endColor': self.__ENDC, 'color': self.__GREEN}


    def info(self, msg):
        self.extra['color'] = self.__GREEN
        self.logger.info(msg, extra=self.extra)

    def error(self, msg):
        self.extra['color'] = self.__RED
        self.logger.error(msg, extra=self.extra)

Применение

Logger("File Name").info("This shows green text")


Для консоли вы можете пропустить имя файла или просто имя файла = '' должно работать. измените basicConfig для включения других свойств, таких как номер файла, модуль ..
estifanos gebrehiwot

0

Следующее решение работает только с Python 3, но для меня это выглядит наиболее ясно.

Идея состоит в том, чтобы использовать фабрику записей журнала, чтобы добавить «цветные» атрибуты для записи объектов записи, а затем использовать эти «цветные» атрибуты в формате журнала.

import logging
logger = logging.getLogger(__name__)

def configure_logging(level):

    # add 'levelname_c' attribute to log resords
    orig_record_factory = logging.getLogRecordFactory()
    log_colors = {
        logging.DEBUG:     "\033[1;34m",  # blue
        logging.INFO:      "\033[1;32m",  # green
        logging.WARNING:   "\033[1;35m",  # magenta
        logging.ERROR:     "\033[1;31m",  # red
        logging.CRITICAL:  "\033[1;41m",  # red reverted
    }
    def record_factory(*args, **kwargs):
        record = orig_record_factory(*args, **kwargs)
        record.levelname_c = "{}{}{}".format(
            log_colors[record.levelno], record.levelname, "\033[0m")
        return record

    logging.setLogRecordFactory(record_factory)

    # now each log record object would contain 'levelname_c' attribute
    # and you can use this attribute when configuring logging using your favorite
    # method.
    # for demo purposes I configure stderr log right here

    formatter_c = logging.Formatter("[%(asctime)s] %(levelname_c)s:%(name)s:%(message)s")

    stderr_handler = logging.StreamHandler()
    stderr_handler.setLevel(level)
    stderr_handler.setFormatter(formatter_c)

    root_logger = logging.getLogger('')
    root_logger.setLevel(logging.DEBUG)
    root_logger.addHandler(stderr_handler)


def main():
    configure_logging(logging.DEBUG)

    logger.debug("debug message")
    logger.info("info message")
    logger.critical("something unusual happened")


if __name__ == '__main__':
    main()

Вы можете легко изменить этот пример для создания других цветных атрибутов (например, message_c), а затем использовать эти атрибуты, чтобы получить цветной текст (только), где вы хотите.

(полезный трюк, который я обнаружил недавно: у меня есть файл с цветными журналами отладки, и всякий раз, когда я хочу временно увеличить уровень журнала моего приложения, я просто tail -fзаписываю файл журнала в другой терминал и вижу на экране журналы отладки без изменения конфигурации и перезапуска приложения. )


0

Это еще один Python3 вариант примера airmind. Мне нужны были некоторые особенности, которых я не видел в других примерах

  • используйте цвета для терминала, но не пишите непечатаемые символы в обработчиках файлов (для этого я определил 2 форматера)
  • возможность переопределить цвет для конкретного сообщения журнала
  • настроить регистратор из файла (в данном случае yaml)

Примечания: я использовал колораму, но вы можете изменить это, так что это не требуется. Также для моего тестирования я просто запустил файл python, так что мой класс находится в модуле. __main__Вы должны были бы перейти (): __main__.ColoredFormatterна любой другой модуль.

pip install colorama pyyaml

logging.yaml

---
version: 1
disable_existing_loggers: False
formatters:
  simple:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
  color:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
    (): __main__.ColoredFormatter
    use_color: true

handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: color
    stream: ext://sys.stdout

  info_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: INFO
    formatter: simple
    filename: app.log
    maxBytes: 20971520 
    backupCount: 20
    encoding: utf8

  error_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: ERROR
    formatter: simple
    filename: errors.log
    maxBytes: 10485760 
    backupCount: 20
    encoding: utf8

root:
  level: DEBUG
  handlers: [console, info_file_handler, error_file_handler]

main.py

import logging
import logging.config
import os
from logging import Logger

import colorama
import yaml
from colorama import Back, Fore, Style

COLORS = {
    "WARNING": Fore.YELLOW,
    "INFO": Fore.CYAN,
    "DEBUG": Fore.BLUE,
    "CRITICAL": Fore.YELLOW,
    "ERROR": Fore.RED,
}


class ColoredFormatter(logging.Formatter):
    def __init__(self, *, format, use_color):
        logging.Formatter.__init__(self, fmt=format)
        self.use_color = use_color

    def format(self, record):
        msg = super().format(record)
        if self.use_color:
            levelname = record.levelname
            if hasattr(record, "color"):
                return f"{record.color}{msg}{Style.RESET_ALL}"
            if levelname in COLORS:
                return f"{COLORS[levelname]}{msg}{Style.RESET_ALL}"
        return msg


with open("logging.yaml", "rt") as f:
    config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)

logger: Logger = logging.getLogger(__name__)
logger.info("Test INFO", extra={"color": Back.RED})
logger.info("Test INFO", extra={"color": f"{Style.BRIGHT}{Back.RED}"})
logger.info("Test INFO")
logger.debug("Test DEBUG")
logger.warning("Test WARN")

вывод:

вывод

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.