Bagaimana saya bisa mewarnai keluaran pendataan Python?


353

Beberapa waktu yang lalu, saya melihat aplikasi Mono dengan output berwarna, mungkin karena sistem log-nya (karena semua pesan distandarisasi).

Sekarang, Python memiliki loggingmodul, yang memungkinkan Anda menentukan banyak opsi untuk menyesuaikan output. Jadi, saya membayangkan sesuatu yang serupa mungkin terjadi dengan Python, tetapi saya tidak bisa mengetahui bagaimana melakukan ini di mana saja.

Apakah ada cara untuk membuat loggingoutput modul Python berwarna?

Apa yang saya inginkan (misalnya) kesalahan warna merah, pesan debug berwarna biru atau kuning, dan sebagainya.

Tentu saja ini mungkin membutuhkan terminal yang kompatibel (sebagian besar terminal modern); tapi saya bisa mundur ke loggingoutput asli jika warna tidak didukung.

Adakah ide bagaimana saya bisa mendapatkan hasil berwarna dengan modul logging?


1
Anda harus menentukan bahwa Anda menginginkan solusi multiplatform - Linux dan Windows.
sorin

1
Terkait jika Anda menggunakan Eclipse / PyDev: Colorize logs in eclipse console
Tobias Kienzler

5
Mungkin Anda juga dapat menggunakan colorlog
Ehtesh Choudhury

5
Anda juga dapat mencoba chromalog yang saya tulis untuk mendukung semua sistem operasi dan versi Python (2.7 dan 3. *)
sebelum

1
Solusi yang benar-benar membuang kode ANSI di logfile adalah ide yang buruk, mereka akan menangkap Anda ketika Anda mencari sesuatu dalam waktu enam bulan tetapi lupa untuk memungkinkan karakter ANSI dalam pola regex Anda. Ada beberapa solusi di bawah ini yang menambah warna saat Anda melihat log, daripada ketika log ditulis ...
Jonathan Hartley

Jawaban:


192

Saya sudah tahu tentang warna yang keluar, saya menggunakannya dalam bash prompt saya beberapa waktu yang lalu. Bagaimanapun, terima kasih.
Yang saya inginkan adalah mengintegrasikannya dengan modul logging, yang akhirnya saya lakukan setelah beberapa percobaan dan kesalahan.
Inilah yang akhirnya saya dapatkan:

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)

Dan untuk menggunakannya, buat Logger Anda sendiri:

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

Kalau-kalau ada orang lain yang membutuhkannya.

Hati-hati jika Anda menggunakan lebih dari satu logger atau handler: ColoredFormattermengubah objek rekaman, yang diteruskan lebih jauh ke handler lain atau disebarkan ke logger lain. Jika Anda telah mengkonfigurasi file logger dll. Anda mungkin tidak ingin memiliki warna dalam file log. Untuk menghindari itu, mungkin yang terbaik adalah cukup membuat salinan recorddengan copy.copy()sebelum memanipulasi atribut levelname, atau untuk mereset levelname ke nilai sebelumnya, sebelum mengembalikan string yang diformat (kredit ke Michael dalam komentar).


Di mana didefinisikan KUNING, PUTIH, BIRU, dll.?
Swaroop CH

1
@Swaroop - Itu adalah kode pelarian ANSI, yang dapat Anda baca cari di Google, atau temukan di sini: en.wikipedia.org/wiki/ANSI_escape_code , atau sebagai alternatif pueblo.sourceforge.net/doc/manual/ansi_color_codes.html
Brian M Perburuan

53
Saya tidak percaya bahwa Anda harus membuat subkelas logger hanya untuk ini - jawaban Anda baik sejauh membuat khusus Formatterdan menentukan penggunaannya pada a StreamHandler. Tetapi tidak perlu untuk subkelas logger. Bahkan penggunaan kelas logger menambahkan handler ke setiap logger yang dibuat, yang bukan apa yang biasanya Anda inginkan.
Vinay Sajip


6
Catatan satu sisi untuk ColoredFormatter. Ini mengubah objek rekaman, yang diteruskan lebih lanjut ke penangan lain atau disebarkan ke penebang lain. Jika Anda telah mengkonfigurasi file logger dll. Anda mungkin tidak ingin memiliki warna dalam file log. Untuk menghindari itu, mungkin yang terbaik, cukup membuat salinan recorddengan copy.copy()sebelum memanipulasi atribut levelname, atau untuk mereset levelname ke nilai sebelumnya, sebelum mengembalikan string yang diformat.
Michael

