it-swarm.com.de

Wie kann ich Python Logging-Ausgabe einfärben?

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

Jetzt hat Python das logging -Modul, mit dem Sie viele Optionen zum Anpassen der Ausgabe angeben können. Daher stelle ich mir vor, dass mit Python etwas Ähnliches möglich wäre, aber ich Ich kann nirgendwo herausfinden, wie das geht.

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

Was ich will (zum Beispiel) Fehler in rot, Debug-Meldungen in blau oder gelb und so weiter.

Natürlich würde dies wahrscheinlich ein kompatibles Terminal erfordern (die meisten modernen Terminals sind); aber ich könnte auf die ursprüngliche Ausgabe von logging zurückgreifen, wenn die Farbe nicht unterstützt wird.

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

313
airmind

Ich wusste bereits, dass die Farbe entweicht, ich habe sie vor einiger Zeit in meinem Bash-Prompt verwendet. Danke trotzdem.
Ich wollte es in das Protokollierungsmodul integrieren, was ich schließlich nach ein paar Versuchen und Fehlern tat.
Hier ist, was ich am Ende mit:

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 benutzen, erstelle deinen 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 an andere Handler weitergegeben oder an andere Logger weitergegeben wird. Wenn Sie Dateiprotokollierer usw. konfiguriert haben, möchten Sie wahrscheinlich nicht die Farben in den Protokolldateien haben. Um dies zu vermeiden, ist es wahrscheinlich am besten, einfach eine Kopie von record mit copy.copy() zu erstellen, bevor Sie das Attribut levelname bearbeiten, oder den levelnamen auf den vorherigen Wert zurückzusetzen, bevor Sie den formatierten String (credit) zurückgeben zu Michael in den Kommentaren).

178
airmind

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 es --- ist (verfügbar unter) PyPI (und GitHub ) und es ist kinderleicht zu bedienen. Es hat auch einen Vim-Syntaxmodus :-). In Zukunft könnte ich es erweitern, um auf Windows zu arbeiten.

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, Name des Protokollierers, PID, Protokollstufe und Protokollmeldung. So sieht es in der Praxis aus:

Screenshot of coloredlogs output

127
xolox

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

So funktioniert es: Auf Plattformen, die ANSI-Escape unterstützen, werden sie verwendet (nicht 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())
70
sorin

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

Update : Da dies ein Juckreiz ist, den ich so lange kratzen wollte, habe ich eine Bibliothek für faule Leute wie mich geschrieben Ich möchte nur einfache Möglichkeiten, Dinge zu tun: zenlog

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

Hier ist ein kurzer, kopier- und einfügbarer Ausschnitt zum Einrichten der Protokollierung und zum Drucken anständiger Protokollnachrichten:

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 output

59
rlafuente

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 Protokollformatierungszeichenfolge. Zum Einstellen des Hintergrunds 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
15
camillobruni

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

Terminal output

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 die Ausgabe von Terminal- und Protokolldateien beliebig formatieren. Nur die Protokollebene wird eingefärbt.

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

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

15
KCJ

Sie können das colorlog Modul importieren und sein ColoredFormatter zum Einfärben von Protokollnachrichten verwenden.

Beispiel

Kesselschild 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 in Protokollmeldungen nur, 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 das Protokollieren von Anrufen:

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

Ausgabe:

enter image description here

14
maxschlepzig

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

Der Nachteil ihrer Lösung besteht darin, dass sie die Nachricht modifiziert, und da dies die eigentliche Protokollnachricht modifiziert, erhalten auch alle anderen Handler die modifizierte Nachricht.

Dies führte in unserem Fall zu Logdateien mit Farbcodes, da wir mehrere Logger 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)
11
Ramonster

Schauen Sie sich die folgende Lösung an. Der Stream-Handler sollte derjenige sein, der die Färbung vornimmt, dann haben Sie die Möglichkeit, Wörter anstatt nur die gesamte Zeile (mit dem Formatierer) einzufärben.

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

10
Nick

Jetzt gibt es ein veröffentlichtes PyPi-Modul für die anpassbare Ausgabe von Farbprotokollen:

