Comment puis-je colorer la sortie de journalisation Python?

il y a quelque temps, j'ai vu une application Mono avec une sortie colorée, probablement à cause de son système de log (parce que tous les messages étaient normalisés).

maintenant, Python a le module logging , qui vous permet de spécifier beaucoup d'options pour personnaliser la sortie. Donc, j'imagine que quelque chose de similaire serait possible avec Python, mais je ne peux pas trouver comment faire ça n'importe où.

y a-t-il un moyen de faire la sortie du module Python logging en de couleur?

Ce que je veux (par exemple) les erreurs en rouge, les messages de débogage en bleu ou en jaune, et ainsi de suite.

bien sûr, cela nécessiterait probablement un terminal compatible (la plupart des terminaux modernes sont); mais je pourrais retomber à la sortie originale logging si la couleur n'est pas prise en charge.

des idées pour obtenir une sortie colorée avec le module de journalisation?

260
demandé sur HaskellElephant 2008-12-21 06:57:45

24 réponses

je savais déjà que les couleurs s'échappaient, je les ai utilisées dans mon invite de bash il y a un moment. Merci quand même.

Ce que je voulais, c'était l'intégrer au module de journalisation, ce que j'ai finalement fait après quelques essais et erreurs.

Voici ce que je finis avec:

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 = "3[0m"
COLOR_SEQ = "3[1;%dm"
BOLD_SEQ = "3[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)

Et pour l'utiliser, créez votre propre Enregistreur:

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

au cas où quelqu'un d'autre en aurait besoin.

Faites attention si vous utilisez plus d'un logger ou handler: ColoredFormatter change l'objet record, qui est transmis plus loin à d'autres handlers ou propagé à d'autres loggers. Si vous avez configuré des loggers de fichiers, etc. vous ne voulez probablement pas à avoir les couleurs dans les fichiers journaux. Pour éviter cela, il est probablement préférable de simplement créer une copie de record avec copy.copy() avant de manipuler l'attribut levelname, ou de réinitialiser le nom de niveau à la valeur précédente, avant de retourner le chaîne formatée (crédit à Michael dans les commentaires).

150
répondu airmind 2015-07-03 14:51:32

il y a des années j'ai écrit un gestionnaire de flux coloré pour mon propre usage. Puis je suis tombé sur cette page et j'ai trouvé une collection d'extraits de code que les gens copient/collent :-(. Mon gestionnaire de flux ne fonctionne actuellement que sur UNIX (Linux, Mac OS X) mais l'avantage est qu'il est disponible sur PyPI (et GitHub ) et il est très simple à utiliser. Il a aussi un mode de syntaxe Vim :-). Dans le futur je pourrais l'étendre pour travailler sur Windows.

pour installer le paquet:

$ pip install coloredlogs

pour confirmer qu'il fonctionne:

$ coloredlogs --demo

pour commencer avec votre propre code:

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

le format de log par défaut montré dans l'exemple ci-dessus contient la date, l'heure, le nom de l'hôte, le nom du logger, le PID, le niveau de log et le message de log. C'est à quoi il ressemble dans la pratique:

Screenshot of coloredlogs output

87
répondu xolox 2016-11-16 16:22:11

Voici une solution qui devrait fonctionner sur n'importe quelle plateforme. Si ça ne me le dit pas, je le mettrai à jour.

comment ça marche: sur la plate-forme supportant les escapades ANSI on les utilise (non-Windows) et sur Windows on utilise des appels API pour changer les couleurs de la console.

le script pirate la journalisation.StreamHandler.emit method de la bibliothèque standard y ajoutant un wrapper.

TestColorer.py

# Usage: add Colorer.py near you script and import it.
import logging
import Colorer

logging.warn("a warning")
logging.error("some error")
logging.info("some info")

Colorer.py

