ฉันจะให้สีเอาต์พุตการบันทึก Python ได้อย่างไร?


352

เมื่อหลายปีก่อนฉันเห็นแอปพลิเคชั่นโมโนที่มีเอาต์พุตสีน่าจะเป็นเพราะระบบบันทึก (เพราะข้อความทั้งหมดเป็นมาตรฐาน)

ตอนนี้ Python มีloggingโมดูลซึ่งให้คุณระบุตัวเลือกมากมายในการปรับแต่งเอาต์พุต ดังนั้นฉันจึงจินตนาการว่าบางสิ่งที่คล้ายกันอาจเป็นไปได้กับ Python แต่ฉันไม่สามารถหาวิธีที่จะทำมันได้ทุกที่

มีวิธีใดที่จะทำให้ Python แสดงloggingผลเป็นสีได้หรือไม่?

สิ่งที่ฉันต้องการ (เช่น) ข้อผิดพลาดเป็นสีแดงแก้ปัญหาข้อความเป็นสีน้ำเงินหรือสีเหลืองและอื่น ๆ

แน่นอนว่านี่อาจจะต้องใช้เทอร์มินัลที่ใช้งานร่วมกันได้ แต่ฉันสามารถย้อนกลับไปที่loggingเอาต์พุตดั้งเดิมหากสีไม่ได้รับการสนับสนุน

ความคิดใดที่ฉันจะได้รับเอาต์พุตสีด้วยโมดูลการบันทึก?


1
คุณควรระบุว่าคุณต้องการโซลูชันที่มีหลายแพลตฟอร์มทั้ง Linux และ Windows
sorin

1
เกี่ยวข้องถ้าคุณใช้ Eclipse / PyDev: บันทึกสีในคอนโซล eclipse
Tobias Kienzler

5
บางทีคุณอาจใช้colorlog
Ehtesh Choudhury

5
คุณอาจลองใช้โครเมี่ยมที่ฉันเขียนเพื่อสนับสนุนระบบปฏิบัติการและรุ่น Python ทั้งหมด (2.7 และ 3 *)
ereOn

1
โซลูชันที่ถ่ายโอนรหัส ANSI ใน logfile เป็นความคิดที่ไม่ดีพวกเขาจะไล่คุณออกเมื่อคุณโลภสำหรับบางอย่างในเวลาหกเดือน แต่ลืมที่จะอนุญาตให้ใช้ตัวอักษร ANSI ในรูปแบบ regex ของคุณ มีการแก้ปัญหาบางด้านล่างซึ่งเพิ่มสีในขณะที่คุณดูบันทึกมากกว่าที่จะเป็นบันทึกที่เขียนเป็น ...
โจนาธานฮาร์ทลี่

คำตอบ:


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)

และเพื่อใช้สร้าง 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()ก่อนจัดการแอ็ตทริบิวต์ levelname หรือเพื่อรีเซ็ตชื่อระดับเป็นค่าก่อนหน้าก่อนส่งคืนสตริงที่จัดรูปแบบ (ให้เครดิตกับMichaelในความคิดเห็น)


YELLOW, WHITE, BLUE, etc. อยู่ที่ไหน?
Swaroop CH

1
@Swaroop - ผู้ที่มี ANSI รหัสหนีซึ่งคุณสามารถอ่านดูใน Google หรือหาที่นี่: en.wikipedia.org/wiki/ANSI_escape_codeหรือมิฉะนั้นpueblo.sourceforge.net/doc/manual/ansi_color_codes.html
ไบรอัน M . ล่า

53
ผมไม่เชื่อว่าคุณควรสร้าง subclass ตัดไม้เพียงสำหรับการนี้ - คำตอบของคุณจะดีเท่าที่การสร้างที่มีความเชี่ยวชาญและระบุการใช้งานบนFormatter StreamHandlerแต่ไม่จำเป็นต้องมีคลาสย่อยตัวบันทึก อันที่จริงแล้วการใช้คลาสตัวบันทึกเพิ่มตัวจัดการให้กับตัวบันทึกทุกตัวที่สร้างขึ้นซึ่งไม่ใช่สิ่งที่คุณต้องการโดยทั่วไป
Vinay Sajip


