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?
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).
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:
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())
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))
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:
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
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:
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
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)
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.
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)
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)))`
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:
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.
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)
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])
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
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.
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
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
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
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'
},
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.
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")
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).