首页 文章

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

提问于
浏览
13

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 .

如果 Logger 是我自己创建的,我可以使用 logging.LoggerAdapter 并提供额外的信息作为dict {'work_id': some_uuid} ,然后我可以使用 record.work_id 在日志记录中访问它 .

但是 app.logger 是由create_logger() in Flask'slogging.py创建的,我是否必须修改Flask源来实现我想要的?

我还想过用我自己的 Logger 覆盖 app.logger ,比如 app.logger = my_logger ,它似乎不对 .

2 回答

  • 2

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

    Via Handler and Formatter of standard logging module

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

    Via overriding logging.Logger class

    通过覆盖默认 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()
    
  • 10

    这是使用自定义 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')
    

相关问题