6
ColoredFormatterอีกด้านหนึ่งหมายเหตุ มันกำลังเปลี่ยนวัตถุบันทึกซึ่งถูกส่งต่อไปยังตัวจัดการอื่นหรือแพร่กระจายไปยังตัวบันทึกอื่น ๆ หากคุณกำหนดค่าตัวบันทึกไฟล์ ฯลฯ คุณอาจไม่ต้องการมีสีในไฟล์บันทึก เพื่อหลีกเลี่ยงปัญหานี้อาจเป็นการดีที่สุดเพียงสร้างสำเนาของrecordด้วยcopy.copy()ก่อนจัดการแอ็ตทริบิวต์ levelname หรือเพื่อรีเซ็ตค่า levelname เป็นค่าก่อนหน้านี้ก่อนส่งคืนสตริงที่จัดรูปแบบ
Michael

148

หลายปีที่ผ่านมาฉันเขียนโปรแกรมจัดการสตรีมหลากสีสำหรับใช้เอง จากนั้นฉันก็พบหน้านี้และพบชุดของโค้ดขนาดเล็กที่คนคัดลอก / วาง :-( ปัจจุบันตัวจัดการกระแสข้อมูลของฉันใช้งานได้กับ UNIX (Linux, Mac OS X) เท่านั้น แต่ข้อดีคือมีให้ใน PyPI (และGitHub ) และมันใช้งานง่ายจนตายนอกจากนี้ยังมีโหมดไวยากรณ์เป็นกลุ่ม :-) ในอนาคตฉันอาจขยายให้ทำงานบน 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 ระดับบันทึกและข้อความบันทึก นี่คือสิ่งที่ดูเหมือนในทางปฏิบัติ:

สกรีนช็อตของเอาต์พุต colorlogs

หมายเหตุ: เมื่อใช้ 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 " ในกระทู้นี้!
Iosu S.

1
ด้วยเหตุผลบางอย่างฉันให้ได้รับเมื่อใช้AttributeError: 'module' object has no attribute 'install' coloredlogs.install()คุณช่วยยืนยันด้วยเวอร์ชั่นล่าสุดได้ไหม
con-f-use

11
มันดูสวยงาม น่าเสียดายที่มันทำลายหลายสิ่งหลายอย่าง โดยเฉพาะอย่างยิ่งจะทำให้การโทร voip ไปที่ logging.basicConfig สิ่งนี้ทำให้ไม่สามารถใช้ตัวจัดรูปแบบที่กำหนดเองได้ตัวอย่างเช่น
Clément

@ Clément: คำถามสอง (ซ้อนทับกัน) คำถาม: (1) คุณหมายถึงอะไรโดย "voids โทรไปที่ logging.basicConfig" และ (2) ทางเลือกอื่นจะเป็นอย่างไร ทั้งสองlogging.basicConfig()และcoloredlogs.install()ติดตั้งตัวจัดการกระแสที่บันทึกไปยังคอนโซลดังนั้นโดยไม่ต้อง "โมฆะ" คุณจะได้รับข้อความซ้ำ ...
xolox

ฉันคาดว่าจะมีเวทย์มนตร์สำหรับ (1) หรือ (มีเหตุผลมากกว่า) วิธีบอกcoloredlogs.installรูปแบบที่จะใช้เช่นเดียวกับในcolorlogแพ็คเกจ
Clément

74

นี่คือทางออกที่ควรทำงานบนแพลตฟอร์มใด ๆ ถ้ามันไม่เพียงแค่บอกฉันและฉันจะอัปเดต

มันทำงานอย่างไร: บนแพลตฟอร์มที่รองรับการหลบหนี ANSI กำลังใช้มัน (ไม่ใช่ Windows) และบน Windows มันใช้การเรียก API เพื่อเปลี่ยนสีคอนโซล

สคริปต์ทำการแฮ็ควิธีการ logging.StreamHandler.emit จากไลบรารี่มาตรฐานเพิ่ม wrapper เข้าไป

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ควรจะเป็น
Rasika Perera

ฉันชอบวิธีนี้ ใช้มันในปัจจุบัน ฉันเห็นว่ามีแอตทริบิวต์ _set_color มีวิธีทำเช่นนี้สำหรับข้อความบันทึกเฉพาะหรือไม่ แก้ไขโอ้เห็นว่าเป็นเพียงแพทช์สำหรับเครื่อง windows จะดีที่จะเพิ่มกำหนดเองสำหรับกรณีการใช้งานที่แตกต่างกัน
brizz

