Сообщить об ошибке.

Ведение журнала логов в приложении Flask

Фреймворк Flask для ведения журнала логов использует стандартный модуль logging Python. Сообщения приложения Flask регистрируются с помощью app.logger, имя которого совпадает с именем app.name. Этот регистратор также может использоваться для регистрации собственных сообщений.

@app.route('/login', methods=['POST'])
def login():
    user = get_user(request.form['username'])

    if user.check_password(request.form['password']):
        login_user(user)
        # логирование успешного входа пользователя
        app.logger.info('%s успешно вошел в систему', user.username)
        return redirect(url_for('index'))
    else:
        # логирование неудачной авторизации
        app.logger.info('%s не удалось войти в систему', user.username)
        abort(401)

Если ведение журнала логов в приложении на Flask не настраивалось, то по умолчанию уровень журнала настроен как предупреждение 'warning'. Следовательно, ниже уровня 'warning' ничего не будет видно.

Содержание:

Базовая конфигурация ведения журнала логов.

Если нужно настроить ведение журнала для своего проекта, то это необходимо делать перед созданием объекта приложения или сразу после его создания. Если доступ к app.logger осуществляется до настройки ведения журнала, то обработчик по умолчанию добавляется автоматически.

В примере ниже, используется dictConfig() для создания конфигурации ведения журнала, аналогичной настройке Flask по умолчанию, за исключением всех журналов:

from logging.config import dictConfig

dictConfig({
    'version': 1,
    'formatters': {'default': {
        'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
    }},
    'handlers': {'wsgi': {
        'class': 'logging.StreamHandler',
        'stream': 'ext://flask.logging.wsgi_errors_stream',
        'formatter': 'default'
    }},
    'root': {
        'level': 'INFO',
        'handlers': ['wsgi']
    }
})

app = Flask(__name__)

Конфигурация по умолчанию.

Если запустить приложение Flask не настраивая ведение журнала, то Flask автоматически добавит в app.logger обработчик StreamHandler. Во время запросов приложение будет записывать логи в поток, указанный сервером WSGI в environment['wsgi.errors'] (обычно это sys.stderr). Вне запроса, сообщения от app.logger будут регистрироваться в sys.stderr.

Удаление обработчика по умолчанию

Если ведение журнала логов приложения настраивается после доступа к app.logger и нужно удалить обработчик по умолчанию (который добавился автоматически), то перед удалением, его необходимо импортировать, а затем удалить:

from flask.logging import default_handler

app.logger.removeHandler(default_handler)

Отправка сообщений об ошибках на Email.

При работе приложения Flask на удаленном сервере в "боевом" режиме, все сообщения журнала будут записываться в файл и скорее всего не будет возможности постоянного мониторинга этого файла на ошибки.

Чтобы своевременно получать сообщения об ошибках в приложении, можно настроить logging.handlers.SMTPHandler для отправки электронного письма при регистрации ошибок.

import logging
from logging.handlers import SMTPHandler

mail_handler = SMTPHandler(
    mailhost='127.0.0.1',
    fromaddr='server-error@example.com',
    toaddrs=['admin@example.com'],
    subject='Application Error'
)
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(logging.Formatter(
    '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
))

if not app.debug:
    app.logger.addHandler(mail_handler)

Информация о запросе в сообщении об ошибке.

Просмотр дополнительной информации о запросе, такой как IP-адрес, может помочь отладить некоторые ошибки. Для добавления собственных полей в сообщения регистратора, можно создать подкласс logging.Formatter. Таким же способом можно изменить форматирование для обработчика Flask по умолчанию, обработчика сообщений отправляемых на почту, или любого другого обработчика.

from flask import has_request_context, request
from flask.logging import default_handler

class RequestFormatter(logging.Formatter):
    def format(self, record):
        if has_request_context():
            record.url = request.url
            record.remote_addr = request.remote_addr
        else:
            record.url = None
            record.remote_addr = None

        return super().format(record)

formatter = RequestFormatter(
    '[%(asctime)s] %(remote_addr)s requested %(url)s\n'
    '%(levelname)s in %(module)s: %(message)s'
)
default_handler.setFormatter(formatter)
mail_handler.setFormatter(formatter)

Сообщения об ошибках из расширений Flask.

Расширения Flask так же могут широко использовать логирование, и что бы увидеть эти сообщения, необходимо добавить соответствующие обработчики к корневому регистратору, а не только к регистратору приложения app.logger.

from flask.logging import default_handler

root = logging.getLogger()
root.addHandler(default_handler)
root.addHandler(mail_handler)

В зависимости от проекта, бывает полезно настраивать каждый регистратор отдельно, а не только корневой регистратор.

for logger in (
    app.logger,
    logging.getLogger('sqlalchemy'),
    logging.getLogger('other_package'),
):
    logger.addHandler(default_handler)
    logger.addHandler(mail_handler)