149

Bertahun-tahun lalu saya menulis stream handler berwarna untuk saya gunakan sendiri. Lalu saya menemukan halaman ini dan menemukan kumpulan potongan kode yang orang salin / tempel :-(. Pengalih aliran saya saat ini hanya berfungsi di UNIX (Linux, Mac OS X) tetapi keuntungannya adalah bahwa itu tersedia di PyPI (dan GitHub ) dan itu sangat mudah digunakan. Ini juga memiliki mode sintaks Vim :-). Di masa depan saya mungkin memperpanjang untuk bekerja di Windows.

Untuk menginstal paket:

$ pip install coloredlogs

Untuk mengonfirmasi bahwa itu berfungsi:

$ coloredlogs --demo

Untuk memulai dengan kode Anda sendiri:

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

Format log default yang ditunjukkan pada contoh di atas berisi tanggal, waktu, nama host, nama logger, PID, level log dan pesan log. Dalam praktiknya seperti ini:

Cuplikan layar dari output log berwarna

CATATAN: Saat menggunakan Git Bash dengan MinTTY

Git Bash di windows memiliki beberapa kebiasaan yang terdokumentasi: Winpty dan Git Bash

Yang untuk kode melarikan diri ANSI dan untuk penulisan ulang karakter gaya ncurses dan animasi, Anda harus mengawali perintah dengan winpty.

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

2
cukup lucu, saya hanya akan menambahkan tautan ke " pypi.python.org/pypi/coloredlogs/0.4.7 " di utas ini!
Iosu S.

1
Untuk beberapa alasan saya terus mendapatkan AttributeError: 'module' object has no attribute 'install'saat menggunakan coloredlogs.install(). Bisakah Anda mengonfirmasinya dengan versi terbaru.
con-f-use

11
Ini memang terlihat indah. Sayangnya, itu merusak banyak hal; khususnya, ini mengosongkan panggilan ke logging.basicConfig. Ini membuat mustahil untuk menggunakan formatter khusus, misalnya.
Clément

@ Clément: Dua (tumpang tindih?) Pertanyaan: (1) Apa yang Anda maksud dengan "void calls to logging.basicConfig" dan (2) apa alternatifnya? Baik logging.basicConfig()dan coloredlogs.install()instal stream handler yang masuk ke konsol, jadi tanpa "membatalkan" Anda akan mendapatkan pesan duplikat ...
xolox

Saya mengharapkan sihir untuk (1), atau (lebih masuk akal) cara untuk mengetahui coloredlogs.installformat mana yang akan digunakan, seperti dalam colorlogpaket.
Clément

74

Ini adalah solusi yang bisa digunakan pada platform apa pun. Jika tidak hanya memberi tahu saya dan saya akan memperbaruinya.

Cara kerjanya: pada platform yang mendukung ANSI melarikan diri menggunakannya (non-Windows) dan pada Windows itu menggunakan panggilan API untuk mengubah warna konsol.

Script tidak hack metode logging.StreamHandler.emit dari pustaka standar menambahkan pembungkus untuk itu.

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
Saya menulis kelas StreamHandler berdasarkan ini, lihat gist.github.com/mooware/a1ed40987b6cc9ab9c65 .
mooware

2
ini berhasil untuk saya! baris 90: seharusnya args[1].msg = color + str(args[1].msg) + '\x1b[0m' # normal.
Rasika Perera

Saya suka solusi ini. menggunakannya saat ini. Saya melihat ada atribut _set_color, apakah ada cara untuk melakukan ini untuk pesan log tertentu? sunting , oh lihat itu hanya tambalan untuk mesin windows. akan menyenangkan untuk menambahkan kustom untuk berbagai kasus penggunaan.
brizz

+1 untuk warna ANSI. Dalam xterm Anda bahkan bisa mendapatkan 256 warna sekaligus dan Anda dapat mendefinisikan palet secara dinamis! Namun, perhatikan bahwa semua panggilan ke fungsi logging harus dalam definisi fungsi untuk menghindari kemungkinan masalah kunci impor saat masuk di luar definisi fungsi . Sebagian besar kode Anda terlihat bagus; Hanya sedikit yang TestColorer.pymembuatku khawatir.
personal_cloud