+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 Scheper

ฉันเพิ่งโหวตขึ้นนี้เพื่อดูตัวอย่างข้อความ OUTPUT ^^
Agustin Barrachina

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; ลองใช้การบันทึกคำเตือน ('นี่คือการทดสอบ') คุณจะเห็นส่วนที่เป็นตัวพิมพ์ใหญ่ของ "คำเตือน: นี่คือการทดสอบ" ที่มีสี มันทำงานบนลินุกซ์เท่านั้น btw
Riccardo Galli

3
เนื่องจากชื่อ loglevel เท่านั้นที่มีสีคุณต้องแน่ใจว่าชื่อ loglevel ถูกพิมพ์ไปยังคอนโซลเลย สิ่งนี้ไม่ได้เกิดขึ้นนอกกรอบสำหรับฉัน บางสิ่งในสายเหล่านี้จะช่วยได้: logging.basicConfig(format='%(asctime)s [%(name)s] [%(levelname)s] %(message)s')แน่นอนว่า%(levelnames)sสิ่งสำคัญคืออะไร
เซบาสเตียน

4
วิธีที่ง่ายและสะอาดที่สุดในการประยุกต์ใช้และทำความเข้าใจ
F. Santiago

1
เพียงลองในคอนโซล Linux echo -e "Normal texst \033[1;31mred bold text\033[0m normal text again". -eตัวเลือกecho ตีความ "\ 033" เป็นรูปแบบแปดของสัญลักษณ์ Escape ASCII สัญลักษณ์พิเศษนี้ทำให้เทอร์มินัลที่ใช้งานร่วมกันได้ตีความตัวละครที่ตามมา (เพื่อmรวมเป็นถ่าน) เป็นคำสั่งพิเศษ en.wikipedia.org/wiki/ANSI_escape_code
eugene-bright

1
ปรับปรุงไมเนอร์: if sys.sdterr.isatty():ใส่ในรหัสนี้ ในกรณีนี้หากคุณเปลี่ยนเส้นทางเอาต์พุตไปยังไฟล์ไฟล์จะไม่มีอักขระยกเว้นเหล่านี้
เลนิ

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

ผลลัพธ์ ป้อนคำอธิบายรูปภาพที่นี่

โทนสีเต็ม ป้อนคำอธิบายรูปภาพที่นี่

สำหรับ windows

วิธีนี้ใช้ได้กับ 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
ฉันชอบคำตอบนี้มากจนฉันทำrepoด้วยการเพิ่มทีละนิดและสี ansi
Teodoro

@constructor คุณเรียกใช้ที่ไหน คอนโซล IDE ขั้ว windows?
Sergey Pleshakov

@ Joe สิ่งที่ไม่ทำงานอย่างแน่นอน? สภาพแวดล้อมของคุณคืออะไรและคุณได้รับข้อผิดพลาดอะไร ฉันต้องการแก้ไขวิธีแก้ปัญหาเพื่อให้สามารถใช้งานได้บนทุกแพลตฟอร์ม
Sergey Pleshakov

17

ฉันเดาว่าฉันอาจเพิ่มรูปแบบตัวบันทึกสีของฉันด้วย

นี่คืออะไรแฟนซี แต่มันง่ายมากที่จะใช้และไม่เปลี่ยนวัตถุบันทึกดังนั้นหลีกเลี่ยงการเข้าสู่ระบบลำดับหนี 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


2
BTW เพื่อเพิ่มสีให้กับข้อความนั้นเพียงเพิ่มบรรทัดนี้ก่อนreturn:colored_record.msg = ('{0}{1}m{2}{3}').format(self.PREFIX, seq, colored_record.getMessage(), self.SUFFIX)
Godfather

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ใช้เฉพาะกับงูหลามโบราณบางรุ่นที่ฉันเดา? เนื่องจากคำตอบนี้มาจาก 2010 มันทำงานได้ดีสำหรับฉันด้วย Python 2.7
Joakim

14

คุณสามารถนำเข้าโมดูลcolorlogและใช้ColoredFormatterสำหรับ colorizing log messages

ตัวอย่าง

หม้อไอน้ำสำหรับโมดูลหลัก:

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 sequences ที่ถูกเขียนไปยังไฟล์เมื่อเอาต์พุตของบันทึกการทำงานถูกเปลี่ยนทิศทาง

