Wie kann ich die Python-Protokollausgabe einfärben?

352

Vor einiger Zeit habe ich eine Mono-Anwendung mit farbiger Ausgabe gesehen, vermutlich aufgrund ihres Protokollsystems (weil alle Nachrichten standardisiert waren).

Jetzt verfügt Python über das loggingModul, mit dem Sie viele Optionen zum Anpassen der Ausgabe angeben können. Ich stelle mir also vor, dass mit Python etwas Ähnliches möglich wäre, aber ich kann nirgendwo herausfinden, wie das geht.

Gibt es eine Möglichkeit, das Python- loggingModul in Farbe auszugeben?

Was ich will (zum Beispiel) Fehler in Rot, Debug-Nachrichten in Blau oder Gelb und so weiter.

Natürlich würde dies wahrscheinlich ein kompatibles Terminal erfordern (die meisten modernen Terminals sind es); Ich könnte jedoch auf die ursprüngliche loggingAusgabe zurückgreifen, wenn die Farbe nicht unterstützt wird.

Irgendwelche Ideen, wie ich mit dem Protokollierungsmodul eine farbige Ausgabe erhalten kann?

airmind
quelle
1
Sie sollten angeben, dass Sie eine plattformübergreifende Lösung wünschen - sowohl Linux als auch Windows.
Sorin
1
Verwandte, wenn Sie Eclipse / PyDev verwenden: Protokolle in der Eclipse-Konsole kolorieren
Tobias Kienzler
5
Vielleicht können Sie auch Colorlog verwenden
Ehtesh Choudhury
5
Sie können auch versuchen , chromalog die ich schrieb alle Betriebssysteme und Python - Versionen zu unterstützen (2,7 und 3 *)
Ereon
1
Lösungen, die tatsächlich ANSI-Codes in der Protokolldatei speichern, sind eine schlechte Idee. Sie werden Sie auffangen, wenn Sie in sechs Monaten nach etwas suchen, aber vergessen Sie, die ANSI-Zeichen in Ihrem Regex-Muster zu berücksichtigen. Es gibt einige Lösungen unten, die die Farbe hinzufügen, wenn Sie das Protokoll anzeigen, anstatt wie das Protokoll geschrieben wird ...
Jonathan Hartley

Antworten:

192

Ich wusste bereits über die Farbentweichungen Bescheid, ich habe sie vor einiger Zeit in meiner Bash-Eingabeaufforderung verwendet. Danke trotzdem.
Ich wollte es in das Protokollierungsmodul integrieren, was ich schließlich nach ein paar Versuchen und Fehlern tat.
Folgendes habe ich am Ende:

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)

Und um es zu verwenden, erstellen Sie Ihren eigenen 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)

Nur für den Fall, dass es jemand anderes braucht.

Seien Sie vorsichtig, wenn Sie mehr als einen Logger oder Handler verwenden: ColoredFormatterÄndert das Datensatzobjekt, das weiter an andere Handler übergeben oder an andere Logger weitergegeben wird. Wenn Sie Dateiprotokollierer usw. konfiguriert haben, möchten Sie wahrscheinlich nicht, dass die Farben in den Protokolldateien enthalten sind. Um dies zu vermeiden, ist es wahrscheinlich am besten, einfach eine Kopie von recordmit zu erstellen, copy.copy()bevor Sie das Attribut levelname bearbeiten, oder den Ebenennamen auf den vorherigen Wert zurückzusetzen, bevor Sie die formatierte Zeichenfolge zurückgeben (Gutschrift an Michael) in den Kommentaren).