Ini menghasilkan kode warna pada awal dan akhir pesan log dalam file log aktual.
MehmedB

74

Pembaruan : Karena ini adalah gatal yang sudah lama ingin saya garuk, saya melanjutkan dan menulis perpustakaan untuk orang-orang malas seperti saya yang hanya ingin cara sederhana untuk melakukan sesuatu: zenlog

Colorlog sangat bagus untuk ini. Ini tersedia di PyPI (dan dengan demikian dapat diinstal melalui pip install colorlog) dan secara aktif dipelihara .

Berikut cuplikan singkat salin dan tempel untuk menyiapkan pencatatan dan mencetak pesan log yang tampak layak:

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

Keluaran:

Output colorlog


4
Jawaban bagus; +1. Contoh kode dapat dipangkas (apakah tiga panggilan setLevelbenar-benar dibutuhkan?)
Clément

1
Aku berharap akan menemukan jawaban seperti ini jika aku mengarungi jawaban cukup lama. ☺ Saya harap @airmind akan mempertimbangkan untuk menjadikan ini jawaban yang diterima, sehingga orang-orang pintar yang bekerja di masa depan dapat menemukan perpustakaan yang tampaknya terbaik dengan kemalasan optimal. 😉
Michael Scheper

Saya baru saja mengangkat ini untuk contoh pesan OUTPUT ^^
Agustin Barrachina

69

Solusi cepat dan kotor untuk level log yang telah ditentukan dan tanpa mendefinisikan kelas baru.

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 impor log; # rekatkan kode dari @ABC; coba dengan logging.warning ('ini ujian'). Anda akan melihat bagian huruf besar dari "PERINGATAN: ini adalah tes" berwarna. Ini hanya bekerja di linux btw
Riccardo Galli

3
Karena hanya nama loglevel yang diwarnai, Anda harus memastikan bahwa nama loglevel tercetak sama sekali. Ini tidak terjadi di luar kotak bagi saya. Sesuatu di sepanjang garis ini akan membantu: logging.basicConfig(format='%(asctime)s [%(name)s] [%(levelname)s] %(message)s')Di mana tentu saja yang %(levelnames)spenting.
Sebastian

4
Solusi paling sederhana dan paling bersih untuk diterapkan dan dipahami.
F. Santiago

1
Coba saja di konsol Linux. echo -e "Normal texst \033[1;31mred bold text\033[0m normal text again". -eopsi echo mengartikan "\ 033" sebagai bentuk oktal dari simbol ASCII Escape. Simbol khusus ini membuat beberapa terminal yang kompatibel menafsirkan karakter selanjutnya ( mtermasuk karakter ) sebagai perintah khusus. en.wikipedia.org/wiki/ANSI_escape_code
eugene-bright

1
Perbaikan kecil: masukkan kode ini ke dalam if sys.sdterr.isatty():. Dalam hal ini jika Anda mengarahkan output ke file, file tidak akan mengandung karakter pelarian ini.
lesnik

36

Kode 2020, tidak perlu paket tambahan, Python 3

Tentukan kelas

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)

Instantiate logger

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

Dan gunakan!

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

Hasil masukkan deskripsi gambar di sini

Skema penuh warna masukkan deskripsi gambar di sini

Untuk windows

Solusi ini berfungsi pada Mac OS, terminal IDE. Sepertinya command prompt window tidak memiliki warna sama sekali secara default. Berikut adalah petunjuk cara mengaktifkannya, yang belum saya coba https://www.howtogeek.com/322432/how-to-customize-your-command-prompts-color-scheme-with-microsofts-colortool/


