Чтобы сделать отладку и диагностику на основе ведения журнала более увлекательной, я создал следующее улучшенное решение для ведения журнала Python. Он раскрашивает и корректирует выходные данные журнала, чтобы можно было более эффективно работать с длинными записями журнала в локальном терминале. Он основан на пакете logutils от Vinay Sajip.
Что оно делает
- Целевые варианты использования отладки и диагностики, а не сценарии использования записи файла журнала
- Определяет, используете ли вы реальный терминал или входите в файловый поток
- Настраивает собственный обработчик журнала, который по-разному окрашивает части сообщений
- Работает в UNIX или Windows
Пример более длинной записи протокола терминала, которая делает большой вывод в logging.DEBUG level:
Исходный код и примеры использования приведены ниже.
1. Дальнейшие идеи
Как сделать вашу жизнь еще проще в журнале Python
- Сделайте это в правильном пакете или объедините с logutils
- Используйте информацию терминала для правильного отступа строк сообщений, чтобы 2+ строки делали отступ под начальной строкой и не нарушали структуру столбца (как получить ширину терминала в Python?)
- Сделать запись в Python, чтобы сохранить полное имя функции, а не просто пару модуль / функция, которая бесполезна в больших проектах
- Сделайте отслеживание обратных вызовов активируемым в iTerm 2. Обработчик щелчков по ссылкам iTerm 2 имеет поддержку регулярных выражений, но не знает о трассировках Python и нуждается в исправлении в iTerm 2
Пожалуйста, оставьте свои собственные идеи
2. Код
Исходный код (также в Gist ) — для цветной версии исходного кода см. Оригинальное сообщение в блоге :
# -*- coding: utf-8 -*-"""
Python logging tuned to extreme.
"""
__author__ ="Mikko Ohtamaa <mikko@opensourcehacker.com>"
__license__ ="MIT"import logging
from logutils.colorize importColorizingStreamHandlerclassRainbowLoggingHandler(ColorizingStreamHandler):
""" A colorful logging handler optimized for terminal debugging aestetichs.
- Designed for diagnosis and debug mode output - not for disk logs
- Highlight the content of logging message in more readable manner
- Show function and line, so you can trace where your logging messages
are coming from
- Keep timestamp compact
- Extra module/function output for traceability
The class provide few options as member variables you
would might want to customize after instiating the handler.
"""
# Define color for message payload
level_map ={
logging.DEBUG:(None,'cyan',False),
logging.INFO:(None,'white',False),
logging.WARNING:(None,'yellow',True),
logging.ERROR:(None,'red',True),
logging.CRITICAL:('red','white',True),
}
date_format ="%H:%m:%S"
#: How many characters reserve to function name logging
who_padding =22
#: Show logger name
show_name =True
def get_color(self, fg=None, bg=None, bold=False):
"""
Construct a terminal color code
:param fg: Symbolic name of foreground color
:param bg: Symbolic name of background color
:param bold: Brightness bit
"""
params =[]
if bg in self.color_map:
params.append(str(self.color_map[bg]+40))
if fg in self.color_map:
params.append(str(self.color_map[fg]+30))
if bold:
params.append('1')
color_code =''.join((self.csi,';'.join(params),'m'))
return color_code
def colorize(self, record):
"""
Get a special format string with ASCII color codes.
"""
# Dynamic message color based on logging level
if record.levelno in self.level_map:
fg, bg, bold = self.level_map[record.levelno]
else:
# Defaults
bg =None
fg ="white"
bold =False
# Magician's hat
# https://www.youtube.com/watch?v=1HRa4X07jdE
template =[
"[",
self.get_color("black",None,True),
"%(asctime)s",
self.reset,
"] ",
self.get_color("white",None,True)if self.show_name else"",
"%(name)s "if self.show_name else"",
"%(padded_who)s",
self.reset,
" ",
self.get_color(bg, fg, bold),
"%(message)s",
self.reset,
]
format ="".join(template)
who =[self.get_color("green"),
getattr(record,"funcName",""),
"()",
self.get_color("black",None,True),
":",
self.get_color("cyan"),
str(getattr(record,"lineno",0))]
who ="".join(who)
# We need to calculate padding length manualy
# as color codes mess up string length based calcs
unformatted_who = getattr(record,"funcName","")+"()"+ \
":"+ str(getattr(record,"lineno",0))
if len(unformatted_who)< self.who_padding:
spaces =" "*(self.who_padding - len(unformatted_who))
else:
spaces =""
record.padded_who = who + spaces
formatter = logging.Formatter(format, self.date_format)
self.colorize_traceback(formatter, record)
output = formatter.format(record)
# Clean cache so the color codes of traceback don't leak to other formatters
record.ext_text =None
return output
def colorize_traceback(self, formatter, record):
"""
Turn traceback text to red.
"""
if record.exc_info:
# Cache the traceback text to avoid converting it multiple times
# (it's constant anyway)
record.exc_text ="".join([
self.get_color("red"),
formatter.formatException(record.exc_info),
self.reset,
])
def format(self, record):
"""
Formats a record for output.
Takes a custom formatting path on a terminal.
"""
if self.is_tty:
message = self.colorize(record)
else:
message = logging.StreamHandler.format(self, record)
return message
if __name__ =="__main__":
# Run test output on stdout
import sys
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG)
handler =RainbowLoggingHandler(sys.stdout)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
root_logger.addHandler(handler)
logger = logging.getLogger("test")
def test_func():
logger.debug("debug msg")
logger.info("info msg")
logger.warn("warn msg")
def test_func_2():
logger.error("error msg")
logger.critical("critical msg")
try:
raiseRuntimeError("Opa!")
exceptExceptionas e:
logger.exception(e)
test_func()
test_func_2()


