Page List

Search on the blog

2016年5月22日日曜日

Pythonでログ出力をする

問題設定

2016-05-22 18:02:48,092 INFO [message]
2016-05-22 18:02:48,199 INFO [message]
2016-05-22 18:02:48,300 INFO [message]
...

のように出力時刻、レベル名、メッセージをログファイルに出力する場合を考える。
また、一定時間ごとにロテートする機能が欲しいとする。

実装1
import logging
import time

from logging.handlers import TimedRotatingFileHandler

logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
handler = TimedRotatingFileHandler('test.log', when="s", interval=1, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
logger.addHandler(handler)

def main():
    for i in range(100):
        logger.info("%d th iteration!" % i)
        time.sleep(0.1)

if __name__ == '__main__':
    main()

TimedRotatingFileHandlerを使うと目的のロテートが出来る。
上の実装だと1sごと(when, intervalで設定)にロテートするようにしている。
最新のログはtest.logに吐かれ、古いログは
test.log.2016-05-22_18-02-48
test.log.2016-05-22_18-02-49
...
のようにロテートされる。保持する古いログファイルは5つ(backupCountで設定)までとしている。

実装2
複数のロガーを使いたい場合は、実装1のように毎回プログラム上でロガーを作成していては面倒。その場合は、設定ファイルを使ってロガーを定義しておくとよい。

log_conf = {
  "version": 1,
  "disable_existing_loggers": False,

  "formatters": {
    "simple": {
      "format": "%(asctime)s %(levelname)s %(message)s"
    }
  }, 

  "handlers": {
    "time_rotate": {
      "class": "logging.handlers.TimedRotatingFileHandler",
      "formatter": "simple", 
      "filename": "test.log", 
      "when": "s",
      "interval": 1,
      "backupCount": 5
    }
  },

  "loggers": {
    "app": {
      "level": "INFO",
      "handlers": ["time_rotate"]
    }
  }
}

とりあえずpythonのdictで設定を書いた。
設定ファイルはdictに変換できればいいので、jsonやyamlで書いてもいい。

設定ファイル3行目のdisable_existing_loggersは、既存のロガーを無効化するかどうかの設定。デフォルト の値はTrueで、その場合、設定ファイルをロードする前に生成されたロガーが無効化されてしまう。無効化して欲しくない場合は、Falseを設定する。

設定ファイルのロードは以下のように行う。実装1と比べるとだいぶスッキリした。
import logging.config
import time
from conf import log_conf

logging.config.dictConfig(log_conf)
logger = logging.getLogger("app")

def main():
    for i in range(100):
        logger.info("%d th iteration!" % i)
        time.sleep(0.1)

if __name__ == '__main__':
    main()

0 件のコメント:

コメントを投稿