1
Saya menjalankan tes (python 3.7, windows), tetapi logging tidak menunjukkan warna:←[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
constructor

Sayangnya ini tidak berhasil.
Joe

2
Saya sangat menyukai jawaban ini sehingga saya membuat repo untuk itu, dengan beberapa peningkatan dan lembar contekan warna ansi.
Teodoro

@ konstruktor di mana Anda menjalankannya? Konsol IDE? terminal windows?
Sergey Pleshakov

@Siapa yang sebenarnya tidak berhasil? apa lingkungan Anda dan kesalahan apa yang Anda dapatkan? Saya ingin merevisi solusi untuk membuatnya bekerja lintas platform
Sergey Pleshakov

17

Yah, saya kira saya mungkin juga menambahkan variasi logger berwarna saya.

Ini bukan sesuatu yang mewah, tetapi sangat mudah digunakan dan tidak mengubah objek rekaman, sehingga menghindari logging urutan pelarian ANSI ke file log jika file handler digunakan. Itu tidak mempengaruhi pemformatan pesan log.

Jika Anda sudah menggunakan Formatter modul logging , yang harus Anda lakukan untuk mendapatkan nama level berwarna adalah mengganti Penasihat Penasihat Formatter dengan ColoredFormatter. Jika Anda mencatat seluruh aplikasi, Anda hanya perlu melakukan ini untuk pencatat tingkat atas.

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

Contoh penggunaan

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

Hasil

Output terminal

Output terminal

konten 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

Tentu saja Anda bisa mendapatkan semewah yang Anda inginkan dengan memformat terminal dan output file log. Hanya level log yang akan diwarnai.

Saya harap seseorang menemukan ini berguna dan itu tidak terlalu banyak sama. :)

File contoh Python dapat diunduh dari GitHub Gist ini: https://gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd


2
BTW untuk menambahkan warna pada pesan itu sendiri cukup tambahkan baris ini sebelumnya return:colored_record.msg = ('{0}{1}m{2}{3}').format(self.PREFIX, seq, colored_record.getMessage(), self.SUFFIX)
The Godfather

15

Saya memperbarui contoh dari tag pendukung airmind untuk latar depan dan latar belakang. Cukup gunakan variabel warna $ BLACK - $ WHITE dalam string formatter log Anda. Untuk mengatur latar belakang cukup gunakan $ 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

Jadi sekarang Anda dapat dengan mudah melakukan hal berikut di file konfigurasi Anda:

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

Perbaikan luar biasa. Namun komentar tentang superhanya berlaku untuk beberapa versi Python kuno, kurasa? Karena jawaban ini dari 2010. Ini bekerja dengan baik untuk saya dengan Python 2.7
Joakim

14

Anda dapat mengimpor modul colorlog dan menggunakannya ColoredFormatteruntuk mewarnai pesan log.

Contoh

Boilerplate untuk modul utama:

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

Kode hanya mengaktifkan warna dalam pesan log, jika modul colorlog diinstal dan jika output benar-benar pergi ke terminal. Ini menghindari urutan melarikan diri sedang ditulis ke file ketika output log diarahkan.

Selain itu, skema warna khusus adalah pengaturan yang lebih cocok untuk terminal dengan latar belakang gelap.

Beberapa contoh mencatat panggilan:

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

Keluaran:

masukkan deskripsi gambar di sini


2
Juga dapat menggunakan colorlog.basicConfigalih-alih logging.basicConfigyang memiliki beberapa default yang baik
MarSoft

1
Sebagai catatan, colorlog tidak selalu bekerja secara langsung pada platform Windows (seperti yang ditentukan, ketergantungan colorama diperlukan). Bahkan dengan itu, saya kesulitan membuatnya bekerja di Anaconda / Spyder env. Anda mungkin perlu menentukan colorama.init (strip = False) misalnya di escape_code.py (seperti yang ditunjukkan dalam utas ini github.com/spyder-ide/spyder/issues/1917 )
Matt-Mac-Muffin


11

Saya memodifikasi contoh asli yang disediakan oleh Sorin dan mensublimasikan StreamHandler menjadi ColorizedConsoleHandler.

Kelemahan dari solusi mereka adalah bahwa ia memodifikasi pesan, dan karena itu memodifikasi pesan yang sebenarnya, setiap penangan lain akan mendapatkan pesan yang dimodifikasi juga.

Ini menghasilkan file log dengan kode warna di dalamnya dalam kasus kami karena kami menggunakan banyak logger.

Kelas di bawah ini hanya berfungsi pada platform yang mendukung ansi, tetapi harus sepele untuk menambahkan kode warna windows ke dalamnya.

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)


7

Ada banyak tanggapan. Tapi tidak ada yang berbicara tentang dekorator. Jadi ini milikku.

Karena itu jauh lebih sederhana.

Tidak perlu mengimpor apa pun, atau menulis subkelas apa pun:

#!/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__)

Ini mengatur kesalahan dalam warna merah, men-debug pesan dengan warna biru, dan sebagainya. Seperti ditanyakan dalam pertanyaan.

