Як я можу забарвити вихід журналу Python?


352

Деякий час тому я побачив додаток Mono з кольоровим виходом, імовірно, через його систему журналів (оскільки всі повідомлення були стандартизовані).

Тепер у Python є loggingмодуль, який дозволяє задати безліч варіантів налаштування виводу. Отже, я гадаю, що щось подібне було б можливо з Python, але я не можу дізнатися, як це зробити де-небудь.

Чи є якийсь спосіб зробити loggingвисновок модуля Python кольоровим?

Що я хочу (наприклад) помилок у червоному, повідомлення про налагодження синього або жовтого кольору тощо.

Звичайно, для цього, мабуть, знадобиться сумісний термінал (більшість сучасних терміналів є); але я міг би повернутися до вихідного loggingвиводу, якщо колір не підтримується.

Будь-які ідеї, як я можу отримати кольоровий вихід за допомогою модуля реєстрації?


1
Вам слід вказати, що потрібно багатоплатформене рішення - і для Linux, і для Windows.
sorin

1
Пов’язано, якщо ви використовуєте Eclipse / PyDev: Розфарбуйте журнали на консолі затемнення
Тобіас Кіензлер

5
Можливо, ви також можете скористатися кольоровим журналом
Ehtesh Choudhury

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

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

Відповіді:


192

Я вже знав про кольорові втечі, я використовував їх у своїй підказці підказки деякий час тому. Все одно, дякую.
Я хотів інтегрувати його з модулем реєстрації даних, що я зробив зрештою після пари спроб та помилок.
Ось що я закінчую:

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)

І щоб використовувати його, створіть свій власний реєстратор:

# 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()перш ніж маніпулювати атрибутом levelname, або скинути ім'я рівня до попереднього значення, перш ніж повернути відформатований рядок (заслуга Майкла в коментарях).


Де визначено ЖОВТИЙ, БІЛИЙ, СВІТИЙ тощо?
Swaroop CH

1
@Swaroop - це ANSI-коди втечі, які ви можете прочитати , шукаючи в Google, або знайдіть тут: en.wikipedia.org/wiki/ANSI_escape_code або альтернативно pueblo.sourceforge.net/doc/manual/ansi_color_codes.html
Брайан М Полювання

53
Я не вірю, що ви повинні створити підклас реєстратора саме для цього - ваша відповідь чудова, що стосується створення спеціалізованого Formatterта уточнення його використання на StreamHandler. Але немає необхідності в підкласі реєстратора. Насправді використання класу реєстраторів додає обробник до кожного створеного реєстратора, що зазвичай не є тим, чого ви хочете.
Vinay Sajip


6
Одна сторона примітка до ColoredFormatter. Це зміна об'єкта запису, який передається далі іншим обробникам або розповсюджується на інші реєстратори. Якщо ви налаштували файлові реєстратори тощо, ви, ймовірно, не хочете мати кольори у файлах журналу. Щоб уникнути цього, мабуть, найкраще просто створити копію recordз, copy.copy()перш ніж маніпулювати атрибутом levelname, або скинути ім'я рівня до попереднього значення, перш ніж повернути відформатований рядок.
Майкл

148

Роки тому я написав кольоровий обробник потоку для власного використання. Потім я натрапив на цю сторінку і знайшов колекцію фрагментів коду, яку люди копіюють / вставляють :-(. Мій обробник потоків наразі працює лише на 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 w / 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

@ 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

Це призводить до кольорових кодів на початку та в кінці повідомлення журналу у фактичних файлах журналу.
МехмедБ

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

Вихід:

Колористичний вихід


4
Чудова відповідь; +1. Приклад коду можна привести до кінця (чи setLevelсправді потрібні три дзвінки ?)
Clément

1
Я сподівався, що знайду відповідь на зразок цієї, якби я досить довго перебирав відповіді. ☺ Я сподіваюся, що @airmind розгляне прийняття цієї відповіді, тому майбутні роботящі люди зможуть знайти найкращу бібліотеку з оптимальною ліністю. 😉
Майкл

Я щойно підтримав це для прикладів повідомлень про
вихід →

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 тільки btw
Ріккардо Галлі

3
Оскільки кольоровим є лише ім'я loglevel, ви повинні переконатися, що ім'я loglevel надруковано для консолі. Це не відбувається поза межами мене. Щось у цьому напрямку допоможе: 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". -eпараметр ехо інтерпретувати "\ 033" як восьмеричну форму символу Escape ASCII. Цей спеціальний символ змушує деякі сумісні термінали інтерпретувати наступні символи (до символу mвключно) як спеціальні команди. en.wikipedia.org/wiki/ANSI_escape_code
eugene -right

1
Незначне вдосконалення: введіть цей код всередину if sys.sdterr.isatty():. У цьому випадку, якщо ви перенаправляєте вихід у файл, файл не буде містити цих символів.
lesnik

35

Код 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
Мені ця відповідь настільки сподобалася, що я зробив репо за це, з декількома кроками та шпаргалкою з анді-кольорів.
Теодоро

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

@Joe, що саме не працює? яке ваше оточення та які помилки ви отримуєте? Я хотів би переглянути рішення, щоб воно працювало на різних платформах
Сергій Плешаков

17

Ну, я думаю, що я міг би також додати свою варіацію кольорового реєстратора.

Це нічого фантазійного, але він дуже простий у використанні і не змінює об'єкт запису, тим самим уникнути внесення ANSI послідовностей виходу у файл журналу, якщо використовується обробник файлів. Це не впливає на форматування повідомлення журналу.

Якщо ви вже використовуєте Formatter модуля реєстрації , все, що вам потрібно зробити, щоб отримати кольорові імена рівнів, - це замінити ваших інструментів обробника консультантів Formatter на ColoredFormatter. Якщо ви реєструєте весь додаток, вам потрібно зробити це лише для реєстратора верхнього рівня.

color_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
BTW, щоб додати кольори до самого повідомлення, просто додайте цей рядок раніше 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

Ви можете імпортувати модуль кольорового журналу та використовувати його 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__)