airmind
quelle
Wo ist GELB, WEISS, BLAU usw. definiert?
Swaroop CH
1
@Swaroop - Dies sind ANSI-Escape-Codes, die Sie bei Google nachschlagen oder hier finden können: en.wikipedia.org/wiki/ANSI_escape_code oder alternativ pueblo.sourceforge.net/doc/manual/ansi_color_codes.html
Brian M. Hunt
53
Ich glaube nicht, dass Sie nur dafür eine Logger-Unterklasse erstellen sollten - Ihre Antwort ist in Ordnung, wenn Sie eine spezialisierte Unterklasse erstellen Formatterund deren Verwendung für a angeben StreamHandler. Es ist jedoch keine Logger-Unterklasse erforderlich. Tatsächlich fügt die Verwendung einer Logger-Klasse jedem erstellten Logger einen Handler hinzu, was normalerweise nicht der Fall ist.
Vinay Sajip
6
Eine Randnotiz zu ColoredFormatter. Es ändert das Datensatzobjekt, das weiter an andere Handler übergeben oder an andere Protokollierer weitergegeben wird. Wenn Sie Dateiprotokollierer usw. konfiguriert haben, möchten Sie wahrscheinlich nicht, dass die Farben in den Protokolldateien enthalten sind. Um dies zu vermeiden, ist es wahrscheinlich am besten, einfach eine Kopie von recordmit zu erstellen, copy.copy()bevor Sie das Attribut levelname bearbeiten, oder den Ebenennamen auf den vorherigen Wert zurückzusetzen, bevor Sie die formatierte Zeichenfolge zurückgeben.
Michael
148

Vor Jahren habe ich einen farbigen Stream-Handler für meinen eigenen Gebrauch geschrieben. Dann bin ich auf diese Seite gestoßen und habe eine Sammlung von Codefragmenten gefunden, die kopiert / eingefügt werden :-(. Mein Stream-Handler funktioniert derzeit nur unter UNIX (Linux, Mac OS X), aber der Vorteil ist, dass er auf PyPI (und GitHub) verfügbar ist ) und es ist kinderleicht zu bedienen. Es hat auch einen Vim-Syntaxmodus :-). In Zukunft könnte ich es auf Windows erweitern.

So installieren Sie das Paket:

$ pip install coloredlogs

Um zu bestätigen, dass es funktioniert:

$ coloredlogs --demo

So beginnen Sie mit Ihrem eigenen Code:

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

Das im obigen Beispiel gezeigte Standardprotokollformat enthält Datum, Uhrzeit, Hostname, den Namen des Protokollierers, die PID, die Protokollstufe und die Protokollnachricht. So sieht es in der Praxis aus:

Screenshot der Ausgabe von Farbprotokollen

HINWEIS: Bei Verwendung von Git Bash mit MinTTY

Git Bash unter Windows hat einige dokumentierte Macken: Winpty und Git Bash

Für ANSI-Escape-Codes und für das Umschreiben und Animieren von Zeichen im ncurses-Stil müssen Sie Befehlen ein Präfix voranstellen winpty.

$ winpty coloredlogs --demo
$ winpty python your_colored_logs_script.py
xolox
quelle
2
lustig genug, ich wollte gerade einen Link zu " pypi.python.org/pypi/coloredlogs/0.4.7 " in diesem Thread hinzufügen !
Iosu S.
1
Aus irgendeinem Grund bekomme ich immer wieder, AttributeError: 'module' object has no attribute 'install'wenn ich benutze coloredlogs.install(). Können Sie das mit der neuesten Version bestätigen?
con-f-use
11
Das sieht wunderschön aus. Leider bricht es viele Dinge; Insbesondere werden Aufrufe von logging.basicConfig ungültig. Dies macht es beispielsweise unmöglich, einen benutzerdefinierten Formatierer zu verwenden.
Clément
@ Clément: Zwei (überlappende?) Fragen: (1) Was meinst du genau mit "ungültige Anrufe an logging.basicConfig" und (2) was wäre die Alternative? Beides logging.basicConfig()und coloredlogs.install()installieren Sie einen Stream-Handler, der sich an der Konsole anmeldet, sodass Sie ohne "Stornieren" doppelte Nachrichten erhalten würden ...
xolox
Ich erwartete entweder Magie für (1) oder (vernünftigerweise) eine Möglichkeit, coloredlogs.installdas zu verwendende Format wie im colorlogPaket zu bestimmen.
Clément
74

Hier ist eine Lösung, die auf jeder Plattform funktionieren sollte. Wenn es mir nicht nur sagt und ich es aktualisieren werde.

So funktioniert es: Auf Plattformen, die ANSI-Escape-Zeichen unterstützen, werden diese verwendet (nicht unter Windows), und unter Windows werden API-Aufrufe verwendet, um die Konsolenfarben zu ändern.

