Flask + sqlalchemy 高级日志记录

Flask + sqlalchemy advanced logging

我发现了一些关于此的其他帖子,但 none 对我有用,所以我想联系一下,看看是否有人可以解释如何在某些记录器上正确获取/重定向/设置处理程序存在于 Flask / Werkzeurg / sqlalchemy 中。

之前的研究无法回答我的问题:

https://github.com/pallets/flask/issues/1359

http://flask.pocoo.org/docs/dev/logging/

https://gist.github.com/ibeex/3257877

我的配置:

main.py

    ...

    def init_app():
    """ Runs prior to app launching, contains initialization code """

    # set logging level
    if not os.path.exists(settings.LOG_DIR):
        os.makedirs(settings.LOG_DIR)

    # default level
    log_level = logging.CRITICAL

    if settings.ENV == 'DEV':
        log_level = logging.DEBUG
    elif settings.ENV == 'TEST':
        log_level = logging.WARNING
    elif settings.ENV == 'PROD':
        log_level = logging.ERROR

    log_formatter = logging.Formatter("[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s")
    api_logger = logging.getLogger()
    api_handler = TimedRotatingFileHandler(
            settings.API_LOG_FILE,
            when='midnight',
            backupCount=10
        )
    api_handler.setLevel(log_level)
    api_handler.setFormatter(log_formatter)
    api_logger.addHandler(api_handler)
    logging.getLogger('werkzeug').addHandler(api_handler)


    db_logger = logging.getLogger('sqlalchemy')
    db_handler = TimedRotatingFileHandler(
            settings.DB_LOG_FILE,
            when='midnight',
            backupCount=10
        )
    db_handler.setLevel(log_level)
    db_handler.setFormatter(log_formatter)
    db_logger.addHandler(db_handler)
    logging.getLogger('sqlalchemy.engine').addHandler(db_handler)
    logging.getLogger('sqlalchemy.dialects').addHandler(db_handler)
    logging.getLogger('sqlalchemy.pool').addHandler(db_handler)
    logging.getLogger('sqlalchemy.orm').addHandler(db_handler)



# add endpoints

...


if __name__ == '__main__':
    init_app()
    app.run(host='0.0.0.0', port=7777)

我尝试通过几种不同的方式获取和更改记录器上的设置,但我最终还是将 werkzeug 调试输出到控制台而不是我的日志,我可以看到正在创建日志,但它看起来不像记录器实际上正在向他们输出:

api.log(格式化程序写入)

2018-02-15 12:03:03,944] {/usr/local/lib/python3.5/dist-packages/werkzeug/_internal.py:88} WARNING -  * Debugger is active!

db.log(空)

任何对此的见解将不胜感激!

更新

我能够使用长手版让 werkzeug 记录器工作,似乎显示的 shorthand 函数调用正在返回空对象。 sqlalchemy 记录器仍在输出到控制台。引擎配置是否可以覆盖我的文件处理程序?

main.py

...

# close current file handlers
for handler in copy(logging.getLogger().handlers):
    logging.getLogger().removeHandler(handler)
    handler.close()
for handler in copy(logging.getLogger('werkzeug').handlers):
    logging.getLogger('werkzeug').removeHandler(handler)
    handler.close()
for handler in copy(logging.getLogger('sqlalchemy.engine').handlers):
    logging.getLogger('sqlalchemy.engine').removeHandler(handler)
    handler.close()
for handler in copy(logging.getLogger('sqlalchemy.dialects').handlers):
    logging.getLogger('sqlalchemy.dialects').removeHandler(handler)
    handler.close()
for handler in copy(logging.getLogger('sqlalchemy.pool').handlers):
    logging.getLogger('sqlalchemy.pool').removeHandler(handler)
    handler.close()
for handler in copy(logging.getLogger('sqlalchemy.orm').handlers):
    logging.getLogger('sqlalchemy.orm').removeHandler(handler)
    handler.close()


# create our own custom handlers
log_formatter = logging.Formatter("[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s")
api_handler = TimedRotatingFileHandler(
        settings.API_LOG_FILE,
        when='midnight',
        backupCount=10
    )
api_handler.setLevel(log_level)
api_handler.setFormatter(log_formatter)
logging.getLogger().setLevel(log_level)
logging.getLogger().addHandler(api_handler)
logging.getLogger('werkzeug').setLevel(log_level)
logging.getLogger('werkzeug').addHandler(api_handler)


db_handler = TimedRotatingFileHandler(
        settings.DB_LOG_FILE,
        when='midnight',
        backupCount=10
    )
db_handler.setLevel(log_level)
db_handler.setFormatter(log_formatter)
logging.getLogger('sqlalchemy.engine').addHandler(db_handler)
logging.getLogger('sqlalchemy.engine').setLevel(log_level)
logging.getLogger('sqlalchemy.dialects').addHandler(db_handler)
logging.getLogger('sqlalchemy.dialects').setLevel(log_level)
logging.getLogger('sqlalchemy.pool').addHandler(db_handler)
logging.getLogger('sqlalchemy.pool').setLevel(log_level)
logging.getLogger('sqlalchemy.orm').addHandler(db_handler)
logging.getLogger('sqlalchemy.orm').setLevel(log_level)

database.py

...
engine = create_engine(getDBURI(), echo="debug", echo_pool=True, pool_recycle=10)

回答

如果有人遇到此问题,供将来参考,sqlalchemy 引擎配置 echo=True|'debug' 将覆盖您的记录器。通过将我的引擎配置更改为:

解决了这个问题
engine = create_engine(getDBURI(), echo_pool=True, pool_recycle=10)

然后一切都很顺利。干杯! :D

据我了解,您的 werkzeug 基于文件的日志配置实际上正在运行 => 它输出到 api.log

数据库日志处理程序也在工作(创建文件等)但没有输出。 这可能是由于这些记录器的日志级别默认处于错误状态。您需要像这样在较低级别手动设置它们:

logging.getLogger('sqlalchemy.engine').setLevel(logging.DEBUG)
logging.getLogger('sqlalchemy.dialects').setLevel(logging.DEBUG)
logging.getLogger('sqlalchemy.pool').setLevel(logging.DEBUG)
logging.getLogger('sqlalchemy.orm').setLevel(logging.DEBUG)

werkzeug 仍在向控制台输出可能是因为始终定义了根记录器。在添加新的处理程序之前,您应该执行以下操作以删除所有日志处理程序:

    for handler in copy(logging.getLogger().handlers):
        logging.getLogger().removeHandler(handler)
        handler.close()  # clean up used file handles

然后您还可以将您的应用程序日志处理程序指定为根日志处理程序

logging.getLogger().addHandler(api_handler)

如果它不是根记录器,而只是定义了默认控制台记录器的 werkzeug 记录器,您也可以在添加您的记录器之前从 werkzeug 记录器中删除所有处理程序,如下所示:

    for handler in copy(logging.getLogger('werkzeug').handlers):
        logging.getLogger('werkzeug').removeHandler(handler)
        handler.close()  # clean up used file handles
    logging.getLogger('werkzeug').addHandler(api_handler)