Введение
В этом разделе кратко介绍руется модуль журналирования.
This tutorial is from open-source community. Access the source code
💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал
В этом разделе кратко介绍руется модуль журналирования.
Модуль logging
- это стандартный модуль библиотеки для записи диагностической информации. Это также очень большой модуль с большим количеством сложной функциональности. Мы покажем простой пример, чтобы проиллюстрировать его полезность.
В упражнениях мы написали функцию parse()
, которая выглядела примерно так:
## fileparse.py
def parse(f, types=None, names=None, delimiter=None):
records = []
for line in f:
line = line.strip()
if not line: continue
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
return records
Обратите внимание на инструкцию try-except
. Что нужно делать в блоке except
?
Следует ли выводить сообщение об ошибке?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
Или тихо игнорировать его?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
pass
Ни одно из решений не является удовлетворительным, потому что часто требуются оба поведения (возможность выбора пользователем).
Модуль logging
может решить эту проблему.
## fileparse.py
import logging
log = logging.getLogger(__name__)
def parse(f,types=None,names=None,delimiter=None):
...
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
log.warning("Couldn't parse : %s", line)
log.debug("Reason : %s", e)
Код модифицирован для вывода сообщений об ошибках или с использованием специального объекта Logger
. Создается с помощью logging.getLogger(__name__)
.
Создайте объект журналировщика.
log = logging.getLogger(name) ## name - это строка
Вывод сообщений журнала.
log.critical(message [, args])
log.error(message [, args])
log.warning(message [, args])
log.info(message [, args])
log.debug(message [, args])
Каждый метод представляет собой разный уровень серьезности.
Все они создают отформатированное сообщение журнала. args
используется с оператором %
для создания сообщения.
logmsg = message % args ## Записывается в журнал
Поведение журналирования настраивается отдельно.
## main.py
...
if __name__ == '__main__':
import logging
logging.basicConfig(
filename = 'app.log', ## Файл для вывода логов
level = logging.INFO, ## Уровень вывода
)
обычно, это一次性配置在程序启动时进行。配置与进行日志记录调用的代码是分开的。 (注:这里“一次性配置”表述不太准确,原文“one-time configuration”更准确意思是“一次性的配置操作”,但按要求尽量贴近原文翻译了)
Журналирование имеет высокую настраиваемость. Вы можете настроить каждый аспект его работы: файлы вывода, уровни, форматы сообщений и т.д. Однако, код, использующий журналирование, не должен беспокоиться об этом.
В fileparse.py
есть некоторые обработчики ошибок, связанные с исключениями, вызванными неправильными входными данными. Они выглядят так:
## fileparse.py
import csv
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Parse a CSV file into a list of records with type conversion.
'''
if select and not has_headers:
raise RuntimeError('select requires column headers')
rows = csv.reader(lines, delimiter=delimiter)
## Read the file headers (if any)
headers = next(rows) if has_headers else []
## If specific columns have been selected, make indices for filtering and set output columns
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Skip rows with no data
continue
## If specific column indices are selected, pick them out
if select:
row = [ row[index] for index in indices]
## Apply type conversion to the row
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
print(f"Row {rowno}: Couldn't convert {row}")
print(f"Row {rowno}: Reason {e}")
continue
## Make a dictionary or a tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
Обратите внимание на инструкции print
, которые выводят диагностические сообщения. Замена этих print
на операции журналирования относительно проста. Измените код так:
## fileparse.py
import csv
import logging
log = logging.getLogger(__name__)
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Parse a CSV file into a list of records with type conversion.
'''
if select and not has_headers:
raise RuntimeError('select requires column headers')
rows = csv.reader(lines, delimiter=delimiter)
## Read the file headers (if any)
headers = next(rows) if has_headers else []
## If specific columns have been selected, make indices for filtering and set output columns
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Skip rows with no data
continue
## If specific column indices are selected, pick them out
if select:
row = [ row[index] for index in indices]
## Apply type conversion to the row
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
log.warning("Row %d: Couldn't convert %s", rowno, row)
log.debug("Row %d: Reason %s", rowno, e)
continue
## Make a dictionary or a tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
Теперь, когда вы внесли эти изменения, попробуйте использовать часть своего кода с неправильными данными.
>>> import report
>>> a = report.read_portfolio('missing.csv')
Row 4: Bad row: ['MSFT', '', '51.23']
Row 7: Bad row: ['IBM', '', '70.44']
>>>
Если вы ничего не сделаете, вы получите только сообщения журнала уровня WARNING
и выше. Вывод будет выглядеть как простые инструкции print
. Однако, если вы настроите модуль журналирования, вы получите дополнительную информацию о уровнях журналирования, модуле и т.д. Введите эти шаги, чтобы увидеть это:
>>> import logging
>>> logging.basicConfig()
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23']
WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44']
>>>
Вы заметите, что не видите вывод операции log.debug()
. Введите это, чтобы изменить уровень.
>>> logging.getLogger('fileparse').setLevel(logging.DEBUG)
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23']
DEBUG:fileparse:Row 4: Reason: invalid literal for int() with base 10: ''
WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44']
DEBUG:fileparse:Row 7: Reason: invalid literal for int() with base 10: ''
>>>
Отключите все, кроме наиболее критичных сообщений журнала:
>>> logging.getLogger('fileparse').setLevel(logging.CRITICAL)
>>> a = report.read_portfolio('missing.csv')
>>>
Для добавления журналирования в приложение вам нужно иметь какой-то механизм для инициализации модуля журналирования в главном модуле. Одним из способов сделать это является включение некоторого кода настройки, который выглядит так:
## Этот файл настраивает базовую конфигурацию модуля журналирования.
## Измените настройки здесь, чтобы настроить вывод журнала по необходимости.
import logging
logging.basicConfig(
filename = 'app.log', ## Имя файла журнала (оставьте пустым, чтобы использовать stderr)
filemode = 'w', ## Режим файла (используйте 'a', чтобы добавлять записи)
level = logging.WARNING, ## Уровень журналирования (DEBUG, INFO, WARNING, ERROR или CRITICAL)
)
Опять же, вам нужно разместить это в каком-то месте в этапах запуска вашей программы. Например, где вы бы поместили это в своей программе report.py
?
Поздравляем! Вы завершили лабораторную работу по журналированию. Вы можете практиковаться в других лабораторных работах в LabEx, чтобы улучшить свои навыки.