過去に何度も書いたが、改めて書き直すことにした。
設定ファイルと、logger.py という logging を書いたスクリプトで構成する。
JSON で記述する設定ファイル(logsetting.json というファイル名)で、 Logger 生成で引数 name に対する以下属性を設定する。
Logger 生成で引数 name を省略する場合は、'default' として以下設定を記述する。
format | ログメッセージフォーマット |
dateformat | ログメッセージ内の時刻フォーマット datetime.strftime の規則と同じ書式で指定する。 |
rotation | ローテーション方法を指定 daily or size |
maxHistory | 履歴として保持する最大数 |
maxbytes | ログファイルの最大サイズをバイトで指定 (サイズローテーションでは必須) |
level | ログ出力レベル CRITICAL, ERROR, WARN, INFO, DEBUG |
stdout | 標準出力するか否か デフォルトは false |
path | ログファイルパス |
logsetting.json のサンプル
{ "default": { "format": "%(asctime)s.%(msecs)03d %(levelname)7s[%(processName)s:%(threadName)s] %(message)s", "dateformat": "%Y-%m-%d %H:%M:%S", "rotation": "daily", "maxHistory": 7, "level": "DEBUG", "path": "/var/log/project.log" }, "alpha": { "format": "%(asctime)s.%(msecs)03d %(levelname)7s %(message)s", "dateformat": "%Y-%m-%d %H:%M:%S", "rotation": "daily", "maxHistory": 7, "level": "DEBUG", "stdout": true, "path": "/var/log/project.log" }, "beta": { "format": "%(asctime)s.%(msecs)03d %(levelname)7s %(message)s", "dateformat": "%Y-%m-%d %H:%M:%S", "rotation": "size", "maxbytes": 1048576, "maxHistory": 4, "level": "INFO", "stdout": false, "path": "/var/log/sample.log" } }
logsetting.json と同じディレクトリに、以下、logger.py を用意する。
# -*- coding: UTF-8 -*- # logger.py : logsetting.json によるロガー # from logging import Formatter, handlers, StreamHandler, getLogger, CRITICAL, ERROR, WARN, INFO, DEBUG import os from pathlib import Path import codecs import json class Logger: def __init__(self, name='default'): leveldict = {"DEBUG": DEBUG, "INFO": INFO, "WARN": WARN, "ERROR": ERROR, "CRITICAL": CRITICAL} with codecs.open("%s/logsetting.json" % Path(os.path.abspath(__file__)).parent.resolve(), 'r', 'utf-8') as f: self.logger = getLogger(name) if len(self.logger.handlers) > 0: return setting = json.load(f, strict=False) if name=='default': if not 'default' in setting: raise RuntimeError('logsetting.json ERROR default Not Found') dict = setting[name] if name in setting else setting['root'] if not 'format' in dict: raise RuntimeError('logsetting.json ERROR format Not Found at %s' % name) if not 'dateformat' in dict: raise RuntimeError('logsetting.json ERROR dateformat Not Found at %s' % name) if not 'maxHistory' in dict: raise RuntimeError('logsetting.json ERROR maxHistory Not Found at %s' % name) self.logger.setLevel(leveldict[dict['level']] if 'level' in dict else INFO) formatter = Formatter(fmt=dict['format'], datefmt=dict['dateformat']) if dict['rotation']=='daily': # 時刻ローテーション handler = handlers.TimedRotatingFileHandler(filename=dict['path'], encoding='UTF-8', when='D', backupCount=dict['maxHistory'] if 'maxHistory' in dict else 7) elif dict['rotation']=='size': # サイズローテーション if not 'maxbytes' in dict: raise RuntimeError('logsetting.json ERROR maxbytes Not Found at %s' % name) handler = handlers.RotatingFileHandler(filename=dict['path'], encoding='UTF-8', maxBytes=dict['maxbytes'] if 'maxbytes' in dict else 1048576, backupCount=dict['maxHistory'] if 'maxHistory' in dict else 3) else: raise RuntimeError('logsetting.json ERROR rotation Not Found') # ログファイル設定 handler.setLevel(leveldict[dict['level']] if 'level' in dict else INFO) handler.setFormatter(formatter) self.logger.addHandler(handler) if dict['stdout'] if 'stdout' in dict else False: # 標準出力用 設定 sthandler = StreamHandler() sthandler.setLevel(dict['level'] if 'level' in dict else INFO) sthandler.setFormatter(formatter) self.logger.addHandler(sthandler) def debug(self, msg): self.logger.debug(msg) def info(self, msg): self.logger.info(msg) def warn(self, msg): self.logger.warning(msg) def error(self, msg, stack_info=False): self.logger.error(msg, stack_info=stack_info) def critical(self, msg): self.logger.critical(msg, stack_info=True)
この logger.py が示す中で注目すべきは、
ロガーを生成して呼出し元で生成いたロガーに伝播させない方法に、propagate 属性を False にする方法があるが、
logger = logging.getLogger(name)
logger.propagate = False
これが、効力を持たないことがある。
self.logger に getLogger した後、既に addHandler でハンドラ追加されている数を
len関数で調べて、コンストラクタを抜けるようにしている。
使用例、
from インポート文は、状況に合わせるとして、
from logger import Logger logger = Logger() logger.error("メッセージ ERROR") logger.warn("メッセージ WARN") logger.info("メッセージ INFO") logger.debug("メッセージ DEBUG")
from logger import Logger logger = Logger(’alpha') logger.error("メッセージ ERROR") logger.warn("メッセージ WARN") logger.info("メッセージ INFO") logger.debug("メッセージ DEBUG")