Предоставьте дополнительную информацию Flask app.logger

формат журнала отладки по умолчанию для Flask 0.10

debug_log_format =
'-------------------------------------------------------------------------\n%
%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s
\n-------------------------------------------------------------------------'

Как мне изменить его на это:

'-------------------------------------------------------------------------\n%
work_id %(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s
\n-------------------------------------------------------------------------'

где work_id - произвольно сгенерированный UUID для каждого запроса.

Если регистратор создан сам, я могу просто использовать logging.LoggerAdapter и предоставить дополнительную информацию как dict {'work_id': some_uuid}, затем я могу получить к нему доступ в записи журнала, используя record.work_id.

Но app.logger создается create_logger() в logging.py в Flask, мне нужно изменить источник Flask для достижения того, что Я хочу?

Я также подумал, что просто переопределить app.logger своим собственным регистратором, например app.logger = my_logger, это не кажется правильным.

Ответ 1

Через Flask.debug_log_format

Просто сделайте следующее:

app.debug = True
app.debug_log_format = """-------------------------------------------------------------------------
%(worker_id)s (levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s
-------------------------------------------------------------------------"""
app.logger.log("test", extra={"worker_id": request.your_uuid_property)

Пример:

import logging
from flask import Flask, request
app = Flask(__name__)

# please replace "request.uuid" with your actual property
log = lambda msg: app.logger.info(msg, extra={'worker_id': "request.uuid" })

@app.route("/")
def hello():
    log("hello world")
    return "Hello World!"

if __name__ == "__main__":
    app.debug_log_format = """-------------------------------------------------------------------------
    %(worker_id)s in %(module)s [%(pathname)s:%(lineno)d]:
    %(message)s
    -------------------------------------------------------------------------"""
    app.debug = True
    log("hello world")
    app.run()

Через обработчик и форматирование стандартного модуля ведения журнала

Flask использует запись в любом случае, поэтому вы можете использовать logging.Handler и logging.Formatter для достижения внешнего фляги. Общий пример можно найти здесь. Дополнительную тему конфигурации регистрации можно найти в документе и в cookbook

Приведенный в качестве примера пример вашего вопроса:

import logging
from flask import Flask
app = Flask(__name__)

class CustomFormatter(logging.Formatter):
    def format(self, record):
        record.worker_id = "request.uuid" # replace this with your variable 
        return super(CustomFormatter,self).format(record)

@app.route("/")
def hello():
    app.logger.info("hello world")
    return "Hello World!"

if __name__ == "__main__":
    custom_format = """-------------------------------------------------------------------------
    %(worker_id)s in %(module)s [%(pathname)s:%(lineno)d]:
    %(message)s
    -------------------------------------------------------------------------"""
    app.debug = True
    ch = logging.StreamHandler()
    ch.setFormatter(CustomFormatter(fmt=custom_format))
    app.logger.addHandler(ch)
    app.logger.debug("hello world")
    app.run()

Через переопределение журнала. Класс logger

Та же самая цель может быть достигнута путем переопределения класса регистратора по умолчанию. Объединив стек контекста запроса флагов, вы сможете получить свое собственное поле в журнале:

import logging
from flask import Flask
app = Flask(__name__)
from flask import _request_ctx_stack

CUSTOM_FORMAT = """-------------------------------------------------------------------------
%(worker_id)s in %(module)s [%(pathname)s:%(lineno)d]:
%(message)s
-------------------------------------------------------------------------"""

class MyLogger(logging.Logger):
    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        ctx = _request_ctx_stack.top
        custom_extra = dict(
            worker_id="request.uuid"
        )
        if ctx is not None:
            url = ctx.request.url # please replace this with your own field
            custom_extra["worker_id"] = url

        if extra is not None:
            extra.update(custom_extra)
        else:
            extra = custom_extra
        return super(MyLogger,self).makeRecord(name, level, fn, lno, msg, args, exc_info, func=func, extra=extra)

logging.setLoggerClass(MyLogger)

@app.route("/")
def hello():
    app.logger.info("hello world")
    return "Hello World!"

if __name__ == "__main__":
    app.debug_log_format = CUSTOM_FORMAT
    app.debug = True
    app.logger.debug("hello world")
    app.run()

Ответ 2

Вот еще один пример использования пользовательского Formatter. Благодаря @chfw и this

Мне нравится использование flask.has_request_context() здесь, так что регистрация не мешает модульным тестам

import logging
from logging import StreamHandler
import flask
from flask import Flask, g, request

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
app = flask.Flask(__name__)


class CustomFormatter(logging.Formatter):
    def format(self, record):
        record.uuid = None
        if flask.has_request_context():
            record.uuid = g.uuid if hasattr(g, 'uuid') else None
            record.path = request.path
            record.endpoint = request.endpoint
            record.remote_addr = request.remote_addr
        return super(CustomFormatter, self).format(record)

custom_format = '''%(levelname)s %(name)s %(uuid)s %(path)s %(endpoint)s %(remote_addr)s  %(message)s'''
handler = StreamHandler()
handler.setFormatter(CustomFormatter(fmt=custom_format))
logger.addHandler(handler)


with app.test_request_context():
    g.uuid = 'foo'
    logger.fatal('help')