Das Skript hackt die Methode logging.StreamHandler.emit aus der Standardbibliothek und fügt ihr einen Wrapper hinzu.

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())
Sorin
quelle
3
Ich habe darauf basierend eine StreamHandler-Klasse geschrieben, siehe gist.github.com/mooware/a1ed40987b6cc9ab9c65 .
Mooware
2
das hat bei mir funktioniert! Zeile 90: sollte sein args[1].msg = color + str(args[1].msg) + '\x1b[0m' # normal.
Rasika Perera
Ich mag diese Lösung. derzeit verwenden. Ich sehe, dass es ein Attribut _set_color gibt. Gibt es eine Möglichkeit, dies für eine bestimmte Protokollnachricht zu tun? bearbeiten , oh sehen, das ist nur ein Patch für Windows-Maschinen. wäre schön, benutzerdefinierte für verschiedene Anwendungsfälle hinzuzufügen.
Brizz
+1 für ANSI-Farbe. In xterm können Sie sogar 256 Farben gleichzeitig erhalten und die Palette dynamisch definieren! Beachten Sie jedoch, dass alle Aufrufe von Protokollierungsfunktionen innerhalb einer Funktionsdefinition liegen sollten , um mögliche Probleme mit der Importsperre bei der Protokollierung außerhalb einer Funktionsdefinition zu vermeiden . Ihr Code sieht meistens gut aus. nur ein bisschen in TestColorer.pygeht mich etwas an .
personal_cloud
Dies führt zu Farbcodes am Anfang und Ende der Protokollmeldungen in tatsächlichen Protokolldateien.
MehmedB
74

Update : Weil dies ein Juckreiz ist, den ich schon so lange kratzen wollte, habe ich eine Bibliothek für faule Leute wie mich geschrieben, die nur einfache Möglichkeiten haben wollen, Dinge zu tun: Zenlog

Colorlog ist dafür hervorragend geeignet. Es ist auf PyPI verfügbar (und somit über installierbar pip install colorlog) und wird aktiv gewartet .

Hier ist ein schneller Ausschnitt zum Kopieren und Einfügen, mit dem Sie die Protokollierung einrichten und anständig aussehende Protokollnachrichten drucken können:

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

Ausgabe:

Colorlog-Ausgabe

rlafuente
quelle
4
Gute Antwort; +1. Das Codebeispiel könnte jedoch gekürzt werden (sind drei Aufrufe setLevelwirklich erforderlich?)
Clément
1
Ich hatte gehofft, ich würde eine Antwort wie diese finden, wenn ich die Antworten lange genug durchblätterte. ☺ Ich hoffe, @airmind wird in Betracht ziehen, dies zur akzeptierten Antwort zu machen, damit zukünftige arbeitskluge Leute die beste Bibliothek mit optimaler Faulheit finden können. 😉
Michael Scheper
Ich habe dies gerade für die Nachrichtenbeispiele des OUTPUT ^^
Agustin Barrachina
69

Schnelle und schmutzige Lösung für vordefinierte Protokollebenen und ohne Definition einer neuen Klasse.

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))
ABC
quelle
@ spiderplant0 import logging; # füge den Code von @ABC ein; Versuchen Sie es mit logging.warning ('Dies ist ein Test'). Sie sehen den Großbuchstaben von "WARNUNG: Dies ist ein Test" farbig. Es funktioniert übrigens nur unter Linux
Riccardo Galli
3
Da nur der Loglevel-Name farbig ist, müssen Sie sicherstellen, dass der Loglevel-Name überhaupt auf der Konsole gedruckt wird. Dies geschieht für mich nicht sofort. Etwas in diese Richtung wird helfen: logging.basicConfig(format='%(asctime)s [%(name)s] [%(levelname)s] %(message)s')Wo natürlich das %(levelnames)swichtig ist.
Sebastian
4
Einfachste und sauberste Lösung zum Anwenden und Verstehen.
F. Santiago
1
Probieren Sie es einfach in der Linux-Konsole aus. echo -e "Normal texst \033[1;31mred bold text\033[0m normal text again". Die Echo- -eOption interpretiert "\ 033" als oktale Form des Escape ASCII-Symbols. Durch dieses spezielle Symbol interpretieren einige kompatible Terminals nachfolgende Zeichen ( meinschließlich Zeichen ) als Sonderbefehle. en.wikipedia.org/wiki/ANSI_escape_code
eugene-bright
1
Kleinere Verbesserung: Geben Sie diesen Code ein if sys.sdterr.isatty():. In diesem Fall enthält die Datei diese Escape-Zeichen nicht, wenn Sie die Ausgabe in eine Datei umleiten.
Lesnik
35