Kami bahkan dapat mengadaptasi pembungkus untuk mengambil colorargumen agar secara dinamis mengatur warna pesan menggunakanlogger.debug("message", color=GREY)

EDIT: Jadi inilah dekorator yang disesuaikan untuk mengatur warna saat runtime:

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

Remix kecil lain dari pendekatan airmind yang menjaga semuanya dalam satu kelas:

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)

Untuk menggunakan melampirkan formatter ke handler, sesuatu seperti:

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

5

Alat sederhana namun sangat fleksibel untuk mewarnai teks terminal APA PUN adalah ' colout '.

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

Dimana teks dalam output 'myprocess' yang cocok dengan grup 1 regex akan diwarnai dengan color1, group 2 dengan color2, dll.

Sebagai contoh:

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

yaitu grup regex pertama (parens) cocok dengan tanggal awal dalam logfile, grup kedua cocok dengan nama file python, nomor baris dan nama fungsi, dan grup ketiga cocok dengan pesan log yang datang setelah itu. Saya juga menggunakan urutan paralel 'tebal / normal' serta urutan warna. Ini terlihat seperti:

file log dengan pemformatan berwarna

Perhatikan bahwa garis atau bagian dari garis yang tidak cocok dengan regex saya masih bergema, jadi ini bukan seperti 'grep --color' - tidak ada yang disaring dari output.

Jelas ini cukup fleksibel sehingga Anda dapat menggunakannya dengan proses apa pun, tidak hanya mengekor file log. Saya biasanya hanya menyiapkan regex baru dengan cepat setiap kali saya ingin mewarnai sesuatu. Untuk alasan ini, saya lebih suka colout ke alat pewarnaan logfile khusus, karena saya hanya perlu mempelajari satu alat, terlepas dari apa yang saya warnai: logging, output tes, sintaks menyoroti potongan kode di terminal, dll.

Ini juga menghindari sebenarnya membuang kode ANSI di logfile itu sendiri, yang IMHO adalah ide yang buruk, karena itu akan merusak hal-hal seperti menangkap pola di logfile kecuali Anda selalu ingat untuk mencocokkan kode ANSI di regex grep Anda.


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 Contoh yang bagus dengan [9*mkode untuk warna ANSI "cerah"! PS baris terakhir Anda membuat saya sedikit khawatir karena belum diketahui apakah login di luar definisi fungsi aman dengan Python .
personal_cloud

2

Inilah solusi saya:

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

Bit yang saya punya masalah adalah menyiapkan formatter dengan benar:

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)

Dan kemudian menggunakan:

import coloured_log
import logging

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

coloured_log.close()    # restore colours

Seharusnya kode pseudo (sebagai _set_colour hilang juga), tetapi telah menambahkan sesuatu. Masalahnya yang paling sulit adalah mengetahui cara memasang formatter dengan benar.
Nick

Lihat solusi "jack tukang ledeng". Saya pikir ini adalah cara yang lebih baik untuk menyelesaikan masalah (yaitu pawang harus melakukan pewarnaan). stackoverflow.com/questions/384076/…
Nick

1

Sementara solusi lain tampaknya baik-baik saja, mereka memiliki beberapa masalah. Beberapa memang mewarnai seluruh garis yang beberapa kali tidak diinginkan dan beberapa menghilangkan konfigurasi yang mungkin Anda miliki bersama. Solusi di bawah ini tidak memengaruhi apa pun kecuali pesan itu sendiri.

Kode

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)

Contoh

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

Keluaran

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

Seperti yang Anda lihat, semua yang lain masih akan dikeluarkan dan tetap dalam warna awal mereka. Jika Anda ingin mengubah apa pun selain pesan, Anda cukup meneruskan kode warnalog_format dalam contoh.


ketika saya menggunakannya, pesan dicetak dua kali. apa kamu tahu kenapa?
Validus Oculus

@ Bisakah Anda menguraikan? Yakni maksud Anda sesuatu seperti [17:01:36]:WARNING:this should be yellowthis should be yellowatau satu baris penuh sedang dicetak dua kali?
Pithikos

Maaf atas singkatnya komentar. Yang pertama terjadi: [17:01:36]: PERINGATAN: ini harus berwarna kuning \ nini harus berwarna kuning. Namun, saya hanya ingin yang diformat untuk ditampilkan, jika tidak terlihat seperti sampah karena log berlebihan.
Validus Oculus