#!/usr/bin/env python
# encoding: utf-8
import logging
# now we patch Python code to add color support to logging.StreamHandler
def add_coloring_to_emit_windows(fn):
        # add methods we need to the class
    def _out_handle(self):
        import ctypes
        return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
    out_handle = property(_out_handle)

    def _set_color(self, code):
        import ctypes
        # Constants from the Windows API
        self.STD_OUTPUT_HANDLE = -11
        hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
        ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code)

    setattr(logging.StreamHandler, '_set_color', _set_color)

    def new(*args):
        FOREGROUND_BLUE      = 0x0001 # text color contains blue.
        FOREGROUND_GREEN     = 0x0002 # text color contains green.
        FOREGROUND_RED       = 0x0004 # text color contains red.
        FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
        FOREGROUND_WHITE     = FOREGROUND_BLUE|FOREGROUND_GREEN |FOREGROUND_RED
       # winbase.h
        STD_INPUT_HANDLE = -10
        STD_OUTPUT_HANDLE = -11
        STD_ERROR_HANDLE = -12

        # wincon.h
        FOREGROUND_BLACK     = 0x0000
        FOREGROUND_BLUE      = 0x0001
        FOREGROUND_GREEN     = 0x0002
        FOREGROUND_CYAN      = 0x0003
        FOREGROUND_RED       = 0x0004
        FOREGROUND_MAGENTA   = 0x0005
        FOREGROUND_YELLOW    = 0x0006
        FOREGROUND_GREY      = 0x0007
        FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.

        BACKGROUND_BLACK     = 0x0000
        BACKGROUND_BLUE      = 0x0010
        BACKGROUND_GREEN     = 0x0020
        BACKGROUND_CYAN      = 0x0030
        BACKGROUND_RED       = 0x0040
        BACKGROUND_MAGENTA   = 0x0050
        BACKGROUND_YELLOW    = 0x0060
        BACKGROUND_GREY      = 0x0070
        BACKGROUND_INTENSITY = 0x0080 # background color is intensified.     

        levelno = args[1].levelno
        if(levelno>=50):
            color = BACKGROUND_YELLOW | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY 
        elif(levelno>=40):
            color = FOREGROUND_RED | FOREGROUND_INTENSITY
        elif(levelno>=30):
            color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY
        elif(levelno>=20):
            color = FOREGROUND_GREEN
        elif(levelno>=10):
            color = FOREGROUND_MAGENTA
        else:
            color =  FOREGROUND_WHITE
        args[0]._set_color(color)

        ret = fn(*args)
        args[0]._set_color( FOREGROUND_WHITE )
        #print "after"
        return ret
    return new

def add_coloring_to_emit_ansi(fn):
    # add methods we need to the class
    def new(*args):
        levelno = args[1].levelno
        if(levelno>=50):
            color = '\x1b[31m' # red
        elif(levelno>=40):
            color = '\x1b[31m' # red
        elif(levelno>=30):
            color = '\x1b[33m' # yellow
        elif(levelno>=20):
            color = '\x1b[32m' # green 
        elif(levelno>=10):
            color = '\x1b[35m' # pink
        else:
            color = '\x1b[0m' # normal
        args[1].msg = color + args[1].msg +  '\x1b[0m'  # normal
        #print "after"
        return fn(*args)
    return new

import platform
if platform.system()=='Windows':
    # Windows does not support ANSI escapes and we are using API calls to set the console color
    logging.StreamHandler.emit = add_coloring_to_emit_windows(logging.StreamHandler.emit)
else:
    # all non-Windows platforms are supporting ANSI escapes so we use them
    logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)
    #log = logging.getLogger()
    #log.addFilter(log_filter())
    #//hdlr = logging.StreamHandler()
    #//hdlr.setFormatter(formatter())
64
répondu sorin 2012-08-21 05:59:02

solution rapide et sale pour des niveaux de log prédéfinis et sans définir une nouvelle classe.

logging.addLevelName( logging.WARNING, "3[1;31m%s3[1;0m" % logging.getLevelName(logging.WARNING))
logging.addLevelName( logging.ERROR, "3[1;41m%s3[1;0m" % logging.getLevelName(logging.ERROR))
52
répondu ABC 2014-04-10 15:26:10

mise à jour : parce que c'est une démangeaison que je voulais gratter depuis si longtemps, je suis allé de l'avant et a écrit une bibliothèque pour les gens paresseux comme moi qui veulent juste des façons simples de faire les choses: zenlog

Colorlog est excellent pour cela. C'est disponible sur PyPI (et donc installable par pip install colorlog ) et est activement maintenu .

voici un une copie-et-pastéable rapide pour configurer la journalisation et imprimer des messages de log d'apparence décente:

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

sortie:

Colorlog output

44
répondu rlafuente 2017-04-24 16:27:34

