Перевод: "Практическое программирование на Python" 08_02_Logging

Python
Перевод: "Практическое программирование на Python" 08_02_Logging

содержание | Предыдущий раздел (тест 8.1) | Следующий раздел (8.3 Отладка)

8.2 Журналы

В этом разделе кратко представлен модуль ведения журнала.

модуль регистрации

loggingМодули — это модули стандартной библиотеки Python для регистрации диагностической информации. Модуль ведения журнала огромен и имеет множество сложных функций. Мы покажем простой пример, чтобы проиллюстрировать его полезность.

Откройте для себя заново исключение

В этом упражнении мы создадим такой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объект.Loggerиспользование объектаlogging.getLogger(__name__)Создайте.

Основы журнала

Создайте объект регистратора.

log = logging.getLogger(name)   # name is a string

Сообщение журнала проблем:

log.critical(message [, args])
log.error(message [, args])
log.warning(message [, args])
log.info(message [, args])
log.debug(message [, args])

Различные методы представляют различные уровни серьезности.

Все методы создают форматированные сообщения журнала.argsи%операторы используются вместе для создания сообщения.

logmsg = message % args # Written to the log

конфигурация журнала

Конфигурация:

# main.py

...

if __name__ == '__main__':
    import logging
    logging.basicConfig(
        filename  = 'app.log',      # Log output file
        level     = logging.INFO,   # Output level
    )

Обычно конфигурация лога одноразовая при запуске программы. Эта конфигурация отделена от регистрации вызовов.

инструкция

Журналы могут быть настроены произвольно. Вы можете настроить любой аспект конфигурации ведения журнала: выходные файлы, уровни, форматы сообщений и т. д., не беспокоясь о том, что это повлияет на код, который использует модуль ведения журнала.

Упражнение

Упражнение 8.2: Добавление журналов в модуль

существует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('Data/missing.csv')
Row 4: Bad row: ['MSFT', '', '51.23']
Row 7: Bad row: ['IBM', '', '70.44']
>>>

Если вы ничего не делаете, вы просто получаетеWARNINGСообщения журнала выше уровня. Вывод выглядит как простые операторы печати. Однако, если вы настроите модуль ведения журнала, вы получите другую информацию об уровнях ведения журнала, модулях и т. д. Для просмотра выполните следующие действия:

>>> import logging
>>> logging.basicConfig()
>>> a = report.read_portfolio('Data/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').level = logging.DEBUG
>>> a = report.read_portfolio('Data/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').level=logging.CRITICAL
>>> a = report.read_portfolio('Data/missing.csv')
>>>

Упражнение 8.3: Добавление журналов в программу

Чтобы добавить ведение журнала в ваше приложение, вам нужен какой-то механизм для инициализации ведения журнала в основном модуле. Один из способов использует код, который выглядит так:

# This file sets up basic configuration of the logging module.
# Change settings here to adjust logging output as needed.
import logging
logging.basicConfig(
    filename = 'app.log',            # Name of the log file (omit to use stderr)
    filemode = 'w',                  # File mode (use 'a' to append)
    level    = logging.WARNING,      # Logging level (DEBUG, INFO, WARNING, ERROR, or CRITICAL)
)

Опять же, вам нужно поместить код конфигурации ведения журнала на этапе запуска программы. Например, положить егоreport.pyГде в программе?

содержание | Предыдущий раздел (тест 8.1) | Следующий раздел (8.3 Отладка)

Примечание. Полный перевод см.GitHub.com/co Статья 3 — /PRA…