機械学習
python
自動取引
作っている株の自動取引基盤でログを分析するにあたって、json形式のログを扱えるようにしたくなった。 特にCloudWatchInsightでログを漁るときに、ログにタグのようなものを付けられると使い勝手が良さそうだということで環境を整備した。 1からloggingライブラリのFormatterを自作するのは面倒くさそうなので、省ける手間はライブラリを使って可能な限り省きたい。 いい感じのpython-json-loggerというライブラリが見つかったのでこれを使った。
loggingのFormatterとして提供されているので、Handlerに登録すれば使える。 以下の2つの方法で出力した内容を指定することで、自由に構造化されたログを出力できるようになる。
loggingのFormatterは第一引数でログの出力形式を指定できるが、 python-json-loggerもその形式に倣った方法で出力する情報を指定できる。 コンストラクタの第一引数にformat文字列を渡すことで構造化ログに含めるLogRecordの情報を変更できる。 使えるattributeはLogRecordに定義されているものと同一。
import logging
import sys
from pythonjsonlogger import jsonlogger
def setup_logging(log_level=logging.INFO):
handlers = []
formatter = jsonlogger.JsonFormatter("%(levelname)%(exc_info)%(message)")
sh = logging.StreamHandler(sys.stdout)
sh.setFormatter(formatter)
sh.setLevel(log_level)
handlers.append(sh)
logging.basicConfig(level=log_level, handlers=handlers, force=True)
setup_logging()
logger = logging.getLogger()
logger.info("Hello")
>> {"levelname": "INFO", "exc_info": null, "message": "Hello"}
上の方法はLogRecord自体が持っている情報を出力する方法だったが、こちらはログする際に任意の情報を付加する方法。 logging.infoやlogging.errorでログを残す際にキーワード引数extraで追加の情報を渡してあげる。(extra引数自体はloggingの標準機能) こんな感じで辞書のルート階層?にextraで指定した情報が追加される。
# ほとんど上のコードと同じ
import logging
import sys
from pythonjsonlogger import jsonlogger
def setup_logging(log_level=logging.INFO):
handlers = []
formatter = jsonlogger.JsonFormatter("%(levelname)%(exc_info)%(message)")
sh = logging.StreamHandler(sys.stdout)
sh.setFormatter(formatter)
sh.setLevel(log_level)
handlers.append(sh)
logging.basicConfig(level=log_level, handlers=handlers, force=True)
setup_logging()
logger = logging.getLogger()
# extraにログに出力したい情報を追記
logger.info("Hello", extra={"target": "World"})
>> {"levelname": "INFO", "exc_info": null, "message": "Hello", "target": "World"}
思惑通りCloudWatchに構造化ログを出力できた。 CloudWatch Insightなどで集計処理などが非常に書きやすくなるので便利。