Python ログ、標準の logging

以前、
Python ログ出力 logging iniファイルを使用しない - Oboe吹きプログラマの黙示録
を書いたが、隣接のサブディレクトリから使用する場合、呼出し側をきちんとインポートしないと、
 ValueError: attempted relative import beyond top-level package
になる。
例えば、
 blue/logger.py
に対して blue と red が隣接、同じ階層ディレクトリであるとき、
red/some.py
から、logger.py をインポートするので、

from ..blue.logger import Logger

と書くが ValueError: attempted relative import beyond top-level package になってしまうことがある。
この場合は、スクリプトがフルパス指定で実行されるのが前提で!

import sys
from pathlib import Path
sys.path.append('%s' % Path(__file__).parent.parent.resolve())
from blue.logger import Logger

と、__file__を利用する
Linux で実行するとき、うっかり、、

$ python  some.py 

と書いてしまうとダメだ、ちゃんとフルパスでスクリプトを指定する必要がある。
以前、書いた logger.py も、あまり良くないので改めて書き直す。

# -*- coding: UTF-8 -*-
from logging import Formatter, handlers, StreamHandler, getLogger, DEBUG, WARN, INFO
import inspect

class Logger:
    def __init__(self, name=__name__):
        if name=="blue.logger":
            fsplits = inspect.stack()[1].filename.split('/')
            name = fsplits[len(fsplits)-1]
        # ロガー生成
        self.logger = getLogger(name)
        self.logger.setLevel(DEBUG)
        formatter = Formatter(fmt="%(asctime)s.%(msecs)03d %(levelname)7s %(message)s [%(name)s  %(processName)s - %(threadName)s]",
                              datefmt="%Y/%m/%d %H:%M:%S")

        # 時刻ローテーション
        handler = handlers.TimedRotatingFileHandler(filename='/var/log/test.log',
                                                    encoding='UTF-8',
                                                    when='D',
                                                    backupCount=7 )
        # サイズローテーション
        ''' 
        handler = handlers.RotatingFileHandler(filename='/var/log/test.log',
                                               encoding='UTF-8',
                                               maxBytes=1048576,
                                               backupCount=3)
        '''
        # ログファイル設定
        handler.setLevel(INFO)
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        # 標準出力用 設定: DEBUG レベルまで標準出力する
        sthandler = StreamHandler()
        sthandler.setLevel(DEBUG)
        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, exc_info=False):
        self.logger.error(msg, exc_info=exc_info)
    def critical(self, msg):
        self.logger.critical(msg)

この中の logger.error は、

    def error(self, msg, exc_info=False):
        self.logger.error(msg, exc_info=exc_info)

True 指定でスタックトレースも出るようにしている

Logger生成で引数を省略すれば、呼出し元スクリプト名が %(name) で 出力

import sys
from pathlib import Path
# sys.path.append('%s' % Path(__file__).parent.parent.resolve())
from ..blue.logger import Logger

logger = Logger()
logger.error("メッセージ  ERROR")
logger.warn("メッセージ  WARN")
logger.info("メッセージ  INFO")
logger.debug("メッセージ  DEBUG")

Logger生成で引数を指定すれば、%(name) は指定した文字列になる

import sys
from pathlib import Path
# sys.path.append('%s' % Path(__file__).parent.parent.resolve())
from ..blue.logger import Logger

logger = Logger('gold')
logger.error("メッセージ  ERROR")
logger.warn("メッセージ  WARN")
logger.info("メッセージ  INFO")
logger.debug("メッセージ  DEBUG")