Чтобы сделать отладку и диагностику на основе ведения журнала более увлекательной, я создал следующее улучшенное решение для ведения журнала 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 <[email protected]>" __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()