นอกจากนี้ชุดรูปแบบสีแบบกำหนดเองยังเหมาะสำหรับเทอร์มินัลที่มีพื้นหลังสีเข้ม

ตัวอย่างการบันทึกการโทร:

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 (ตามที่ระบุไว้, ต้องขึ้นอยู่กับ colorama) ถึงอย่างนั้นฉันก็มีปัญหาที่จะให้มันทำงานใน Anaconda / Spyder env คุณอาจต้องระบุ colorama.init (strip = False) เช่นใน escape_code.py (ตามที่ระบุในหัวข้อนี้github.com/spyder-ide/spyder/issues/1917 )
Matt-Mac-Muffin

11

ดูวิธีแก้ปัญหาต่อไปนี้ สตรีมตัวจัดการควรเป็นสิ่งที่ทำสีแล้วคุณมีตัวเลือกของคำสีมากกว่าแค่ทั้งบรรทัด (ด้วยตัวจัดรูปแบบ)

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


คุณสามารถค้นหาการใช้งานที่ปรับปรุงแล้วในส่วนสำคัญนี้(ดูแลโดยผู้เขียนบล็อก) ฉันใช้มันและใช้งานได้ดี ขอบคุณสำหรับการแบ่งปัน.
ส่งเสียงดัง

11

ฉันปรับเปลี่ยนตัวอย่างดั้งเดิมที่จัดทำโดย Sorin และ StreamHandler แบบ subclassed เป็น ColorizedConsoleHandler

ข้อเสียของการแก้ปัญหาของพวกเขาคือการปรับเปลี่ยนข้อความและเพราะนั่นคือการปรับเปลี่ยน logmessage ที่เกิดขึ้นจริงใด ๆ ตัวจัดการอื่น ๆ จะได้รับข้อความที่ถูกปรับเปลี่ยนเช่นกัน

สิ่งนี้ทำให้ logfiles มีรหัสสีในกรณีของเราเพราะเราใช้ตัวบันทึกหลายตัว

คลาสด้านล่างนี้ใช้งานได้บนแพลตฟอร์มที่รองรับ 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

  • รองรับ Django

  • สีที่ปรับแต่งได้

เนื่องจากสิ่งนี้ถูกแจกจ่ายเป็นไข่งูหลามมันง่ายมากที่จะติดตั้งสำหรับแอปพลิเคชัน 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โต้แย้งกับ dynamicaly ตั้งค่าสีของข้อความโดยใช้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 ของ regex จะถูกระบายสีด้วย color1, กลุ่ม 2 กับ color2 เป็นต้น

ตัวอย่างเช่น:

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

เช่นกลุ่ม regex แรก (parens) ตรงกับวันที่เริ่มต้นใน logfile กลุ่มที่สองตรงกับชื่อไฟล์ไพ ธ อนหมายเลขบรรทัดและชื่อฟังก์ชันและกลุ่มที่สามตรงกับข้อความบันทึกที่เกิดขึ้นหลังจากนั้น ฉันยังใช้ลำดับแบบขนานของ 'ตัวหนา / บรรทัดฐาน' เช่นเดียวกับลำดับของสี ดูเหมือนว่า:

logfile ด้วยการจัดรูปแบบสี

โปรดทราบว่าเส้นหรือส่วนต่าง ๆ ของบรรทัดที่ไม่ตรงกับ regex ใด ๆ ของฉันยังคงถูกสะท้อนดังนั้นสิ่งนี้จึงไม่เหมือนกับ 'grep --color' - ไม่มีการกรองสิ่งใดออกจากผลลัพธ์

เห็นได้ชัดว่านี่คือความยืดหยุ่นเพียงพอที่คุณสามารถใช้กับกระบวนการใด ๆ ไม่ใช่แค่ปรับแต่ง logfiles ฉันมักจะชัก regex ใหม่ได้ทันทีทุกครั้งที่ฉันต้องการบางสิ่งบางอย่างสี ด้วยเหตุนี้ฉันชอบ colout กับเครื่องมือกำหนดค่าสีไฟล์ล็อกไฟล์แบบกำหนดเองเพราะฉันเพียงต้องการเรียนรู้เครื่องมือเดียวโดยไม่คำนึงถึงสิ่งที่ฉันกำลังทำสี: การบันทึกการทดสอบผลลัพธ์การทดสอบการเน้นไวยากรณ์ของโค้ดในเทอร์มินัล ฯลฯ

