向Flask的app.logger提供额外信息

时间:2015-01-05 07:21:56

标签: python flask

Flask 0.10的default debug log format

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() in Flask'slogging.py创建的,我是否必须修改Flask源代码以实现我想要的目标?

我还想过用我自己的记录器覆盖app.logger,例如app.logger = my_logger,它看起来不对。

2 个答案:

答案 0 :(得分:11)

通过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.Handlerlogging.Formatter来实现Flask外部。可以找到一个通用示例here。可以在the doccookbook

中找到日志记录配置的高级主题

关于您的问题的定制示例是:

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()

通过覆盖logging.Logger类

通过覆盖默认的记录器类可以实现相同的目标。结合flask request context stack,您就可以在日志中获得自己的字段:

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()

答案 1 :(得分: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')