Python
python3

Python簡易コマンドラインツール用のlogging設定の備忘録

書き捨て以上、製品以下くらいの、自分用・社内用ツール的なスクリプトをPythonで作るとき、つい適当にしがちなログ処理を上品に実装するためのメモ。

#!/usr/bin/env python3

from logging import getLogger
logger = getLogger(__name__)
del getLogger

def _main():
    from argparse import ArgumentParser
    parser = ArgumentParser()
    # configure arguments here
    set_logging_arguments(parser)
    args = parser.parse_args()
    init_logging(args)
    log_environment_info(args)

    logger.debug('This is debug message.')
    logger.info('This is info message.')
    logger.warning('This is warning message.')
    logger.error('This is error message.')
    logger.critical('This is critical message.')


def set_logging_arguments(parser):
    q = parser.add_argument_group('logging arguments')
    q.add_argument('--verbose', '-v', action='count', default=0, help='increase log level')
    q.add_argument('--quiet', '-q', action='count', default=0, help='decrease log level')
    q.add_argument('--logfile', help='dump all logs to file')

def init_logging(args):
    from logging import getLogger, StreamHandler, FileHandler, Formatter, NOTSET, WARNING, CRITICAL
    rootlogger = getLogger()
    rootlogger.setLevel(NOTSET)

    handler = StreamHandler()
    # https://docs.python.org/3/library/logging.html?highlight=notset#logging-levels
    handler.setLevel(sorted([NOTSET, WARNING+(args.quiet-args.verbose)*10, CRITICAL])[1])
    rootlogger.addHandler(handler)

    if args.logfile:
        handler = FileHandler(args.logfile)
        handler.setLevel(NOTSET)
        # https://docs.python.org/3/library/logging.html?highlight=notset#logrecord-attributes
        handler.setFormatter(Formatter('%(asctime)s: %(name)s: %(levelname)s: %(message)s'))
        rootlogger.addHandler(handler)

def log_environment_info(args, *, logger=logger, envvars=True):
    import sys, os
    logger.debug(f'python executable: {sys.executable}')
    logger.debug(f'python version: {sys.version}')
    logger.debug(f'working directory: {os.getcwd()}')
    logger.debug(f'command line: {" ".join(map(repr,sys.argv))}')
    logger.debug(f'arguments:')
    for k in sorted(vars(args).keys()):
        logger.debug(f'    {k}: {getattr(args, k)!r}')
    if envvars:
        logger.debug(f'environment variables:')
        for k, v in sorted(os.environ.items()):
            logger.debug(f'    {k}: {v!r}')

if __name__ == '__main__':
    _main()

コマンドラインに、-q又は-vオプションを必要な個数付けることで、標準エラー出力に出すログの量を変えられるようにしている。
--logfileを指定すれば実行時の環境情報も含めて全てのログをファイルにダンプできる。環境変数までは要らない、という場合はenvvars=Falseとしてやる。

ログ設定をする3つの関数は、別ファイルに切り出して、

    from hogehoge import set_logging_arguments, init_logging, log_environment_info
    set_logging_arguments(parser)
    args = parser.parse_args()
    init_logging(args)
    log_environment_info(args, logger=logger)

のように読み込むようにしてやれば、目障りではなくなるし使いまわしも簡単になる。。