นอกจากนี้ยังหลีกเลี่ยงการทิ้งรหัส ANSI ใน logfile เองซึ่ง IMHO เป็นความคิดที่ไม่ดีเพราะมันจะทำลายสิ่งต่าง ๆ เช่น grepping สำหรับรูปแบบใน logfile ยกเว้นว่าคุณจำเสมอเพื่อให้ตรงกับรหัส ANSI ใน grep regex ของคุณ


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 หายไปเช่นกัน) แต่ได้เพิ่มบางอย่าง สิ่งที่มีปัญหามากที่สุดคือการทราบวิธีแนบฟอร์แมตเตอร์อย่างถูกต้อง
Nick

ดูวิธีแก้ปัญหา "แจ็คช่างประปา" ฉันคิดว่านี่เป็นวิธีที่ดีกว่าในการแก้ปัญหา (เช่นตัวจัดการควรทำ colourisation) 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หรือเต็มบรรทัดที่พิมพ์สองครั้ง?
Pithikos

ขออภัยสำหรับความคิดเห็นที่สั้นลง อดีตเกิดขึ้น: [17:01:36]: คำเตือน: นี่ควรเป็นสีเหลือง \ n นี่ควรเป็นสีเหลือง อย่างไรก็ตามฉันต้องการให้แสดงเฉพาะการจัดรูปแบบเท่านั้นมิฉะนั้นจะดูเหมือนขยะเนื่องจากบันทึกที่ซ้ำซ้อน
Validus Oculus

@ MuratKarakuşไม่แน่ใจว่าทำไมสิ่งนี้เกิดขึ้นโดยไม่ต้องมีมุมมองแบบเต็มเกี่ยวกับการใช้งาน หากคุณกำลังใช้ตัวบันทึกที่กำหนดเองบางทีคุณกำลังเข้าไปแทรกแซงบางจุด? การแก้ไขอย่างรวดเร็วอาจจะเอามาจาก7s:%(message)s log_format
Pithikos

1

ฉันมีสองการส่งเพื่อเพิ่มหนึ่งในนั้น colorizes เพียงข้อความ (ColoredFormatter) และหนึ่งในนั้น colorizes ทั้งบรรทัด (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

เป็นอีกหนึ่งวิธีแก้ปัญหาด้วยสีของ 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()

คุณสามารถแก้ไขตัวอย่างนี้ได้อย่างง่ายดายเพื่อสร้างคุณสมบัติสีอื่น ๆ (fe message_c) จากนั้นใช้คุณสมบัติเหล่านี้เพื่อรับข้อความสี (เฉพาะ) ที่คุณต้องการ

(เคล็ดลับที่มีประโยชน์ฉันค้นพบเมื่อเร็ว ๆ นี้: ฉันมีไฟล์ที่มีบันทึกการดีบักสีและเมื่อใดก็ตามที่ฉันต้องการเพิ่มระดับการบันทึกของแอปพลิเคชันของฉันชั่วคราวฉันเพียงแค่tail -fไฟล์บันทึกในเทอร์มินัลที่แตกต่างกัน )


0

นี่เป็นอีกตัวอย่างหนึ่งของตัวแปร Python3 ของ Airmind ฉันต้องการคุณสมบัติบางอย่างที่ฉันไม่เห็นในตัวอย่างอื่น

  • ใช้สีสำหรับเทอร์มินัล แต่อย่าเขียนตัวอักษรที่ไม่สามารถพิมพ์ได้ในตัวจัดการไฟล์ (ฉันกำหนดตัวฟอร์แมต 2 ตัวสำหรับสิ่งนี้)
  • ความสามารถในการแทนที่สีสำหรับข้อความบันทึกเฉพาะ
  • กำหนดค่า logger จากไฟล์ (yaml ในกรณีนี้)

หมายเหตุ: ฉันใช้coloramaแต่คุณสามารถแก้ไขได้ดังนั้นจึงไม่จำเป็น นอกจากนี้สำหรับการทดสอบของฉันฉันเพิ่งเรียกใช้ไฟล์หลามดังนั้นคลาสของฉันอยู่ในโมดูล__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")

เอาท์พุท:

เอาท์พุต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.