プロジェクトが大きくなるにつれて、プログラムの不備を見つけるのにログの構成、ログデザインを考えることが重要になってくる。上手なログの書き方を覚えれば、プリントデバッグがとても用意になるので、習慣づける価値はある。
システムトレードの取引をしていると、サーバーの遅延や分散処理による非同期処理が増えて全体としてのフローが複雑になって、取引残高がずれた時の原因究明が容易ではなくなっていく。その時に、ログを上手に使ってトラッキングできるようにしておくと、不具合が起こった時の対処が楽になる。
Logging Module
LogBook
- mitsuhiko (Flaskの開発者) が開発にいた
- スター数一位
- 追加の情報や、logをサーバーに収集させる機能が標準で備わってる
- 他の主要ライブラリで使われていない
- logging との親和性は悪そう?
structlog
- 変数の確認に使いやすくなっている。
- ターミナルに色付けできる
logzero
- 色をつける
- FileRatation
- unicode
- logging との親和性高め
logzero を使うことにする
基本的には標準のloggingを用いて、拡張機能を利用したい時だけlogzeroを使うという形が取れるのがlogzeroのメリット。
logging を使う上でのお勧め機能
Formatter を定義
呼び出し元のファイルや行数までログに出力できる。
お勧め Format 設定
"%(color)s%(asctime)s-%(threadName)s|%(filename)s:%(lineno)d|%(levelname)-7s%(end_color)s : %(message)s"
同一テキストの書き出しを行っても、呼び出し元が違うことが分かるだけで十分なヒントになる。
color
というのは、 logzero.LogFormatter
独自のもの。
必要に応じてここからパラメータを引っ張ると良い
stdout と stderr も出力させる
stderr で落ちたときの処理もpython側でloggingに流せるようにしたい。
ライブラリ化してみた。
logging の設定が終わったあとに下記を追加するだけ。
import stdlogging
stdlogging.enable()
これで、terminalでログの確認をしなくて良くなる。
Logging Config File
ファイルからLOGLEVELの設定を行えるようにする。
ログ用サーバー
- Elasticsearch
- LogStash
ネットワークを使ったログ収集で、これらを使うと統計情報の利用などの連携ソフトが使いやすくなるが、個人的にはそこまでの必要は今のところない。
LogViewer
SnakeTail
- 一致するものに色をつけることができるなど大体の要望は満たす。
- ただ、表示するものをフィルタリングで選べない(DEBUGは表示しないなど)
サンプルコード
pip install stdlogging logzero
必要モジュールをインストール。
import logging
import logzero
import time
import threading
import stdlogging
from logzero import logger
def test_log(logger):
logger.error("log from thread")
if __name__ == '__main__':
formatter = logzero.LogFormatter(
fmt="%(color)s%(asctime)s-%(threadName)s|%(filename)s:%(lineno)d|%(levelname)-7s%(end_color)s : %(message)s",
datefmt=None)
logging.basicConfig(level=logging.DEBUG)
root_logger = logging.getLogger()
handler = root_logger.handlers[0]
handler.setFormatter(formatter)
logging.info("log")
threading.current_thread().name = 'LogMain'
logger = logzero.setup_logger(formatter=formatter)
logger.debug("hello")
logger.info("info")
logger.warn("warn")
logger.error("error")
t = threading.Thread(target=test_log, args=(logger,), name="LogThread")
t.start()
t.join()
stdlogging.enable()
print "test comment"
raise RuntimeError("Runtime Error")
コメント