2020 Code, keine zusätzlichen Pakete erforderlich, Python 3

Definieren Sie eine Klasse

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)

Logger instanziieren

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

Und verwenden!

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

Ergebnis Geben Sie hier die Bildbeschreibung ein

Das Vollfarbschema Geben Sie hier die Bildbeschreibung ein

Für Windows

Diese Lösung funktioniert unter Mac OS und IDE-Terminals. Die Eingabeaufforderung des Fensters hat standardmäßig überhaupt keine Farben. Hier finden Sie Anweisungen zum Aktivieren, die ich nicht ausprobiert habe. Https://www.howtogeek.com/322432/how-to-customize-your-command-prompts-color-scheme-with-microsofts-colortool/

Sergey Pleshakov
quelle
1
Ich führe den Test aus (Python 3.7, Windows), aber die Protokollierung zeigt keine Farben:←[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
Konstruktor
Das funktioniert leider nicht.
Joe
2
Diese Antwort hat mir so gut gefallen, dass ich ein Repo dafür gemacht habe, mit ein paar Schritten und einem Spickzettel mit Ansi-Farben.
Teodoro
@constructor wo läuft es? IDE-Konsole? Windows Terminal?
Sergey Pleshakov
@ Joe was genau funktioniert nicht? Was ist Ihre Umgebung und welche Fehler erhalten Sie? Ich möchte die Lösung überarbeiten, damit sie plattformübergreifend funktioniert
Sergey Pleshakov,
17

Nun, ich denke, ich könnte genauso gut meine Variation des farbigen Loggers hinzufügen.

Dies ist nichts Besonderes, aber es ist sehr einfach zu verwenden und ändert das Datensatzobjekt nicht. Dadurch wird vermieden, dass die ANSI-Escape-Sequenzen in einer Protokolldatei protokolliert werden, wenn ein Datei-Handler verwendet wird. Die Formatierung der Protokollnachrichten wird nicht beeinflusst.

Wenn Sie bereits den Formatter des Protokollierungsmoduls verwenden , müssen Sie lediglich den Formatter Ihres Counsel-Handlers durch den ColoredFormatter ersetzen, um farbige Ebenennamen zu erhalten. Wenn Sie eine gesamte App protokollieren, müssen Sie dies nur für den Logger der obersten Ebene tun.

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

Anwendungsbeispiel

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

Ergebnisse

Terminalausgang

Terminalausgang

app.log Inhalt

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

Natürlich können Sie mit der Formatierung der Terminal- und Protokolldateiausgaben so ausgefallen werden, wie Sie möchten. Nur die Protokollstufe wird eingefärbt.

Ich hoffe, jemand findet das nützlich und es ist nicht einfach viel mehr dasselbe. :) :)

Die Python-Beispieldateien können von diesem GitHub-Gist heruntergeladen werden: https://gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd

KCJ
quelle
2
Übrigens, um der Nachricht selbst Farben hinzuzufügen, fügen Sie einfach diese Zeile vor return:colored_record.msg = ('{0}{1}m{2}{3}').format(self.PREFIX, seq, colored_record.getMessage(), self.SUFFIX)
Der Pate
15

Ich habe das Beispiel von airmind aktualisiert, das Tags für Vordergrund und Hintergrund unterstützt. Verwenden Sie einfach die Farbvariablen $ BLACK - $ WHITE in Ihrer Protokollformatiererzeichenfolge. Um den Hintergrund einzustellen, verwenden Sie einfach $ 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

Jetzt können Sie in Ihrer Konfigurationsdatei einfach Folgendes tun:

