ボールを蹴りたいシステムエンジニア

ボール蹴りが大好きなシステムエンジニア、ボールを蹴る時間確保の為に時間がある時には勉強する。

pythonでのロギング機能を実装してみる。(設定ファイル読み込んでloggerで出力)

pythonを勉強しはじめの頃はprintでログ出力していたが、そろそろ規模も大きくなってきて運用も視野に入れpythonでのロギング機能を実装してみる。

標準出力と/tmp/test.logの両方にログ出力する場合の例
ログレベルはDEBUG

from logging import getLogger,Formatter,StreamHandler,FileHandler,DEBUG
logger = getLogger("test")
formatter = Formatter('%(asctime)s [%(levelname)s] [%(filename)s: %(funcName)s: %(lineno)d] %(message)s')
handlerSh = StreamHandler()
handlerFile = FileHandler("c:/tmp/test.log")
handlerSh.setFormatter(formatter)
handlerSh.setLevel(DEBUG)
handlerFile.setFormatter(formatter)
handlerFile.setLevel(DEBUG)
logger.setLevel(DEBUG)
logger.addHandler(handlerSh)
logger.addHandler(handlerFile)

上記だと、全てのPythonファイルに記述しなくていけないので面倒なので設定ファイル読み込み版にする。
Python2.7から導入されたというYAML形式が記述しやすい

インストール

pip install PyYAML

前述と設定内容は変わるけどこんな感じで設定。
logging.conf

version: 1

formatters:
  custmoFormatter:
    format: '[%(asctime)s] %(levelname)s - %(filename)s#%(funcName)s:%(lineno)d: %(message)s'
    datefmt: '%Y/%m/%d %H:%M:%S'

loggers:
  test:
    handlers: [fileRotatingHandler,consoleHandler]
    level: DEBUG
    qualname: test
    propagate: no

  file:
    handlers: [fileRotatingHandler]
    level: DEBUG
    qualname: file
    propagate: no

  console:
    handlers: [consoleHandler]
    level: DEBUG
    qualname: console
    propagate: no

handlers:
  fileRotatingHandler:
    formatter: custmoFormatter
    class: logging.handlers.TimedRotatingFileHandler
    level: DEBUG
    filename: c:/tmp/test.log
    encoding: utf8
    when: 'D'
    interval: 1
    backupCount: 14

  consoleHandler:
    class: logging.StreamHandler
    level: DEBUG
    formatter: custmoFormatter
    stream: ext://sys.stdout

root:
  level: DEBUG
  handlers: [fileRotatingHandler,consoleHandler]

最後のrootを忘れると動かないので注意。
インデントが不正だと「'NoneType' object is not iterable」エラーが発生するので注意。

ログ出力するPythonコード側はこんな感じ

import yaml
from logging import config,getLogger
config.dictConfig(yaml.load(open("logging.conf", encoding='UTF-8').read()))
logger = getLogger("test")

logger.debug("output logging test!!")

config.dictConfigとかの処理は共通初期化モジュールとかに分離したい所。

ちなみに、Pythonではエラーログで例外情報出力用のメソッドがあるらしい、便利。

try:
    dosomething()
except (KeyError, ValueError) as err:
    logger.exception('Error dosomething: %s', err)