Flask の logging 設定
tl;dl
- 虚無ありき - Django の logging 設定 の Flask Version
- やりたいこととやることはほとんど一緒
環境
これも一緒
$ cat /etc/os-release NAME="Alpine Linux" ID=alpine VERSION_ID=3.8.2 PRETTY_NAME="Alpine Linux v3.8" HOME_URL="http://alpinelinux.org" BUG_REPORT_URL="http://bugs.alpinelinux.org" $ python -V Python 3.7.2 $ django-admin version 2.1.5
Flask の Logging の初期設定
- Flask - Docs - Logging
- あんまり詳しくは書いていない
下のコードで、Flask インスタンス作成時の Logger を調べると、
# coding: utf-8 import logging from pprint import pprint from flask import Flask if __name__ == "__main__": app = Flask(__name__) pprint(logging.root.manager.loggerDict)
{'asyncio': <Logger asyncio (WARNING)>, 'concurrent': <logging.PlaceHolder object at 0x7f71fe7bb550>, 'concurrent.futures': <Logger concurrent.futures (WARNING)>}
であり、Flask で生成される Logger は存在しない。
ただ、上の Docs より、
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 は root logger に色々吐いているようだ。
# coding: utf-8 import logging from pprint import pprint from flask import Flask if __name__ == "__main__": app = Flask(__name__) pprint(vars(logging.getLogger()))
{'_cache': {}, 'disabled': False, 'filters': [], 'handlers': [], 'level': 30, 'name': 'root', 'parent': None, 'propagate': True}
ここでは、handler は登録されていないが、root logger は console に出力する。
$ python3 Python 3.7.2 (default, Jan 30 2019, 23:40:31) [GCC 6.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import logging >>> root = logging.getLogger() >>> root.warning("FUGA") FUGA
どうしたか
logging_config.py
を作って以下を記述する。この際、root は handlers の中に入れず、別エントリを作る
# coding: utf-8 from .config import LOG_FILE_PATH from copy import deepcopy TEMPLATE = { "version": 1, "disable_existing_loggers": False, "formatters": { "default": { "format": "[%(asctime)s] %(levelname)s %(message)s", "datefmt": "%Y-%m-%d %H:%M:%S", }, "long": { "format": "[%(asctime)s] %(levelname)s - %(filename)s#%(funcName)s:%(lineno)d: %(message)s", "datefmt": "%Y-%m-%d %H:%M:%S", }, }, "handlers": { "console": { "level": "INFO", "class": "logging.StreamHandler", "formatter": "default", "stream": "ext://sys.stderr" }, "file": { "level": "INFO", "class": "logging.FileHandler", "formatter": "default", "filename": LOG_FILE_PATH, }, }, "root": { "handlers": ["console", "file"], "level": "INFO", }, } wsgi_info = deepcopy(TEMPLATE) wsgi_info["root"]["handlers"] = ["file"] wsgi_debug = deepcopy(TEMPLATE) wsgi_debug["handlers"]["file"]["level"] = "DEBUG" wsgi_debug["handlers"]["file"]["formatter"] = "long" wsgi_debug["root"]["handlers"] = ["file"] wsgi_debug["root"]["level"] = "DEBUG" local_info = deepcopy(TEMPLATE) local_debug = deepcopy(TEMPLATE) local_debug["handlers"]["console"]["level"] = "DEBUG" local_debug["handlers"]["console"]["formatter"] = "long" local_debug["handlers"]["file"]["level"] = "DEBUG" local_debug["handlers"]["file"]["formatter"] = "long" local_debug["root"]["level"] = "DEBUG"
Flask インスタンスを作成後に、logger を更新する。
# coding: utf-8 import logging from pprint import pprint import os from flask import Flask if __name__ == "__main__": app = Flask(__name__) if os.environ.get("DEBUG") == "True": if os.environ.get("LOG_LEVEL") == "DEBUG": dictConfig(local_debug) else: dictConfig(local_info) else: if os.environ.get("LOG_LEVEL") == "DEBUG": dictConfig(wsgi_debug) else: dictConfig(wsgi_info)
更に、Flask は Debug mode の場合、ブラウザで debug するための html が response で帰ってくる。また、log には traceback の情報が残らない仕様になっている。そのため、Error Handler を編集する。
@app.errorhandler(400) @app.errorhandler(401) @app.errorhandler(404) @app.errorhandler(500) def error_handler(error): response = { "msg": error.description, "status_code": error.code, } response = jsonify(response) response.status_code = error.code return response @app.errorhandler(Exception) def error_handler_exception(exception): import traceback root_logger.error(exception.args[0]) root_logger.debug(traceback.format_exc()) response = { "msg": "The server encountered an internal error and was unable to complete your request.", "status_code": 500, } response = jsonify(response) response.status_code = 500 return response return app
上を使うことで、DEBUG レベルの log として、traceback が出力されるようになる。