j'ai mis à jour l'exemple d'airmind supporting tags pour le premier plan et le fond. Il suffit d'utiliser les variables de couleur $Noir - $blanc dans votre chaîne de formatage de journal. Pour définir le fond il suffit d'utiliser $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 = "3[0m"
COLOR_SEQ = "3[1;%dm"
BOLD_SEQ  = "3[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

donc maintenant vous pouvez simplement faire ce qui suit dans votre fichier de configuration:

[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s
14
répondu camillobruni 2010-03-28 12:49:40

vous pouvez importer le module colorlog et utiliser son ColoredFormatter pour colorer les messages de log.

exemple

Standard pour le module principal:

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

le code n'autorise les couleurs que dans les messages log, si le module colorlog est installé et si la sortie va effectivement à un terminal. Cela évite que des séquences d'échappement soient écrites dans un fichier lorsque la sortie du journal est redirigée.

en outre, un arrangement de couleur personnalisé est la configuration qui est mieux adapté pour les terminaux avec le fond sombre.

quelques exemples d'appels de journalisation:

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

sortie:

enter image description here

11
répondu maxschlepzig 2017-06-23 08:19:51

regardez la solution suivante. Le gestionnaire de flux devrait être la chose faisant la coloration, alors vous avez l'option de la coloration des mots plutôt que juste la ligne entière (avec le formatage).

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

10
répondu Nick 2011-01-14 13:52:14

j'ai modifié L'exemple original fourni par Sorin et subclassed StreamHandler en un ColorizedConsoleHandler.

l'inconvénient de leur solution est qu'elle modifie le message, et parce que cela modifie le logmessage actuel, tout autre gestionnaire recevra le message modifié.

cela a donné des logfiles avec des colorcodes dans notre cas parce que nous utilisons plusieurs loggers.

La classe ci-dessous uniquement fonctionne sur les plates-formes qui prennent en charge ansi, mais il devrait être trivial d'y ajouter les couleurs de windows.

import copy
import logging


class ColoredConsoleHandler(logging.StreamHandler):
    def emit(self, record):
        # Need to make a actual copy of the record
        # to prevent altering the message for other loggers
        myrecord = copy.copy(record)
        levelno = myrecord.levelno
        if(levelno >= 50):  # CRITICAL / FATAL
            color = '\x1b[31m'  # red
        elif(levelno >= 40):  # ERROR
            color = '\x1b[31m'  # red
        elif(levelno >= 30):  # WARNING
            color = '\x1b[33m'  # yellow
        elif(levelno >= 20):  # INFO
            color = '\x1b[32m'  # green
        elif(levelno >= 10):  # DEBUG
            color = '\x1b[35m'  # pink
        else:  # NOTSET and anything else
            color = '\x1b[0m'  # normal
        myrecord.msg = color + str(myrecord.msg) + '\x1b[0m'  # normal
        logging.StreamHandler.emit(self, myrecord)
9
répondu Ramonster 2012-07-03 12:20:41

maintenant il y a un module PyPI libéré pour la sortie de journalisation colorée personnalisable:

https://pypi.python.org/pypi/rainbow_logging_handler /

et

https://github.com/laysakura/rainbow_logging_handler

  • Prend En Charge Windows

  • "151900920 Soutient" Django

  • Personnalisable couleurs

comme il est distribué comme un oeuf de Python, il est très facile à installer pour n'importe quelle application de Python.

9
répondu Mikko Ohtamaa 2013-12-20 08:08:40

un autre remix mineur de l'approche d'airmind qui garde tout dans une classe:

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 = "3[0m"
  COLOR_SEQ = "3[1;%dm"
  BOLD_SEQ = "3[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)

pour utiliser attacher le formatteur à un handler, quelque chose comme:

handler.setFormatter(ColorFormatter())
logger.addHandler(handler)
6
répondu gravitation 2010-04-19 11:12:33
import logging
import sys

colors = {'pink': '3[95m', 'blue': '3[94m', 'green': '3[92m', 'yellow': '3[93m', 'red': '3[91m',
      'ENDC': '3[0m', 'bold': '3[1m', 'underline': '3[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
répondu Serhii Khachko 2015-08-13 14:52:25

un outil simple mais très flexible pour colorer N'importe quel texte de terminal est colout ".

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

où tout texte dans la sortie de 'myprocess' qui correspond au groupe 1 du regex sera coloré avec la couleur 1, Le Groupe 2 avec la couleur 2, etc.

par exemple:

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

c'est-à-dire que le premier groupe regex (parens) correspond à la date initiale dans le fichier LOG, le second groupe correspond à un nom de fichier python, numéro de ligne et le nom de la fonction, et le troisième groupe correspond au message de journal qui vient après. J'utilise aussi, en parallèle, une séquence de "gras/normales" ainsi que la séquence de couleurs. On dirait:

logfile with colored formatting

notez que les lignes ou parties de lignes qui ne correspondent à aucun de mes regex sont toujours en écho, donc ce n'est pas comme 'grep --color' - rien n'est filtré de la sortie.

Évidemment, c'est assez flexible pour que vous puissiez l'utiliser avec n'importe quel processus, pas seulement avec des logfiles. D'habitude, je concocte un nouveau regex à la volée chaque fois que je veux coloriser quelque chose. Pour cette raison, je préfère colout à tout outil personnalisé logfile-coloring, parce que je n'ai besoin d'apprendre qu'un seul outil, indépendamment de ce que je colore: journalisation, sortie de test, mise en évidence syntaxique des bribes de code dans le terminal, etc.

il évite également le dumping des codes ANSI dans le fichier journal lui-même, ce qui IMHO est une mauvaise idée, parce qu'il va briser des choses comme grepping pour les modèles dans le fichier log sauf si vous vous rappelez toujours de faire correspondre les codes ANSI dans votre grep regex.

5
répondu Jonathan Hartley 2015-08-19 11:00:38

Il y a des tonnes de réponses. Mais aucun ne parle des décorateurs. Voici donc la mienne.

Parce que c'est beaucoup plus simple.

il n'est pas nécessaire d'importer quoi que ce soit, ni d'écrire une sous-classe:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import logging


NO_COLOR = "[m"
RED, GREEN, ORANGE, BLUE, PURPLE, LBLUE, GREY = \
    map("[%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__)

définit les erreurs en rouge, les messages de débogage en bleu, et ainsi de suite. Comme demandé dans la question.

nous pourrions même adapter l'emballage pour prendre un argument color pour mettre dynamicaly la couleur du message en utilisant logger.debug("message", color=GREY)

EDIT: Donc voici le décorateur adapté pour mettre les couleurs à l'exécution:

def add_color(logger_method, _color):
  def wrapper(message, *args, **kwargs):
    color = kwargs.pop("color", _color)
    if isinstance(color, int):
      color = "[%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)
4
répondu lain 2017-11-22 13:44:31

voici ma solution:

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
répondu veegee 2014-10-03 17:10:01

le morceau que j'ai eu des problèmes avec était la mise en place du formatteur correctement:

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)

et ensuite à utiliser:

import coloured_log
import logging

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

coloured_log.close()    # restore colours
1
répondu Nick 2011-01-14 13:53:50

bien que les autres solutions semblent bonnes, elles ont certains problèmes. Certains colorent les lignes entières qui parfois n'est pas voulu et d'autres omettent toute configuration que vous pourriez avoir tous ensemble. La solution ci-dessous n'affecte que le message lui-même.

Code

class ColoredFormatter(logging.Formatter):
    def format(self, record):
        if record.levelno == logging.WARNING:
            record.msg = '3[93m%s3[0m' % record.msg
        elif record.levelno == logging.ERROR:
            record.msg = '3[91m%s3[0m' % record.msg
        return logging.Formatter.format(self, record)

exemple

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

Sortie

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

comme vous le voyez, tout le reste est encore sorti et reste dans sa couleur initiale. Si vous voulez changer autre chose que le message, vous pouvez simplement passer les codes de couleur à log_format dans l'exemple.

1
répondu Pithikos 2015-04-29 16:32:54

j'ai deux soumissions à ajouter, l'une colorise juste le message (ColoredFormatter), et l'autre colorise la ligne entière (ColorizingStreamHandler). Ceux-ci comprennent également plus de codes de couleur ANSI que les solutions précédentes.

une partie du contenu provient (avec modification) de: Le post ci-dessus, et http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html .

colorise le message seulement:

class ColoredFormatter(logging.Formatter):
    """Special custom formatter for colorizing log messages!"""

    BLACK = '3[0;30m'
    RED = '3[0;31m'
    GREEN = '3[0;32m'
    BROWN = '3[0;33m'
    BLUE = '3[0;34m'
    PURPLE = '3[0;35m'
    CYAN = '3[0;36m'
    GREY = '3[0;37m'

    DARK_GREY = '3[1;30m'
    LIGHT_RED = '3[1;31m'
    LIGHT_GREEN = '3[1;32m'
    YELLOW = '3[1;33m'
    LIGHT_BLUE = '3[1;34m'
    LIGHT_PURPLE = '3[1;35m'
    LIGHT_CYAN = '3[1;36m'
    WHITE = '3[1;37m'

    RESET = "3[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

colorise toute la ligne:

class ColorizingStreamHandler(logging.StreamHandler):

    BLACK = '3[0;30m'
    RED = '3[0;31m'
    GREEN = '3[0;32m'
    BROWN = '3[0;33m'
    BLUE = '3[0;34m'
    PURPLE = '3[0;35m'
    CYAN = '3[0;36m'
    GREY = '3[0;37m'

    DARK_GREY = '3[1;30m'
    LIGHT_RED = '3[1;31m'
    LIGHT_GREEN = '3[1;32m'
    YELLOW = '3[1;33m'
    LIGHT_BLUE = '3[1;34m'
    LIGHT_PURPLE = '3[1;35m'
    LIGHT_CYAN = '3[1;36m'
    WHITE = '3[1;37m'

    RESET = "3[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
répondu ZetaSyanthis 2015-06-04 16:56:59

Eh bien, je suppose que je pourrais aussi bien ajouter ma variation de l'enregistreur de couleur.

ce n'est rien de fantaisiste, mais c'est très simple à utiliser et ne change pas l'objet d'enregistrement, évitant ainsi de journaliser les séquences d'échappement ANSI vers un fichier log si un gestionnaire de fichier est utilisé. Il n'affecte pas le formatage du message de journal.

si vous utilisez déjà le Formatteur du module de journalisation , tout ce que vous avez à faire pour obtenir des noms de niveaux colorés est pour remplacer votre conseiller Formatter handlers par la ColoredFormatter. Si vous enregistrez une application entière, vous n'avez besoin de le faire que pour l'enregistreur de haut niveau.

colored_log.py

#!/usr/bin/env python

from copy import copy
from logging import Formatter

MAPPING = {
    'DEBUG'   : 37, # white
    'INFO'    : 36, # cyan
    'WARNING' : 33, # yellow
    'ERROR'   : 31, # red
    'CRITICAL': 41, # white on red bg
}

PREFIX = '3['
SUFFIX = '3[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)

exemple d'usage

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

résultats

Borne de sortie

Terminal output

app.journal contenu

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 - ERROR - Hello from the sub module

bien sûr, vous pouvez obtenir aussi fantaisie que vous voulez avec le formatage du terminal et les sorties de fichier journal. Seul le niveau de journal sera colorisée.

j'espère que quelqu'un trouvera cela utile et ce n'est pas trop la même chose. :)

le Les fichiers d'exemple Python peuvent être téléchargés à partir de ce Github Gist: https://gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd

1
répondu KCJ 2017-11-01 05:22:44

il s'agit d'un Enum contenant les codes de couleur:

class TerminalColour:
    """
    Terminal colour formatting codes
    """
    # /q/print-in-terminal-with-colors-28239/"{}{}{}".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))

notez que votre formater log doit inclure le nom du niveau de log

%(levelname)

par exemple:

    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
répondu Joe Heffer 2018-06-21 13:34:08

Utiliser pyfancy .

exemple:

print(pyfancy.RED + "Hello Red!" + pyfancy.END)
0
répondu WebMaster 2015-02-01 18:32:25

juste une autre solution, avec les couleurs de ZetaSyanthis:

def config_log(log_level):

    def set_color(level, code):
        level_fmt = "3[1;" + str(code) + "m%s3[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)

appelez-le une fois à partir de votre fonction __main__ . J'ai quelque chose comme ça:

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

il vérifie également que la sortie est une console, sinon aucune couleur n'est utilisée.

0
répondu yucer 2017-07-29 22:05:58
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 = "3[92m"
    __RED = '3[91m'
    __ENDC = '3[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)

Utilisation

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

0
répondu estifanos gebrehiwot 2017-09-22 13:16:02

vient de répondre à la même question sur une question similaire: Python / changer la couleur du texte dans l'interpréteur de commandes

l'idée est d'utiliser la bibliothèque clint . Qui prend en charge les interpréteurs de commandes MAC, Linux et Windows (CLI).

-1
répondu Kostanos 2017-05-23 10:31:37