[python] 파이썬 로깅 출력을 어떻게 채색 할 수 있습니까?

얼마 전, 로그 시스템 때문에 모든 출력이 표준화 되었기 때문에 컬러 출력의 모노 응용 프로그램을 보았습니다.

이제 파이썬에는 logging모듈이 있는데,이를 통해 출력을 사용자 정의하기위한 많은 옵션을 지정할 수 있습니다. 그래서 파이썬에서 비슷한 것이 가능하다고 상상하지만 어디서나이 작업을 수행하는 방법을 찾을 수 없습니다.

파이썬 logging모듈을 컬러로 출력 하는 방법이 있습니까?

내가 원하는 것 (예를 들어) 빨간색으로 오류, 파란색 또는 노란색으로 메시지를 디버그하는 등.

물론 이것은 아마도 호환 가능한 터미널을 필요로 할 것입니다 (대부분의 최신 터미널은). logging색상이 지원되지 않으면 원래 출력으로 대체 될 수 있습니다.

로깅 모듈로 컬러 출력을 얻는 방법에 대한 아이디어가 있습니까?



답변

나는 이미 색 이스케이프에 대해 알고 있었고 얼마 전에 내 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)

그리고 그것을 사용하려면 자신의 로거를 만드십시오.

# 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. 레코드 오브젝트를 변경하는 중입니다. 레코드 오브젝트는 다른 핸들러로 전달되거나 다른 로거로 전파됩니다. 파일 로거 등을 구성한 경우 로그 파일에 색상을 원하지 않을 수 있습니다. 이를 피하려면 , formatname 속성을 조작하기 전에 recordwith 의 사본을 작성 copy.copy()하거나 형식화 된 문자열 ( 주석에서 Michael 에게 신용)을 리턴하기 전에 levelname을 이전 값으로 재설정하는 것이 가장 좋습니다 .


답변

몇 년 전 나는 자신이 사용할 컬러 스트림 처리기를 썼습니다. 그런 다음이 페이지를 방문하여 사람들이 복사 / 붙여 넣는 코드 스 니펫 모음을 발견했습니다 .- (. 스트림 처리기는 현재 UNIX (Linux, Mac OS X)에서만 작동하지만 이점은 PyPI (및 GitHub) 에서 사용할 수 있다는 것입니다 ) 그리고 사용하기가 간단하며 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, 로그 레벨 및 로그 메시지가 포함됩니다. 실제로는 다음과 같습니다.

coloredlogs 출력 스크린 샷

참고 : MinTTY와 함께 Git Bash를 사용하는 경우

Windows의 Git Bash에는 Winpty 및 Git Bash와 같은 문서화 된 단점이 있습니다.

ANSI 이스케이프 코드 및 ncurses 스타일 문자 재 작성 및 애니메이션의 경우 명령 앞에 접두사를 추가해야합니다 winpty.

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


답변

다음은 모든 플랫폼에서 작동해야하는 솔루션입니다. 그것이 단지 나에게 말하지 않으면 업데이트 할 것입니다.

작동 방식 : 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())


답변

업데이트 : 이것은 오랫동안 스크래치로 생각했던 가려움증이기 때문에 간단한 방법으로 일을하고 싶은 저 같은 게으른 사람들을위한 라이브러리를 작성했습니다 .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")

산출:

컬러 로그 출력


답변

새 클래스를 정의하지 않고 사전 정의 된 로그 수준을위한 빠르고 더러운 솔루션입니다.

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


답변

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/을 시도하지 않은 방법을 활성화하는 방법은 다음과 같습니다.


답변

글쎄, 나는 컬러 로거의 변형을 추가 할 수도 있다고 생각합니다.

이것은 멋진 것은 아니지만 사용하기가 매우 쉽고 레코드 객체를 변경하지 않으므로 파일 핸들러가 사용되는 경우 ANSI 이스케이프 시퀀스를 로그 파일에 기록하지 않습니다. 로그 메시지 형식에는 영향을 미치지 않습니다.

로깅 모듈의 포맷터를 이미 사용중인 경우 색상 레벨 이름을 얻으려면 상담원 핸들러 포맷터를 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