544 Stimmen

Wie kann ich die Python-Logging-Ausgabe einfärben?

Vor einiger Zeit sah ich eine Mono-Anwendung mit farbiger Ausgabe, vermutlich aufgrund des Protokollsystems (da alle Meldungen standardisiert waren).

Jetzt hat Python die logging Modul, mit dem Sie eine Vielzahl von Optionen zur Anpassung der Ausgabe angeben können. Ich stelle mir vor, dass etwas Ähnliches auch mit Python möglich wäre, aber ich kann nirgendwo herausfinden, wie man das macht.

Gibt es eine Möglichkeit, die Python logging Modulausgabe in Farbe?

Ich möchte z. B. Fehler in rot, Fehlermeldungen in blau oder gelb usw.

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

Haben Sie eine Idee, wie ich mit dem Logging-Modul eine farbige Ausgabe erhalten kann?

9voto

Installieren Sie das colorlog-Paket, und Sie können sofort Farben in Ihren Logmeldungen verwenden:

  • Erwerben Sie eine logger Instanz, genau wie Sie es normalerweise tun würden.
  • Legen Sie die Protokollierungsstufe fest. Sie können auch Konstanten verwenden wie DEBUG y INFO direkt aus dem Logging-Modul.
  • Setzen Sie den Nachrichtenformatierer auf den ColoredFormatter bereitgestellt von der colorlog Bibliothek.

    import colorlog

    logger = colorlog.getLogger() logger.setLevel(colorlog.colorlog.logging.DEBUG)

    handler = colorlog.StreamHandler() handler.setFormatter(colorlog.ColoredFormatter()) logger.addHandler(handler)

    logger.debug("Debug message") logger.info("Information message") logger.warning("Warning message") logger.error("Error message") logger.critical("Critical message")

Ausgabe: enter image description here


UPDATE: zusätzliche Informationen

Einfach aktualisieren ColoredFormatter :

handler.setFormatter(colorlog.ColoredFormatter('%(log_color)s [%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s', datefmt='%a, %d %b %Y %H:%M:%S'))

Ausgabe: enter image description here


Paket:

pip install colorlog

Ausgabe:

Collecting colorlog
  Downloading colorlog-4.6.2-py2.py3-none-any.whl (10.0 kB)
Installing collected packages: colorlog
Successfully installed colorlog-4.6.2

7voto

aidanmelen Punkte 5260

Lösung mit Standard-Python3-Protokollierungsbibliothek

Ich freue mich sehr, diese flexible Lösung für das Einfärben von Stämmen mitzuteilen. Ich denke, es ist eine Verbesserung zu diesem Lösung by @SergeyPleshakov. Ich habe den Log-Datensatz von zusätzliche Zwerge um ein Präfix und ein Suffix für das Protokoll festzulegen. Dann beginnen und enden Präfix und Suffix standardmäßig mit Terminal-Farbcodes, die der Protokollstufe entsprechen.

Bonusfunktion

Das Extra prefix y suffix kann durch den Log-Aufruf überschrieben werden, was auch immer es sein soll. Sie wollen, dass Ihr Debug-Protokoll mit einem vorangestellt wird, warum nicht. Wenn Sie möchten, dass eines der Info-Protokolle grün statt standardmäßig ist, nur zu!

Definieren Sie das Terminal Color y ColorLogFormatter Klassen

import logging

class Color:
    """A class for terminal color codes."""

    BOLD = "\033[1m"
    BLUE = "\033[94m"
    WHITE = "\033[97m"
    GREEN = "\033[92m"
    YELLOW = "\033[93m"
    RED = "\033[91m"
    BOLD_WHITE = BOLD + WHITE
    BOLD_BLUE = BOLD + BLUE
    BOLD_GREEN = BOLD + GREEN
    BOLD_YELLOW = BOLD + YELLOW
    BOLD_RED = BOLD + RED
    END = "\033[0m"

