programar - rellenar turtle python
¿Cómo puedo colorear la salida de registro de Python? (24)
Hace algún tiempo, vi una aplicación Mono con salida de color, probablemente debido a su sistema de registro (porque todos los mensajes estaban estandarizados).
Ahora, Python tiene el módulo de logging
, que le permite especificar muchas opciones para personalizar la salida. Entonces, estoy imaginando que algo similar sería posible con Python, pero no puedo descubrir cómo hacer esto en cualquier lugar.
¿Hay alguna manera de hacer que el módulo de logging
Python se logging
en color?
Lo que quiero (por ejemplo) errores en rojo, mensajes de depuración en azul o amarillo, etc.
Por supuesto, esto probablemente requeriría un terminal compatible (la mayoría de los terminales modernos son); pero podría retroceder a la salida de logging
original si el color no es compatible.
¿Alguna idea de cómo puedo obtener una salida de color con el módulo de registro?
Actualicé el ejemplo de las etiquetas de soporte de airmind para el primer plano y el fondo. Simplemente use las variables de color $ BLACK - $ WHITE en la cadena del formateador de registro. Para establecer el fondo solo use $ 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
Así que ahora puedes simplemente hacer lo siguiente en tu archivo de configuración:
[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s
Ahora hay un módulo PyPi lanzado para la salida de registro de color personalizable:
https://pypi.python.org/pypi/rainbow_logging_handler/
y
https://github.com/laysakura/rainbow_logging_handler
Soporta Windows
Soporta Django
Colores personalizables
Como se distribuye como un huevo de Python, es muy fácil de instalar para cualquier aplicación de Python.
Aquí está mi solución:
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
Aquí hay una solución que debería funcionar en cualquier plataforma. Si no solo me lo dice y lo actualizo.
Cómo funciona: en la plataforma que admite ANSI escapes los está utilizando (no Windows) y en Windows usa llamadas API para cambiar los colores de la consola.
La secuencia de comandos piratea el método logging.StreamHandler.emit de la biblioteca estándar y le agrega un contenedor.
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())
Bueno, supongo que también podría agregar mi variación del registrador de colores.
Esto no es nada sofisticado, pero es muy simple de usar y no cambia el objeto de registro, por lo que evita el registro de las secuencias de escape ANSI en un archivo de registro si se usa un controlador de archivos. No afecta el formato del mensaje de registro.
Si ya está utilizando el formateador del módulo de registro , todo lo que tiene que hacer para obtener nombres de nivel de color es reemplazar el manejador de abogados Formatter por el ColoredFormatter. Si está registrando una aplicación completa, solo necesita hacer esto para el registrador de nivel superior.
colored_log.py
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])
Ejemplo de uso
app.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)
sub_module.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
Resultados
Salida de terminal
contenido de app.log
#!/usr/bin/env python
import logging
log = logging.getLogger(''main.sub_module'')
log.debug("Hello from the sub module")
Por supuesto, puede obtener todo lo que quiera con el formato del terminal y las salidas de archivos de registro. Sólo el nivel de registro será coloreado.
Espero que alguien lo encuentre útil y no sea mucho más de lo mismo. :)
Los archivos de ejemplo de Python se pueden descargar de este GitHub Gist: https://gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd
El problema con el que tuve problemas fue configurar correctamente el formateador:
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)
Y luego usar:
import coloured_log
import logging
coloured_log.init()
logging.info("info")
logging.debug("debug")
coloured_log.close() # restore colours
Hace años escribí un controlador de flujo de color para mi propio uso. Luego encontré esta página y encontré una colección de fragmentos de código que las personas están copiando / pegando :-(. Mi controlador de secuencias actualmente solo funciona en UNIX (Linux, Mac OS X) pero la ventaja es que está disponible en PyPI (y GitHub ) y es muy sencillo de usar. También tiene un modo de sintaxis Vim :-). En el futuro podría extenderlo para que funcione en Windows.
Para instalar el paquete:
$ pip install coloredlogs
Para confirmar que funciona:
$ coloredlogs --demo
Para comenzar con su propio código:
$ python
> import coloredlogs, logging
> coloredlogs.install()
> logging.info("It works!")
2014-07-30 21:21:26 peter-macbook root[7471] INFO It works!
El formato de registro predeterminado que se muestra en el ejemplo anterior contiene la fecha, la hora, el nombre de host, el nombre del registrador, el PID, el nivel de registro y el mensaje de registro. Esto es lo que parece en la práctica:
Hay toneladas de respuestas. Pero ninguno habla de decoradores. Así que aquí está la mía.
Porque es mucho más simple.
No es necesario importar nada, ni escribir ninguna subclase:
#!/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__)
Esto establece los errores en rojo, los mensajes de depuración en azul, y así sucesivamente. Como se pregunta en la pregunta.
Incluso podríamos adaptar la envoltura para tomar un argumento de color
para establecer dinámicamente el color del mensaje usando logger.debug("message", color=GREY)
EDIT: Así que aquí está el decorador adaptado para establecer colores en tiempo de ejecución:
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)
Mira la siguiente solución. El controlador de flujo debe ser lo que hace el coloreado, entonces tiene la opción de colorear palabras en lugar de solo la línea completa (con el formateador).
http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html
Modifiqué el ejemplo original proporcionado por Sorin y subclasificé StreamHandler a ColorizedConsoleHandler.
La desventaja de su solución es que modifica el mensaje, y como eso está modificando el mensaje de registro real, cualquier otro manejador recibirá el mensaje modificado también.
Esto resultó en archivos de registro con códigos de colores en nuestro caso porque usamos múltiples registradores.
La siguiente clase solo funciona en plataformas compatibles con ansi, pero debería ser trivial agregarle los códigos de colores 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)
Otro remix menor del enfoque de Airmind que mantiene todo en una clase:
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)
Para usar adjuntar el formateador a un controlador, algo como:
handler.setFormatter(ColorFormatter())
logger.addHandler(handler)
Puede importar el módulo de registro de color y usar su ColoredFormatter
para colorear los mensajes de registro.
Ejemplo
Boilerplate para el módulo 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__)
El código solo habilita los colores en los mensajes de registro, si el módulo de registro de color está instalado y si la salida realmente va a un terminal. Esto evita que las secuencias de escape se escriban en un archivo cuando se redirige la salida del registro.
Además, se configura un esquema de color personalizado que es más adecuado para terminales con fondo oscuro.
Algunos ejemplos de llamadas de registro:
log.debug (''Hello Debug'')
log.info (''Hello Info'')
log.warn (''Hello Warn'')
log.error (''Hello Error'')
log.critical(''Hello Critical'')
Salida:
Solución rápida y sucia para niveles de registro predefinidos y sin definir una nueva clase.
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))
Una herramienta simple pero muy flexible para colorear CUALQUIER texto terminal es '' colout ''.
pip install colout
myprocess | colout REGEX_WITH_GROUPS color1,color2...
Donde cualquier texto en la salida de ''myprocess'' que coincida con el grupo 1 de la expresión regular se coloreará con color1, grupo 2 con color2, etc.
Por ejemplo:
tail -f /var/log/mylogfile | colout ''^(/w+ /d+ [/d:]+)|(/w+/.py:/d+ .+/(/)): (.+)$'' white,black,cyan bold,bold,normal
es decir, el primer grupo de expresiones regulares (parens) coincide con la fecha inicial en el archivo de registro, el segundo grupo coincide con un nombre de archivo de python, número de línea y nombre de la función, y el tercer grupo coincide con el mensaje de registro que viene después de eso. También utilizo una secuencia paralela de "negrita / normal", así como la secuencia de colores. Esto se ve como
Tenga en cuenta que las líneas o partes de líneas que no coinciden con mi regex todavía tienen eco, así que esto no es como ''grep --color'' - nada se filtra fuera de la salida.
Obviamente, esto es lo suficientemente flexible como para que pueda usarlo con cualquier proceso, no solo siguiendo los archivos de registro. Por lo general, solo levanto un nuevo regex sobre la marcha cada vez que quiero colorear algo. Por esta razón, prefiero colout a cualquier herramienta personalizada para colorear archivos de log, porque solo necesito aprender una herramienta, independientemente de lo que esté coloreando: registro, salida de prueba, resaltado de sintaxis, fragmentos de código en el terminal, etc.
También evita el hecho de volcar códigos ANSI en el archivo de registro, lo cual, en mi humilde opinión, es una mala idea, porque romperá cosas como grepping para patrones en el archivo de registro a menos que siempre recuerde que coincida con los códigos ANSI en su regex grep.
Ya sabía acerca de los escapes de color, los usé en mi mensaje de bash hace un tiempo. Gracias de cualquier manera.
Lo que quería era integrarlo con el módulo de registro, lo que finalmente hice después de un par de intentos y errores.
Esto es lo que termino con:
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)
Y para usarlo, crea tu propio 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)
Por si alguien más lo necesita.
Tenga cuidado si está utilizando más de un registrador o controlador: ColoredFormatter
está cambiando el objeto de registro, que se pasa a otros controladores o se propaga a otros registradores. Si ha configurado los registradores de archivos, etc., es probable que no quiera tener los colores en los archivos de registro. Para evitar eso, probablemente sea mejor crear una copia del record
con copy.copy()
antes de manipular el atributo levelname, o restablecer el levelname al valor anterior, antes de devolver la cadena con formato (crédito a Michael en los comentarios).
Actualización : como esta es una picazón que he querido rascar durante tanto tiempo, seguí adelante y escribí una biblioteca para personas perezosas como yo, que solo quieren formas simples de hacer las cosas: zenlog
Colorlog es excelente para esto. Está disponible en PyPI (y, por lo tanto, se puede instalar a través de pip install colorlog
) y se mantiene activamente .
Aquí hay un fragmento rápido para copiar y pegar para configurar el registro e imprimir mensajes de registro de aspecto decente:
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")
Salida:
Este es un Enum que contiene los códigos de color:
class TerminalColour:
"""
Terminal colour formatting codes
"""
# https://.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''
Esto se puede aplicar a los nombres de cada nivel de registro. Ten en cuenta que este es un hack monstruoso.
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))
Tenga en cuenta que el formateador de su registro debe incluir el nombre del nivel de registro
%(levelname)
por ejemplo:
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''
},
Una solución más, con los colores de 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)
Llámalo una vez desde tu __main__
función. Tengo algo como esto allí:
options, arguments = p.parse_args()
log_level = logging.DEBUG if options.verbose else logging.WARNING
config_log(log_level)
También verifica que la salida sea una consola, de lo contrario no se utilizan colores.
Acabo de responder lo mismo en una pregunta similar: Python | cambiar el color del texto en shell
La idea es usar la librería clint . Que tiene soporte para MAC, Linux y Windows shells (CLI).
Si bien las otras soluciones parecen estar bien, tienen algunos problemas. Algunos colorean las líneas completas, lo que algunas veces no se desea, y otros omiten cualquier configuración que pueda tener todos juntos. La solución a continuación no afecta nada más que el mensaje en sí.
Código
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)
Ejemplo
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'')
Salida
[17:01:36]:WARNING:this should be yellow
[17:01:37]:ERROR :this should be red
Como puede ver, todo lo demás aún se imprime y permanece en su color inicial. Si desea cambiar algo más que el mensaje, simplemente puede pasar los códigos de color log_format
en el ejemplo.
Tengo dos presentaciones para agregar, una de las cuales colorea solo el mensaje (ColoredFormatter) y una de las que colorea la línea completa (ColorizingStreamHandler). Estos también incluyen más códigos de color ANSI que las soluciones anteriores.
Parte del contenido se ha obtenido (con modificaciones) de: La publicación anterior y http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html .
Colorea solo el mensaje:
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
Colorea toda la línea:
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
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)
Uso
Logger("File Name").info("This shows green text")
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)))`