[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s
Camillobruni
quelle
Große Verbesserung. Allerdings gilt der Kommentar zu supernur für einige alte Python-Versionen, denke ich? Da diese Antwort aus dem Jahr 2010 stammt, hat es für mich mit Python 2.7
Joakim
14

Sie können das Colorlog- Modul importieren und ColoredFormatterzum Kolorieren von Protokollnachrichten verwenden.

Beispiel

Boilerplate für Hauptmodul:

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

Der Code aktiviert Farben nur in Protokollnachrichten, wenn das Colorlog-Modul installiert ist und die Ausgabe tatsächlich an ein Terminal geht. Dadurch wird vermieden, dass Escape-Sequenzen in eine Datei geschrieben werden, wenn die Protokollausgabe umgeleitet wird.

Außerdem wird ein benutzerdefiniertes Farbschema eingerichtet, das sich besser für Terminals mit dunklem Hintergrund eignet.

Einige Beispiele für die Protokollierung von Anrufen:

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

Ausgabe:

Geben Sie hier die Bildbeschreibung ein

maxschlepzig
quelle
2
Kann auch verwendet werden, colorlog.basicConfiganstatt logging.basicConfigeinige gute
Standardeinstellungen zu haben
1
Für den Datensatz funktioniert Colorlog nicht immer direkt auf Windows-Plattformen (wie angegeben ist eine Colorama-Abhängigkeit erforderlich). Trotzdem hatte ich Probleme, es in Anaconda / Spyder env zum Laufen zu bringen. Möglicherweise müssen Sie colorama.init (strip = False) angeben, beispielsweise in Escape_code.py (wie in diesem Thread angegeben github.com/spyder-ide/spyder/issues/1917 )
Matt-Mac-Muffin
11

Schauen Sie sich die folgende Lösung an. Der Stream-Handler sollte das Färben übernehmen, dann haben Sie die Möglichkeit, Wörter und nicht nur die gesamte Zeile (mit dem Formatierer) zu färben.

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

Nick
quelle
Eine aktualisierte Implementierung finden Sie in dieser Übersicht (vom Blog-Autor gepflegt). Ich benutze es und funktioniert gut. Danke für das Teilen.
Noisebleed
11

Ich habe das ursprüngliche Beispiel von Sorin geändert und StreamHandler in einen ColorizedConsoleHandler unterteilt.

Der Nachteil ihrer Lösung besteht darin, dass die Nachricht geändert wird. Da dadurch die tatsächliche Protokollnachricht geändert wird, erhalten auch alle anderen Handler die geänderte Nachricht.

Dies führte in unserem Fall zu Protokolldateien mit Farbcodes, da wir mehrere Protokollierer verwenden.

Die folgende Klasse funktioniert nur auf Plattformen, die Ansi unterstützen. Es sollte jedoch trivial sein, die Windows-Farbcodes hinzuzufügen.

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

Es gibt Unmengen von Antworten. Aber keiner spricht von Dekorateuren. Also hier ist meins.

Weil es viel einfacher ist.

Es ist nicht erforderlich, etwas zu importieren oder eine Unterklasse zu schreiben:

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

Dadurch werden die Fehler in Rot, Debug-Meldungen in Blau usw. festgelegt. Wie in der Frage gestellt.

Wir könnten sogar den Wrapper anpassen, um einen zu nehmen color Argument für die dynamische Einstellung der Farbe der Nachricht zu verwendenlogger.debug("message", color=GREY)

EDIT: Also hier ist der angepasste Dekorateur, um Farben zur Laufzeit einzustellen:

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)
gelegen
quelle
6

Ein weiterer kleiner Remix von Airminds Ansatz, der alles in einer Klasse hält:

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)

Um den Formatierer an einen Handler anzuhängen, gehen Sie wie folgt vor:

handler.setFormatter(ColorFormatter())
logger.addHandler(handler)
Gravitation
quelle
5

Ein einfaches, aber sehr flexibles Werkzeug zum Färben von JEDEM Terminaltext ist ' colout '.

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

Wenn Text in der Ausgabe von 'myprocess', der mit Gruppe 1 des regulären Ausdrucks übereinstimmt, mit Farbe1, Gruppe 2 mit Farbe2 usw. gefärbt wird.

Zum Beispiel:

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

Das heißt, die erste Regex-Gruppe (Parens) stimmt mit dem Anfangsdatum in der Protokolldatei überein, die zweite Gruppe entspricht einem Python-Dateinamen, einer Zeilennummer und einem Funktionsnamen und die dritte Gruppe entspricht der Protokollnachricht, die danach kommt. Ich verwende auch eine parallele Folge von 'Fett / Normal' sowie die Folge von Farben. Das sieht so aus:

Protokolldatei mit farbiger Formatierung

Beachten Sie, dass Zeilen oder Teile von Zeilen, die keinem meiner regulären Ausdrücke entsprechen, immer noch wiedergegeben werden. Dies ist also nicht wie 'grep --color' - nichts wird aus der Ausgabe herausgefiltert.