class ColorLogFormatter(logging.Formatter):
    """A class for formatting colored logs."""

    FORMAT = "%(prefix)s%(msg)s%(suffix)s"

    LOG_LEVEL_COLOR = {
        "DEBUG": {'prefix': '', 'suffix': ''},
        "INFO": {'prefix': '', 'suffix': ''},
        "WARNING": {'prefix': Color.BOLD_YELLOW, 'suffix': Color.END},
        "ERROR": {'prefix': Color.BOLD_RED, 'suffix': Color.END},
        "CRITICAL": {'prefix': Color.BOLD_RED, 'suffix': Color.END},
    }

    def format(self, record):
        """Format log records with a default prefix and suffix to terminal color codes that corresponds to the log level name."""
        if not hasattr(record, 'prefix'):
            record.prefix = self.LOG_LEVEL_COLOR.get(record.levelname.upper()).get('prefix')

        if not hasattr(record, 'suffix'):
            record.suffix = self.LOG_LEVEL_COLOR.get(record.levelname.upper()).get('suffix')

        formatter = logging.Formatter(self.FORMAT)
        return formatter.format(record)

Logger instanziieren

logger = logging.getLogger('bobcat')
logger.setLevel('DEBUG')

stream_handler = logging.StreamHandler()
stream_handler.setFormatter(ColorLogFormatter())
logger.addHandler(stream_handler)

Und nutzen!

    logger.debug("This is debug", extra={'prefix': ' '})
    logger.info("This is info")
    logger.info("This is a green info", extra={'prefix': Color.GREEN, 'suffix': Color.END})
    logger.warning("This is warning")
    logger.error("This is error")
    logger.critical("This is critical")

und Voilà!

screenshot

7voto

Jonathan Hartley Punkte 14645

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

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

Jeder Text in der Ausgabe von "myprocess", der mit Gruppe 1 der Regex übereinstimmt, wird mit Farbe1 eingefärbt, Gruppe 2 mit Farbe2, usw.

Zum Beispiel:

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

d.h. die erste Regex-Gruppe (Parens) entspricht dem Anfangsdatum in der Logdatei, die zweite Gruppe entspricht einem Python-Dateinamen, einer Zeilennummer und einem Funktionsnamen, und die dritte Gruppe entspricht der darauf folgenden Logmeldung. Ich verwende auch eine parallele Abfolge von 'bold/normals' sowie die Abfolge der Farben. Das sieht dann so aus:

logfile with colored formatting

Beachten Sie, dass Zeilen oder Teile von Zeilen, die mit keiner meiner Regex übereinstimmen, trotzdem ausgegeben werden, dies ist also nicht wie 'grep --color' - nichts wird aus der Ausgabe herausgefiltert.

Natürlich ist diese Regex flexibel genug, dass Sie sie für jeden Prozess verwenden können, nicht nur für das Tailing von Logdateien. Normalerweise erstelle ich jedes Mal, wenn ich etwas einfärben möchte, spontan eine neue Regex. Aus diesem Grund ziehe ich colout jedem benutzerdefinierten Tool zum Einfärben von Logdateien vor, weil ich nur ein Tool lernen muss, unabhängig davon, was ich einfärben will: Logging, Testausgaben, Syntax-Highlighting von Codeschnipseln im Terminal usw.

Es vermeidet auch, ANSI-Codes in die Logdatei selbst zu schreiben, was IMHO eine schlechte Idee ist, weil es Dinge wie das Greifen nach Mustern in der Logdatei kaputt macht, es sei denn, Sie denken immer daran, die ANSI-Codes in Ihrer grep regex abzugleichen.

7voto

gravitation Punkte 1908

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, etwa so:

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

7voto

lain Punkte 420

Es gibt eine Vielzahl von Antworten. Aber keine spricht über Dekorateure. Also hier ist meine.

Weil es viel einfacher ist.

Es ist nicht nötig, 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 rot, die Fehlermeldungen blau usw. angezeigt. Wie in der Frage gefragt.

Wir könnten den Wrapper sogar so anpassen, dass er eine color Argument, um die Farbe der Nachricht dynamisch einzustellen, indem man logger.debug("message", color=GREY)

EDIT: Hier ist also der angepasste Dekorator, um Farben zur Laufzeit zu setzen:

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)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X