Pythonのロギング

pythonの公式ドキュメントはどうにも不自由な日本語なのでまとめ直し

Loggingモジュールのイメージ

Loggerオブジェクト

loggerオブジェクトを各モジュールに明示する( logging.getLogger(“hogehoge”) )ことで、ログを収集する。

loggerは名前で管理され、違うモジュールでも同じ名前を指定すると同じloggerになる。

木構造

loggerは階層構造を持ち、名前をピリオドで分割することで表現する。

Python パッケージ名前空間におけるモジュール名は __name__ で取得できるので、


logging.getLogger(__name__)

を使ってloggerをモジュール単位で構成することが推奨される。

loggerとhandlerを組み合わせて君だけの最強のloggingを手に入れろ!

loggerがログを収集するものだとすれば、handlerはログを吐き出すオブジェクトである。

loggerとhandlerは多対多で紐付けることが可能である。

標準出力用のhandlerとログファイル出力用のhandlerを用意してどちらもルートロガーに紐付ければ、そのアプリケーションの全loggerの出力を標準出力&ログファイルに出力できる。

 logging

複数モジュールでstdoutとファイルにログを吐き出すサンプル

  • 構成例

# 実行ファイル
cook_sukiyaki.py

# 設定ファイル
settings.py

# モジュール群
Models
  |- Nabe.py
  |- Cooker.py
  |- SukiyakiGuzai.py
  |- Soup.py

# サブモジュール群
AussieBeef.py        # SukiyakiGuzai.pyでimport
Gas.py                     # Cooker.pyでimport
   :
   :
  • settings.py

#!/usr/bin/env python
# coding: utf-8
import os
import argparse
import logging
import logging.handlers


# デバッグか否かをコマンドラインオプションで指定
parser = argparse.ArgumentParser(description="Sukiyaki tsukuruyo!")
parser.add_argument("-d", "--debug", action="store_true", help="Debug mode.")
parser.add_argument("-b", "--beef", type=str, default="aussie", choices=["aussie", "koube"], help="Beef choice.")
# 引数のparse
args = parser.parse_args()


if args.debug:
    LOG_LEVEL = logging.DEBUG
else:
    LOG_LEVEL = logging.INFO
logger = logging.getLogger("") # root loggerに対して設定することで、これ以後使うloggerすべてに共通の設定が適用される。
formatter = logging.Formatter(
    fmt="%(asctime)s:[%(name)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
logger.setLevel(LOG_LEVEL)

# stdout出力
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
stream_handler.setLevel(LOG_LEVEL)
logger.addHandler(stream_handler)

# ログ出力
file_handler = logging.handlers.RotatingFileHandler(
    os.path.join(os.path.abspath(os.path.dirname(__file__)), "log", "app.log"),
    maxBytes=50000000, backupCount=5)
file_handler.setFormatter(formatter)
file_handler.setLevel(LOG_LEVEL)
logger.addHandler(file_handler)

BEEF = args.beef
  • cook_sukiyaki.py

#!/usr/bin/env python
# coding: utf-8
import settings

from Nabe import Nabe
from Cooker import Cooker
from SukiyakiGuzai import SukiyakiGuzai
from Soup import Soup

from logging import getLogger
# __name__でloggerを指定することで、pythonの階層構造と同じようにloggerの名前が付与される。
logger = getLogger(__name__)

def cook_sukiyaki(nabe, cooker, guzai, soup):
   logger.info("Sukiyaki tsukuruyo!")
   logger.debug("Beef: " + guzai.beef)
     :
     :

if __name__ == "__main__":
    nabe = Nabe()
    cooker = Cooker()
    guzai = SukiyakiGuzai(beef=settings.BEEF)
    soup = Soup()
  • モジュール、サブモジュールでのloggingでの呼び出し(例: Nabe.py)

#!/usr/bin/env python
# coding: utf-8

# これを書くだけでモジュールごとにloggerが作られる。
from logging import getLogger
logger = getLogger(__name__)

class Nabe:
    pass
  • log例

2016-07-04 18:26:51:[cook_sukiyaki.py] Sukiyaki tsukuruyo!
2016-07-04 18:26:56:[cook_sukiyaki.py] Beef: aussie
2016-07-04 18:27:00:[Models.Nabe] Nabe oitayo!

ハマりがちな落とし穴

なんか実行してもstdoutに表示されないんだけど

参考

Leave a Comment

Your email address will not be published. Required fields are marked *