Offensichtlich ist dies flexibel genug, dass Sie es mit jedem Prozess verwenden können, nicht nur mit dem Tailing von Protokolldateien. Normalerweise zaubere ich jedes Mal, wenn ich etwas einfärben möchte, spontan einen neuen regulären Ausdruck. Aus diesem Grund bevorzuge ich Colout gegenüber jedem benutzerdefinierten Tool zum Ausmalen von Protokolldateien, da ich nur ein Tool lernen muss, unabhängig davon, was ich einfärbe: Protokollierung, Testausgabe, Syntax, die Codeausschnitte im Terminal hervorhebt usw.

Es wird auch vermieden, ANSI-Codes tatsächlich in die Protokolldatei selbst zu kopieren, was meiner Meinung nach eine schlechte Idee ist, da dadurch beispielsweise das Suchen nach Mustern in der Protokolldatei unterbrochen wird, es sei denn, Sie denken immer daran, die ANSI-Codes in Ihrer Grep-Regex abzugleichen.

Jonathan Hartley
quelle
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)))`
Serhii Khachko
quelle
+1 Schönes Beispiel mit den [9*mCodes für die "hellen" ANSI-Farben! PS Ihre letzte Zeile betrifft mich ein wenig, da noch nicht bekannt ist, ob die Protokollierung außerhalb einer Funktionsdefinition in Python sicher ist .
personal_cloud
2

Hier ist meine Lösung:

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])
veegee
quelle
1

Ich hatte Probleme damit, den Formatierer richtig einzurichten:

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)

Und dann zu verwenden:

import coloured_log
import logging

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

coloured_log.close()    # restore colours
Nick
quelle
Es sollte Pseudocode sein (da _set_colour ebenfalls fehlt), aber etwas hinzugefügt haben. Das größte Problem bestand darin, zu wissen, wie der Formatierer richtig angebracht wird.
Nick
Siehe die "Klempnerheber" -Lösung. Ich denke, dies ist ein besserer Weg, um das Problem zu lösen (dh der Handler sollte die Kolorierung durchführen). stackoverflow.com/questions/384076/…
Nick
1

Während die anderen Lösungen in Ordnung zu sein scheinen, haben sie einige Probleme. Einige färben die gesamten Linien, was manchmal nicht erwünscht ist, und andere lassen jede Konfiguration aus, die Sie möglicherweise alle zusammen haben. Die folgende Lösung wirkt sich nur auf die Nachricht selbst aus.

Code

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)

Beispiel

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

Ausgabe

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

Wie Sie sehen, wird alles andere immer noch ausgegeben und bleibt in der ursprünglichen Farbe. Wenn Sie etwas anderes als die Nachricht ändern möchten, können Sie einfach die Farbcodes log_formatim Beispiel übergeben.

Pithikos
quelle
Wenn ich es benutze, werden Nachrichten zweimal gedruckt. wissen Sie, warum?
Validus Oculus
@ könntest du das näher erläutern? Du meinst nämlich so etwas wie [17:01:36]:WARNING:this should be yellowthis should be yellowoder eine ganze Zeile, die zweimal gedruckt wird?
Pithikos
Entschuldigung für die Kürze des Kommentars. Ersteres geschah: [17:01:36]: WARNUNG: Dies sollte gelb sein \ ndies sollte gelb sein. Ich möchte jedoch nur, dass das formatierte angezeigt wird, andernfalls sieht es aufgrund redundanter Protokolle wie ein Müll aus.
Validus Oculus
@ MuratKarakuş nicht sicher, warum dies passiert, ohne einen vollständigen Überblick über die Implementierung zu haben. Wenn Sie einen benutzerdefinierten Logger verwenden, stören Sie möglicherweise irgendwann? Eine schnelle Lösung könnte darin bestehen, das 7s:%(message)saus dem zu entfernen log_format.
Pithikos
1

Ich muss zwei Einreichungen hinzufügen, von denen eine nur die Nachricht einfärbt (ColoredFormatter) und eine die gesamte Zeile einfärbt (ColorizingStreamHandler). Dazu gehören auch mehr ANSI-Farbcodes als bei früheren Lösungen.

Einige Inhalte stammen (mit Änderungen) aus: dem obigen Beitrag und http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html .

Färbt nur die Nachricht ein:

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

Färbt die ganze Linie ein:

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
ZetaSyanthis
quelle
1

Dies ist eine Aufzählung mit den Farbcodes:

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'

Dies kann auf die Namen der einzelnen Protokollebenen angewendet werden. Seien Sie sich bewusst, dass dies ein monströser Hack ist.

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

Beachten Sie, dass Ihr Protokollformatierer den Namen der Protokollebene enthalten muss

%(levelname)

zum Beispiel:

    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'
        },
Joe Heffer
quelle
1

FriendlyLog ist eine weitere Alternative. Es funktioniert mit Python 2 & 3 unter Linux, Windows und MacOS.

SebiSebi
quelle
Ich freue mich auf die neue PR zur Reduzierung der
Modulpfad-
1

Was ist mit dem Hervorheben von Protokollnachrichtenargumenten mit abwechselnden Farben zusätzlich zum Färben nach Ebene? Ich habe kürzlich einfachen Code dafür geschrieben. Ein weiterer Vorteil besteht darin, dass der Protokollaufruf mit der Formatierung im Python 3-Klammerstil erfolgt. ( "{}").

Den neuesten Code und Beispiele finden Sie hier: https://github.com/davidohana/colargulog

Beispielprotokollierungscode:

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)

Ausgabe:

Geben Sie hier die Bildbeschreibung ein

Implementierung:

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

Written by [email protected]
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
dux2
quelle
0

Verwenden Sie Pyfancy .

Beispiel:

print(pyfancy.RED + "Hello Red!" + pyfancy.END)
WebMaster
quelle
Die Frage war, die loggingFunktionalität zu optimieren , um eine separate Farbbibliothek zu verwenden.
Infizierter Drake
0

Nur eine weitere Lösung mit den Farben von 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)

Rufen Sie es einmal von Ihrer __main__Funktion aus. Ich habe so etwas dort:

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

Außerdem wird überprüft, ob es sich bei der Ausgabe um eine Konsole handelt. Andernfalls werden keine Farben verwendet.

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

Verwendungszweck

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

estifanos gebrehiwot
quelle
Für die Konsole können Sie den Dateinamen weglassen oder einfach Dateiname = '' sollte funktionieren. Ändern Sie basicConfig, um andere Eigenschaften wie
Dateinummer
0

Die folgende Lösung funktioniert nur mit Python 3, aber für mich sieht es am klarsten aus.

Die Idee ist, mithilfe der Protokolldatensatzfabrik 'farbige' Attribute zu Protokolldatensatzobjekten hinzuzufügen und diese 'farbigen' Attribute dann im Protokollformat zu verwenden.

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

Sie können dieses Beispiel leicht ändern, um andere farbige Attribute (z. B. message_c) zu erstellen, und diese Attribute dann verwenden, um farbigen Text (nur) dort abzurufen, wo Sie möchten.

(Praktischer Trick, den ich kürzlich entdeckt habe: Ich habe eine Datei mit farbigen Debug-Protokollen und wann immer ich die Protokollstufe meiner Anwendung vorübergehend erhöhen möchte, habe ich nur tail -fdie Protokolldatei in einem anderen Terminal und sehe Debug-Protokolle auf dem Bildschirm, ohne die Konfiguration zu ändern und die Anwendung neu zu starten )

lesnik
quelle
0

Dies ist eine weitere Python3-Variante des Beispiels von airmind. Ich wollte einige spezifische Funktionen, die ich in den anderen Beispielen nicht gesehen habe

  • Verwenden Sie Farben für das Terminal, aber schreiben Sie keine nicht druckbaren Zeichen in die Dateihandler (ich habe dafür 2 Formatierer definiert).
  • Möglichkeit, die Farbe für eine bestimmte Protokollnachricht zu überschreiben
  • Konfigurieren Sie den Logger aus einer Datei (in diesem Fall yaml).

Anmerkungen: Ich habe colorama verwendet, aber Sie können dies ändern, sodass es nicht erforderlich ist. Auch für meine Tests habe ich nur eine Python-Datei ausgeführt, sodass sich meine Klasse im Modul befindet. __main__Sie müssten zu dem wechseln (): __main__.ColoredFormatter, was auch immer Ihr Modul ist.

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

Ausgabe:

Ausgabe

Scott
quelle