@ MuratKarakuş tidak yakin mengapa ini terjadi tanpa memiliki pandangan penuh tentang implementasinya. Jika Anda menggunakan logger kustom, mungkin Anda mengganggu di beberapa titik? Perbaikan cepat dapat menghapus 7s:%(message)sdari log_format.
Pithikos

1

Saya memiliki dua kiriman untuk ditambahkan, yang satu hanya mewarnai pesan (ColoredFormatter), dan satu lagi mewarnai seluruh baris (ColorizingStreamHandler). Ini juga mencakup lebih banyak kode warna ANSI daripada solusi sebelumnya.

Beberapa konten telah bersumber (dengan modifikasi) dari: Posting di atas, dan http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html .

Hanya mewarnai pesan:

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

Mewarnai seluruh baris:

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

Ini adalah Enum yang berisi kode warna:

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'

Ini dapat diterapkan pada nama - nama setiap tingkat log. Ketahuilah bahwa ini adalah hack yang mengerikan.

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

Perhatikan bahwa pemformat log Anda harus menyertakan nama level log

%(levelname)

sebagai contoh:

    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 adalah alternatif lain. Ini bekerja dengan Python 2 & 3 di Linux, Windows dan MacOS.


Menantikan PR baru untuk mengurangi kekacauan jalur modul
mbspark

1

Bagaimana dengan menyoroti juga mencatat argumen pesan dengan warna bergantian, selain pewarnaan menurut tingkat? Baru-baru ini saya menulis kode sederhana untuk itu. Keuntungan lain adalah bahwa panggilan log dibuat dengan pemformatan gaya Python 3. ( "{}").

Lihat kode dan contoh terbaru di sini: https://github.com/davidohana/colargulog

Contoh kode logging:

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)

Keluaran:

masukkan deskripsi gambar di sini

Penerapan:

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

Gunakan pyfancy .

Contoh:

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

Pertanyaannya adalah untuk mengubah loggingfungsi untuk menggunakan perpustakaan pewarnaan yang terpisah.
Infeksi Drake

0

Hanya solusi lain, dengan warna 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)

menyebutnya sekali dari __main__fungsi Anda . Saya punya sesuatu seperti ini di sana:

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

itu juga memverifikasi bahwa output adalah konsol, jika tidak, tidak ada warna yang digunakan.


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)

Pemakaian

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


Untuk konsol, Anda dapat meninggalkan nama file atau hanya nama file = '' harus berfungsi. ubah basicConfig untuk memasukkan properti lain seperti nomor file, modul ..
estifanos gebrehiwot

0

Solusi berikut ini hanya berfungsi dengan python 3, tetapi bagi saya itu terlihat paling jelas.

Idenya adalah menggunakan pabrik catatan log untuk menambahkan atribut 'berwarna' untuk mencatat objek rekaman dan daripada menggunakan atribut 'berwarna' ini dalam format log.

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

Anda dapat dengan mudah mengubah contoh ini untuk membuat atribut berwarna lainnya (fe message_c) dan kemudian menggunakan atribut ini untuk mendapatkan teks berwarna (hanya) di tempat yang Anda inginkan.

(Trik praktis yang saya temukan baru-baru ini: Saya memiliki file dengan log debug berwarna dan setiap kali saya ingin sementara meningkatkan level log aplikasi saya, saya hanya tail -ffile log di terminal yang berbeda dan melihat log debug di layar tanpa mengubah konfigurasi dan memulai kembali aplikasi )


0

Ini adalah varian Python3 lain dari contoh airmind. Saya ingin beberapa fitur spesifik yang tidak saya lihat dalam contoh lain

  • gunakan warna untuk terminal tetapi jangan menulis karakter yang tidak dapat dicetak dalam penangan file (saya mendefinisikan 2 pemformat untuk ini)
  • kemampuan untuk menimpa warna untuk pesan log tertentu
  • konfigurasikan logger dari file (yaml dalam kasus ini)

Catatan: Saya menggunakan colorama tetapi Anda dapat memodifikasi ini sehingga tidak diperlukan. Juga untuk pengujian saya, saya hanya menjalankan file python sehingga kelas saya ada di modul. __main__Anda harus mengubah (): __main__.ColoredFormatterapa pun modul Anda.

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

keluaran:

keluaran

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.