На PyCon 2018 Марио Корчеро представил процесс упрощения ведения журнала во время разработки.
Вся речь включает в себя:
- Почему важно вести журнал
- Как проходит процесс регистрации
- как войти
- Как настроить ведение журнала
- Распространенные ошибки при использовании ведения журнала
Давайте разберемся со всем речевым процессом, по сути, ядром является знакомство с использованием и настройкой модуля логирования.
Важность ведения журнала
В процессе разработки, если возникает проблема с работающей программой, мы можем использовать наш собственный инструмент отладки, чтобы определить, на каком этапе возникла проблема.Если есть проблема, ее легко устранить. Но после того, как разработка программы будет завершена, мы развернем ее в производственной среде.В это время код эквивалентен запуску в среде черного ящика.Мы можем видеть только эффект от его работы, но мы не можем непосредственно видеть процесс выполнения кода, статус каждого шага. В этой среде неизбежно, что где-то в процессе эксплуатации возникнет проблема, и даже эта проблема может быть проблемой, с которой мы не столкнулись в процессе разработки, что нам делать в этой ситуации?
Если мы можем знать только явление текущей проблемы без какой-либо другой информации, если мы хотим решить эту проблему, мы можем только попытаться воспроизвести ее в соответствии с явлением проблемы, а затем отладить ее шаг за шагом. Боюсь, это очень сложно. Существует высокая вероятность того, что мы не сможем точно воспроизвести эту проблему, и процесс отладки займет много времени, поэтому, как только возникнет проблема в производственной среде, ремонт станет очень сложным. . Но если мы сделали записи журнала в то время, будь то нормальная работа или отчет об ошибке, есть соответствующие записи времени, записи состояния, записи об ошибках и т. д., то мы можем легко отследить возникновение рабочего процесса в это время. Что за ситуация, чтобы можно было быстро устранить проблему.
Поэтому ведение журнала очень необходимо.Если какое-либо программное обеспечение не имеет стандартного ведения журнала, оно не может считаться квалифицированным программным обеспечением. Как разработчики, мы должны обратить внимание на ведение журнала и хорошо поработать над ним.
Структура процесса ведения журнала
Итак, как в Python это можно считать относительно стандартным процессом ведения журнала? Может быть, многие люди будут использовать оператор печати для вывода некоторой текущей информации, а затем просматривать ее в консоли, а затем перенаправлять вывод в поток вывода файла и сохранять его в файл при запуске.Это на самом деле очень нестандартно. является стандартом в Python.Модуль ведения журнала, мы можем использовать его для записи аннотаций, мы можем использовать его для более удобной записи, и в то же время мы можем сделать более удобное разграничение уровней и записать некоторую дополнительную информацию журнала, такую как время, информация о работающем модуле и т.д.
Далее, давайте взглянем на общую структуру процесса регистрации.
Как показано на рисунке, всю структуру ведения журнала можно разделить на следующие части:
- Logger: Основной класс Logger — это объект, созданный, когда мы регистрируем записи.Мы можем вызвать его методы для передачи шаблонов журнала и информации для создания записей журнала, которые называются Log Records.
- Запись журнала: относится к сгенерированным записям журнала.
- Handler: класс, используемый для обработки записей журнала. Он может выводить запись журнала в указанное нами расположение и форму хранения журнала. Например, если мы можем указать запись журнала на удаленный сервер через протокол FTP, обработчик поможет нам завершите это.
- Formatter: на самом деле сгенерированные записи журнала также являются объектами, поэтому, если мы хотим сохранить их в тексте журнала, который нам нужен, нам нужен процесс форматирования, затем этот процесс завершается Formatter, возвращается строка журнала, которая затем возвращается обработчику для обработки.
- Фильтр: Кроме того, при сохранении журналов нам может не понадобиться сохранять их все. Нам может потребоваться сохранить только те части, которые мы хотим, поэтому нам нужно отфильтровать их перед сохранением и оставить нужные журналы, например, сохранить только определенные журналы на уровне журнала или сохранять только журналы, содержащие определенное ключевое слово и т. д., то этот процесс фильтрации передается для завершения в фильтр.
- Родительский обработчик: между обработчиками может быть иерархическая связь, поэтому разные обработчики используют один и тот же функциональный код.
Выше приведена базовая структура и объектные функции всего модуля ведения журнала.После понимания мы подробно узнаем об использовании модуля ведения журнала.
Связанное использование ведения журнала
В целом модуль логирования имеет ряд преимуществ перед печатью:
- Уровень журнала может быть установлен в модуле ведения журнала, и соответствующие журналы могут быть записаны путем установки разных уровней вывода в разных версиях (например, в среде разработки и рабочей среде), что очень гибко.
- Выходная информация печати будет выводиться в стандартный поток вывода, а модуль ведения журнала является более гибким и может быть настроен на вывод в любое место, например запись в файл, запись на удаленный сервер и т. д.
- Модуль ведения журнала имеет гибкие функции настройки и форматирования, такие как настройка и вывод информации о текущем модуле, времени работы и т. д., что более удобно и просто в использовании, чем строковое форматирование печати.
Давайте предварительно рассмотрим базовое использование модуля ведения журнала, сначала рассмотрим его на примере:
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')
Здесь мы сначала вводим модуль ведения журнала, а затем выполняем базовую настройку.Здесь информация об уровне и информация о формате настраиваются через basicConfig.Здесь уровень настраивается как информация INFO, то есть выводится только информация на уровне INFO. Кроме того, здесь указывается формат формата Строка, включающая asctime, имя, имя уровня, сообщение, четыре содержимого, представляющие время выполнения, имя модуля, уровень журнала, содержимое журнала, поэтому выходное содержимое представляет собой комбинацию этих четырех, это глобальная настройка логирования.
Затем объявляется объект Logger, который является основным классом вывода журнала.Вызов метода info() объекта может вывести информацию журнала уровня INFO, а вызов метода debug() может вывести информацию журнала уровня DEBUG, которая очень удобно. При инициализации мы передали имя модуля, которое используется непосредственно здесь__name__
Вместо этого это имя модуля. Если вы запустите скрипт напрямую, оно будет__main__
, если это модуль импорта, то это имя импортируемого модуля.Имя этой переменной в разных модулях разное, поэтому обычно используется__name__
Хорошо указать это, и тогда выводятся четыре сообщения журнала, в том числе два сообщения INFO, одно WARNING и одно сообщение DEBUG.Давайте посмотрим на результаты вывода:
2018-06-03 13:42:43,526 - __main__ - INFO - This is a log info
2018-06-03 13:42:43,526 - __main__ - WARNING - Warning exists
2018-06-03 13:42:43,526 - __main__ - INFO - Finish
Видно, что в результате вывода есть три сообщения журнала, каждый журнал соответствует указанному форматированному содержимому, и мы обнаружили, что информация DEBUG не выводится, это связано с тем, что мы установили вывод на уровень INFO во время глобальной конфигурации, поэтому информация уровня DEBUG отфильтровывается.
В это время, если мы установим уровень журнала вывода на DEBUG, мы увидим вывод журнала уровня DEBUG:
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
Выходной результат:
2018-06-03 13:49:22,770 - __main__ - INFO - This is a log info
2018-06-03 13:49:22,770 - __main__ - DEBUG - Debugging
2018-06-03 13:49:22,770 - __main__ - WARNING - Warning exists
2018-06-03 13:49:22,770 - __main__ - INFO - Finish
Видно, что по сравнению с печатью, через код только что мы можем выводить время, имя модуля и информацию журнала разных уровней для различения и фильтрации.Это более гибко?
Конечно, это лишь малая часть функций модуля логирования, далее подробно рассмотрим параметры basicConfig:
- имя_файла: имя файла вывода журнала.Если указана эта информация, вместо StreamHandler будет включен FileHandler, поэтому информация журнала будет выводиться в файл.
- filemode: это режим записи указанного файла журнала.Есть две формы, одна w, а другая a, которые представляют собой запись после очистки и дополнительную запись.
- формат: определяет выходной формат информации журнала, т. е. параметры, показанные в приведенном выше примере.Подробные параметры см. в:docs.Python.org/3/library/…, некоторые параметры следующие:
- %(levelno)s: вывести значение уровня журнала.
- %(levelname)s: вывести имя уровня журнала.
- %(pathname)s: вывести путь к выполняемой в данный момент программе, которая на самом деле является sys.argv[0].
- %(filename)s: вывести имя выполняемой в данный момент программы.
- %(funcName)s: Текущая функция, печатающая журнал.
- %(lineno)d: вывести номер текущей строки журнала.
- %(asctime)s: время печати журнала.
- %(thread)d: вывести идентификатор потока.
- %(threadName)s: вывести имя потока.
- %(process)d: вывести идентификатор процесса.
- %(processName)s: вывести имя потока.
- %(module)s: вывести имя модуля.
- %(message)s: Распечатать информацию журнала.
- datefmt: определяет выходной формат времени.
- стиль: если указан параметр формата, этот параметр может указать стиль заполнителя для форматирования, например %, {, $ и т. д.
- уровень: указывает тип вывода журнала, программа будет выводить информацию больше или равную этому уровню.
- stream: StreamHandler будет использоваться по умолчанию, если имя файла не указано, а stream может указывать инициализированный файловый поток.
- обработчики: вы можете указать обработчики, используемые для обработки журнала, которые должны быть повторяемыми.
Давайте посмотрим на другой пример:
import logging
logging.basicConfig(level=logging.DEBUG,
filename='output.log',
datefmt='%Y/%m/%d %H:%M:%S',
format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s')
logger = logging.getLogger(__name__)
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')
Здесь мы указываем имя выходного файла как output.log, а также указываем формат вывода даты, где формат года, месяца и дня становится%Y/%m/%d
, а формат выходного формата добавляет две информации: lineno и module.После запуска он сгенерирует файл output.log со следующим содержимым:
2018/06/03 14:43:26 - __main__ - INFO - 9 - demo3 - This is a log info
2018/06/03 14:43:26 - __main__ - DEBUG - 10 - demo3 - Debugging
2018/06/03 14:43:26 - __main__ - WARNING - 11 - demo3 - Warning exists
2018/06/03 14:43:26 - __main__ - INFO - 12 - demo3 - Finish
Вы можете видеть, что лог будет выводиться в файл, и одновременно будет выводиться номер строки, имя модуля и другая информация.
Выше мы сделали некоторую глобальную настройку через basicConfig.Мы также можем использовать Formatter и Handler для более гибкой обработки.Давайте посмотрим.
Level
Во-первых, давайте взглянем на информацию об уровне выходного журнала. Модуль ведения журнала предоставляет следующие уровни. Каждый уровень фактически соответствует значению. Список выглядит следующим образом:
оценка | Численная величина |
---|---|
CRITICAL | 50 |
FATAL | 50 |
ERROR | 40 |
WARNING | 30 |
WARN | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
Самые высокие уровни здесь — CRITICAL и FATAL, оба из которых соответствуют 50, а краткая форма WARN также предусмотрена для WARNING, оба из которых соответствуют 30.
Когда мы устанавливаем уровень вывода, система будет выводить только результаты журнала, значение уровня которых больше или равно уровню. Например, если мы установим уровень вывода журнала в INFO, то выходной уровень больше или равен INFO журналы, такие как ПРЕДУПРЕЖДЕНИЕ, ОШИБКА и т. д., уровни DEBUG и NOSET не выводятся.
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.WARN)
# Log
logger.debug('Debugging')
logger.critical('Critical Something')
logger.error('Error Occurred')
logger.warning('Warning exists')
logger.info('Finished')
Здесь мы устанавливаем уровень вывода на WARN, а затем выводим информацию журнала пяти различных уровней.Результаты следующие:
Critical Something
Error Occurred
Warning exists
Видно, что выводится только информация CRITICAL, ERROR, WARNING, а информация DEBUG и INFO не выводится.
Handler
Давайте сначала посмотрим на использование Handler, см. следующий пример:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler('output.log')
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')
Здесь вместо использования глобальной конфигурации basicConfig мы сначала объявляем объект Logger, затем указываем соответствующий ему Handler в качестве объекта FileHandler, а затем отдельно указываем объект Formatter для объекта Handler, чтобы отдельно настроить выходной формат, и, наконец, добавляем соответствующий Logger object.Handler достаточно, и, наконец, вы можете обнаружить, что журнал будет выводиться в output.log, содержание которого выглядит следующим образом:
2018-06-03 14:53:36,467 - __main__ - INFO - This is a log info
2018-06-03 14:53:36,468 - __main__ - WARNING - Warning exists
2018-06-03 14:53:36,468 - __main__ - INFO - Finish
Кроме того, мы также можем использовать другие обработчики для вывода журнала.Обработчики, предоставляемые модулем ведения журнала, включают в себя:
- StreamHandler: logging.StreamHandler, вывод журнала в поток, который может быть sys.stderr, sys.stdout или файлом.
- FileHandler: logging.FileHandler, вывод журнала в файл.
- BaseRotatingHandler: logging.handlers.BaseRotatingHandler — основной метод отката журнала.
- RotatingHandler: logging.handlers.RotatingHandler; метод отката журнала, поддерживает максимальное количество файлов журнала и откат файла журнала.
- TimeRotatingHandler: logging.handlers.TimeRotatingHandler, режим отката журнала, файлы журнала отката в пределах определенного часового пояса.
- SocketHandler: logging.handlers.SocketHandler; журналы удаленного вывода в сокеты TCP/IP.
- DatagramHandler: logging.handlers.DatagramHandler, удаленный вывод журнала в сокеты UDP.
- SMTPHandler: logging.handlers.SMTPHandler; удаленный вывод журнала на адрес электронной почты.
- SysLogHandler: logging.handlers.SysLogHandler, вывод журнала в системный журнал.
- NTEventLogHandler: logging.handlers.NTEventLogHandler, удаленный вывод журнала в журнал событий Windows NT/2000/XP.
- MemoryHandler: logging.handlers.MemoryHandler, логи выводятся в указанный буфер в памяти.
- HTTPHandler: logging.handlers.HTTPHandler, удаленный вывод на HTTP-сервер через «GET» или «POST».
Ниже мы используем три обработчика для одновременного вывода логов на консоль, в файл и на HTTP-сервер:
import logging
from logging.handlers import HTTPHandler
import sys
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.DEBUG)
# StreamHandler
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(level=logging.DEBUG)
logger.addHandler(stream_handler)
# FileHandler
file_handler = logging.FileHandler('output.log')
file_handler.setLevel(level=logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# HTTPHandler
http_handler = HTTPHandler(host='localhost:8001', url='log', method='POST')
logger.addHandler(http_handler)
# Log
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')
Перед запуском нам нужно запустить HTTP-сервер и запустить его на порту 8001, где интерфейс журнала — это интерфейс, используемый для получения журналов.
После запуска вывод консоли будет содержать следующее:
This is a log info
Debugging
Warning exists
Finish
В файле output.log будет написано следующее:
2018-06-03 15:13:44,895 - __main__ - INFO - This is a log info
2018-06-03 15:13:44,947 - __main__ - WARNING - Warning exists
2018-06-03 15:13:44,949 - __main__ - INFO - Finish
HTTP-сервер получит вывод консоли.
Таким образом, мы контролируем многоцелевой вывод журнала, устанавливая несколько обработчиков.
Также стоит отметить, что здесь мы не устанавливали Formatter для объекта StreamHandler, поэтому консоль выводит только содержимое лога без включения времени, модуля и другой информации, тогда как для FileHandler мы устанавливаем объект Formatter через setFormatter() метод, поэтому выходные данные представляют собой отформатированную информацию журнала.
Кроме того, каждый обработчик также может установить информацию об уровне, и информация об уровне конечного выходного результата будет представлять собой пересечение уровня объекта Logger и уровня объекта Handler.
Formatter
При выполнении форматированного вывода журнала мы можем настроить содержимое форматированного вывода глобально без помощи basicConfig, и мы можем завершить его с помощью Formatter.Давайте рассмотрим использование Formatter отдельно:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.WARN)
formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
# Log
logger.debug('Debugging')
logger.critical('Critical Something')
logger.error('Error Occurred')
logger.warning('Warning exists')
logger.info('Finished')
Здесь мы указываем Formatter и передаем параметры fmt и datefmt, чтобы указать формат вывода и формат времени результата журнала, а затем обработчик может установить объект Formatter с помощью метода setFormatter().Вывод выглядит следующим образом :
2018/06/03 15:47:15 - __main__ - CRITICAL - Critical Something
2018/06/03 15:47:15 - __main__ - ERROR - Error Occurred
2018/06/03 15:47:15 - __main__ - WARNING - Warning exists
Таким образом, мы можем настроить формат вывода отдельно для каждого обработчика, что очень гибко.
Захват трассировки
Если мы сталкиваемся с ошибкой, мы предпочитаем подробную информацию о трассировке, которая появляется при сообщении об ошибке, что удобно для отладки. Используя модуль ведения журнала, мы можем легко реализовать эту запись. Давайте на примере, чтобы почувствовать это:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.DEBUG)
# Formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# FileHandler
file_handler = logging.FileHandler('result.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# StreamHandler
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
# Log
logger.info('Start')
logger.warning('Something maybe fail.')
try:
result = 10 / 0
except Exception:
logger.error('Faild to get result', exc_info=True)
logger.info('Finished')
Здесь мы добавляем параметр в метод error() и устанавливаем для exc_info значение True, чтобы мы могли выводить информацию в процессе выполнения, то есть полную информацию о трассировке.
Результаты приведены ниже:
2018-06-03 16:00:15,382 - __main__ - INFO - Start print log
2018-06-03 16:00:15,382 - __main__ - DEBUG - Do something
2018-06-03 16:00:15,382 - __main__ - WARNING - Something maybe fail.
2018-06-03 16:00:15,382 - __main__ - ERROR - Faild to get result
Traceback (most recent call last):
File "/private/var/books/aicodes/loggingtest/demo8.py", line 23, in <module>
result = 10 / 0
ZeroDivisionError: division by zero
2018-06-03 16:00:15,383 - __main__ - INFO - Finished
Видно, что таким образом мы можем очень удобно записывать информацию об ошибке.Если возникает ошибка, мы также можем очень легко ее устранить.
настроить общий доступ
При написании проекта мы обязательно будем размещать множество конфигураций во многих модулях. В настоящее время было бы слишком громоздко, если мы настроим конфигурацию ведения журнала для каждого файла. Модуль ведения журнала предоставляет механизм для совместного использования конфигурации между родительским и дочерним модулями. для автоматической загрузки конфигурации родительского модуля.
Например, мы сначала определяем файл main.py здесь:
import logging
import core
logger = logging.getLogger('main')
logger.setLevel(level=logging.DEBUG)
# Handler
handler = logging.FileHandler('result.log')
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info('Main Info')
logger.debug('Main Debug')
logger.error('Main Error')
core.run()
Здесь мы настраиваем формат вывода и путь к файлу журнала, определяем имя Logger как main, затем вводим еще одно ядро модуля и, наконец, вызываем метод run() ядра.
Затем мы определяем core.py со следующим содержимым:
import logging
logger = logging.getLogger('main.core')
def run():
logger.info('Core Info')
logger.debug('Core Debug')
logger.error('Core Error')
Здесь мы определяем имя Logger как main.core.Обратите внимание, что начало здесь — main, которое сейчас является именем Logger в main.py, так что Logger в core.py будет повторно использовать конфигурацию Logger в main. .py Вместо того, чтобы настраивать его снова.
После запуска будет создан файл result.log со следующим содержимым:
2018-06-03 16:55:56,259 - main - INFO - Main Info
2018-06-03 16:55:56,259 - main - ERROR - Main Error
2018-06-03 16:55:56,259 - main.core - INFO - Core Info
2018-06-03 16:55:56,259 - main.core - ERROR - Core Error
Вы можете видеть, что и родительский, и дочерний модули используют одну и ту же конфигурацию вывода.
Таким образом, нам нужно только определить выходную конфигурацию модуля ведения журнала в файле записи, а подмодули должны начинаться только с имени родительского модуля при определении объекта Logger для совместного использования конфигурации, что очень удобно.
конфигурация файла
В процессе разработки не рекомендуется прописывать конфигурацию в коде, лучше прописать конфигурацию в конфигурационном файле, мы можем записать конфигурацию в конфигурационный файл, а затем прочитать конфигурационный файл во время выполнения .Таким удобнее управлять и обслуживать.Давайте проиллюстрируем на примере.Сначала определим конфигурационный файл yaml:
version: 1
formatters:
brief:
format: "%(asctime)s - %(message)s"
simple:
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
handlers:
console:
class : logging.StreamHandler
formatter: brief
level : INFO
stream : ext://sys.stdout
file:
class : logging.FileHandler
formatter: simple
level: DEBUG
filename: debug.log
error:
class: logging.handlers.RotatingFileHandler
level: ERROR
formatter: simple
filename: error.log
maxBytes: 10485760
backupCount: 20
encoding: utf8
loggers:
main.core:
level: DEBUG
handlers: [console, file, error]
root:
level: DEBUG
handlers: [console]
Здесь мы определяем такие модули, как форматтеры, обработчики, регистраторы и корень, которые фактически соответствуют конфигурации каждого форматтера, обработчика и регистратора.Параметры и методы их построения одинаковы.
Затем мы определяем основной файл ввода, main.py, со следующим содержимым:
import logging
import core
import yaml
import logging.config
import os
def setup_logging(default_path='config.yaml', default_level=logging.INFO):
path = default_path
if os.path.exists(path):
with open(path, 'r', encoding='utf-8') as f:
config = yaml.load(f)
logging.config.dictConfig(config)
else:
logging.basicConfig(level=default_level)
def log():
logging.debug('Start')
logging.info('Exec')
logging.info('Finished')
if __name__ == '__main__':
yaml_path = 'config.yaml'
setup_logging(yaml_path)
log()
core.run()
Здесь мы определяем метод setup_logging(), который считывает конфигурацию файла yaml, а затем передает элементы конфигурации в модуль ведения журнала через метод dictConfig() для глобальной инициализации.
Кроме того, этот модуль также вводит еще одно ядро модуля, поэтому мы определяем core.py следующим образом:
import logging
logger = logging.getLogger('main.core')
def run():
logger.info('Core Info')
logger.debug('Core Debug')
logger.error('Core Error')
Содержимое этого файла не отличается от приведенного выше.
Глядя на файл конфигурации, основной файл записи main.py на самом деле соответствует корневой конфигурации, в которой указано, что обработчики являются консольными, то есть только с выводом на консоль. Кроме того, в конфигурации логгеров определяем модуль main.core Обработчики консольные, файловые и ошибочные, то есть вывод в консоль, вывод в обычные файлы и файлы отката.
После такого запуска мы видим, что все результаты выполнения выводятся на консоль:
2018-06-03 17:07:12,727 - Exec
2018-06-03 17:07:12,727 - Finished
2018-06-03 17:07:12,727 - Core Info
2018-06-03 17:07:12,727 - Core Info
2018-06-03 17:07:12,728 - Core Error
2018-06-03 17:07:12,728 - Core Error
Файл debug.log содержит результат выполнения core.py:
2018-06-03 17:07:12,727 - main.core - INFO - Core Info
2018-06-03 17:07:12,727 - main.core - DEBUG - Core Debug
2018-06-03 17:07:12,728 - main.core - ERROR - Core Error
Видно, что через конфигурационный файл мы можем очень гибко определять Handler, Formatter, Logger и другие конфигурации, а также он очень интуитивно понятен и прост в обслуживании.В реальных проектах этот метод рекомендуется для настройки.
Вышеизложенное является основным использованием модуля ведения журнала, с его помощью мы можем легко управлять журналами и вести их, что значительно упростит нашу работу.
Распространенные ошибки при использовании ведения журнала
В выводе журнала мы часто используем форму конкатенации строк.Во многих случаях мы можем использовать формат() строки для построения строки, но на самом деле это не очень хороший метод, потому что есть лучший метод, давайте сравним два Примеры:
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# bad
logging.debug('Hello {0}, {1}!'.format('World', 'Congratulations'))
# good
logging.debug('Hello %s, %s!', 'World', 'Congratulations')
Существует два способа вывода журнала. Первый использует для построения метод string format(), а для логирования используется только первый параметр. На самом деле модуль logging предоставляет метод форматирования строки. Нам нужно только написать шаблон для вывода в первый параметр. Заполнитель представлен %s, %d и т. д., а затем добавить соответствующее значение к последующим параметрам. Этот метод рекомендуется.
Результаты приведены ниже:
2018-06-03 22:27:51,220 - root - DEBUG - Hello World, Congratulations!
2018-06-03 22:27:51,220 - root - DEBUG - Hello World, Congratulations!
Кроме того, при обработке исключений мы обычно форматируем исключение напрямую как строку, но на самом деле мы можем напрямую указать параметр для вывода трассировки, например:
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
try:
result = 5 / 0
except Exception as e:
# bad
logging.error('Error: %s', e)
# good
logging.error('Error', exc_info=True)
# good
logging.exception('Error')
Если мы напрямую используем метод форматирования строки для вывода ошибки, информация трассировки не будет включена, но если мы добавим параметр exc_info или воспользуемся методом exception() для прямой печати, информация трассировки будет выведена.
Результаты приведены ниже:
2018-06-03 22:24:31,927 - root - ERROR - Error: division by zero
2018-06-03 22:24:31,927 - root - ERROR - Error
Traceback (most recent call last):
File "/private/var/books/aicodes/loggingtest/demo9.py", line 6, in <module>
result = 5 / 0
ZeroDivisionError: division by zero
2018-06-03 22:24:31,928 - root - ERROR - Error
Traceback (most recent call last):
File "/private/var/books/aicodes/loggingtest/demo9.py", line 6, in <module>
result = 5 / 0
ZeroDivisionError: division by zero
Выше приведено полное введение в модуль ведения журнала. Что ж, пришло время отказаться от печати и начать пользоваться удобством ведения журнала!