Код вмикає кольори лише в повідомленнях журналу, якщо встановлений модуль кольорових кольорів і якщо висновок фактично переходить до терміналу. Це дозволяє уникнути запису послідовностей втечі у файл, коли вихід журналу буде перенаправлений.

Крім того, призначена спеціальна кольорова схема, яка краще підходить для терміналів з темним фоном.

Деякі приклади реєстрації дзвінків:

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
Для запису кольоровий журнал не завжди працює безпосередньо на платформах Windows (як зазначено, потрібна залежність від кольорів). Навіть з цим у мене виникли проблеми змусити його працювати в Anaconda / Spyder env. Можливо, вам потрібно буде вказати colorama.init (strip = False), наприклад, у escape_code.py (як зазначено в цій темі github.com/spyder-ide/spyder/isissue/1917 )
Matt-Mac-Muffin

11

Подивіться на наступне рішення. Обробник потоку повинен бути тим, що робить забарвлення, тоді ви маєте можливість фарбувати слова, а не просто весь рядок (з Форматтером).

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)

EDIT: Ось ось адаптований декоратор для встановлення кольорів під час виконання:

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 регулярного вираження, буде забарвлений кольором1, група 2 кольором2 тощо.

Наприклад:

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

тобто перша група регулярних виразів (паронів) відповідає початковій даті в лог-файлі, друга група відповідає імені файлу python, номеру рядка та імені функції, а третя група відповідає повідомленню журналу, яке надходить після цього. Я також використовую паралельну послідовність "жирних / нормальних", а також послідовність кольорів. Це виглядає так:

журнал файлів з кольоровим форматуванням

Зауважте, що рядки або частини рядків, які не відповідають жодному моєму регулярному вираженню, все ще перегукуються, тому це не схоже на "grep --color" - нічого не фільтрується з виводу.

Очевидно, що це досить гнучко, що ви можете використовувати його з будь-яким процесом, а не лише з підключенням логістів. Я, як правило, просто збиваю нову виразку на льоту, коли б я хотів щось розфарбувати. З цієї причини я віддаю перевагу розфарбуванню будь-якому користувальницькому інструменту забарвлення файлів, тому що мені потрібно вивчити лише один інструмент, незалежно від того, що я забарвлюю: ведення журналів, тестовий вихід, виділення синтаксису, фрагменти коду в терміналі тощо.

Це також дозволяє уникнути фактичного скидання ANSI-кодів у сам файл реєстрації, що IMHO - це погана ідея, оскільки він порушить такі речі, як прив'язка до шаблонів у лог-файлі, якщо ви завжди не пам’ятаєте відповідати кодам ANSI у своєму греп-регексе.


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в прикладі.


коли я ним користуюся, повідомлення надрукуються двічі. ти знаєш чому?
Валідус Окулус

@ ти міг би допрацювати? А саме ви маєте на увазі щось на кшталт [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.


З нетерпінням чекаю на новий піар, щоб зменшити захаращення шляху модуля
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

Використовуйте піфантність .

Приклад:

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


Для консолі ви можете залишити ім'я файлу або просто filename = '' має працювати. змінити 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()

Ви можете легко змінити цей приклад, щоб створити інші кольорові атрибути (fe message_c), а потім використовувати ці атрибути, щоб отримати кольоровий текст (лише) там, де вам потрібно.

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


0

Це ще один варіант прикладу повітряного розуму Python3. Я хотів певних особливостей, яких я не бачив в інших прикладах

  • використовувати кольори для терміналу, але не писати символи, що не друкуються, у файлових обробниках (для цього я визначив 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.