https://pypi.python.org/pypi/Rainbow_logging_handler/

und

https://github.com/laysakura/Rainbow_logging_handler

  • Unterstützt Windows

  • Unterstützt Django

  • Anpassbare Farben

Da dies als Python Ei verteilt wird, ist es sehr einfach, es für jede Python Anwendung zu installieren.

9
Mikko Ohtamaa

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

Weil es viel einfacher ist.

Sie müssen nichts importieren oder eine Unterklasse 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__)

Dies setzt die Fehler in rot, Debug-Meldungen in blau und so weiter. Wie in der Frage gestellt.

Wir könnten sogar den Wrapper so anpassen, dass er ein color-Argument verwendet, um die Farbe der Nachricht mithilfe von logger.debug("message", color=GREY) dynamisch festzulegen.

EDIT: Also hier ist der angepasste Dekorator, 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)
7
lain

Ein weiterer kleiner Remix des Airmind-Ansatzes, 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)

Wenn Sie den Formatierer an einen Handler anhängen möchten, gehen Sie wie folgt vor:

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

Ein einfaches, aber sehr flexibles Werkzeug zum Ausmalen von Terminal-Texten ist ' colout '.

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

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

Zum Beispiel:

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

dh die erste reguläre Ausdrucksgruppe (parens) stimmt mit dem Anfangsdatum in der Protokolldatei überein, die zweite Gruppe stimmt mit einem python Dateiname, Zeilennummer und Funktionsname) überein und die dritte Gruppe stimmt mit der nachfolgenden Protokollmeldung überein Das. Ich verwende auch eine parallele Folge von 'Fett/Normalen' sowie die Folge von Farben. Das sieht so aus:

logfile with colored formatting

Beachten Sie, dass Linien oder Teile von Linien, die mit keinem meiner regulären Ausdrücke übereinstimmen, weiterhin als Echo angezeigt werden. Dies entspricht also nicht '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 ziehe ich jedes Mal, wenn ich etwas färben möchte, einen neuen Regex im Flug auf. Aus diesem Grund ziehe ich Colout jedem benutzerdefinierten Tool zum Färben von Protokolldateien vor, da ich nur ein Tool lernen muss, unabhängig davon, was ich färbe: Protokollierung, Testausgabe, Hervorheben von Codefragmenten in der Syntax im Terminal usw.

Es wird auch vermieden, ANSI-Codes in der Protokolldatei selbst abzulegen, was IMHO eine schlechte Idee ist, da es Dinge wie das Greifen nach Mustern in der Protokolldatei unterbricht, es sei denn, Sie denken immer daran, die ANSI-Codes in Ihrem regulären Ausdruck abzugleichen.

5
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)))`
5
Serhii Khachko

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])
2
veegee

2019 Frischer Code, der ohne zusätzliche Pakete einfach hinzugefügt werden kann

Definiere 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 benutze!

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

Ergebnis enter image description here

2

Das bisschen, mit dem ich Probleme hatte, war, 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 benutzen:

import coloured_log
import logging

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

coloured_log.close()    # restore colours
1
Nick

Während die anderen Lösungen in Ordnung zu sein scheinen, haben sie einige Probleme. Einige färben die ganzen Zeilen, was manchmal nicht erwünscht ist, und andere lassen die 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 noch ausgegeben und bleibt in der ursprünglichen Farbe. Wenn Sie etwas anderes als die Nachricht ändern möchten, können Sie die Farbcodes im Beispiel einfach an log_format Übergeben.

1
Pithikos

Dies ist eine Aufzählung mit den folgenden Farbcodes:

class TerminalColour:
    """
    Terminal colour formatting codes
    """
    # https://stackoverflow.com/questions/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 jeder Protokollebene angewendet werden. Sei dir 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'
        },
1
Joe Heffer

Ich muss zwei Vorlagen 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:

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:

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

Nur eine andere 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)

nenne es einmal von deinem __main__ Funktion. Ich habe dort so etwas:

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.

0
yucer
0
herrk

Verwenden Sie Pyfancy .

Beispiel:

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