0
0

pythonのloggingが便利だと知った話

Posted at

記事の対象者

  • printでlogを出している
  • pythonの標準モジュールloggingを使ってみたい

printでのlog出力

例えばログに以下の内容を含めたいとします

  • logレベル(debug, info, warn, error, critical)
  • 時間
  • モジュール名
  • 関数名
  • 任意のメッセージ

printを使った場合、以下のような関数実装になるかなと思います

from datetime import datetime as dt

def print_log(log_level: str, module_name: str, func_name: str, message: Optional[str] = None):
    """printでlogを出す関数

    Args:
        log_level (str): logレベル
        module_name (str): モジュール名
        func_name (str): 関数名
        message (Optional[str]): logに出力したいメッセージ
    """
    if message is not None:
        print("{time} {level} {name} {func} {meg}".format(time=dt.now().strftime('%Y/%m/%d %H:%M:%S'), level=log_level, name=module_name, func=func_name, meg=message))
        return
    
    print("{time} {level} {name} {func}".format(time=dt.now().strftime('%Y/%m/%d %H:%M:%S'), level=log_level, name=module_name, func=func_name))

↑の関数を使う場合

import print_log

def print_log_run():
    print_log('info', __name__, print_log_run.__name__, 'test')


if __name__ == '__main__':
    print_log_run()

# 結果
2024/06/29 10:43:52 info __main__ print_log_run test

出せてはいますが引数多いし、なんか汚いですよね。
messageを工夫したい時もさらに関数が複雑になりそうな予感が現段階でします。
さらにlogfileを出力したいと言われたら、更に複雑な関数になりそうです。

loggingでのlog出力

この問題を簡単に解決できるのがpythonの標準モジュールである「logging 」です。
使い方は更新サイトのものを載せます

import logging
logger = logging.getLogger(__name__)
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
logger.debug('This message should go to the log file')
logger.info('So should this')
logger.warning('And this, too')
logger.error('And non-ASCII stuff, too, like Øresund and Malmö')

# 以下のような出力となる
DEBUG:__main__:This message should go to the log file
INFO:__main__:So should this
WARNING:__main__:And this, too
ERROR:__main__:And non-ASCII stuff, too, like Øresund and Malmö

loggingの使い方は更新サイトや分かりやすく記事があるので紹介します

オレオレlogging関数

loggingをいい感じに使える関数を作ったので載せます。

↓オレオレlogging関数

import logging

def get_logger(name: str, log_level: str, log_export_path: str):
    """ロガーの取得

    Args:
        name (str): モジュール名
        log_level (str): ログレベル
        log_export_path (str): ログの出力先(名前も含む)

    Returns:
        _type_: loggingクラス
    """
    if(log_level == 'debug'):
        level = logging.DEBUG
    else:
        level = logging.INFO
        
    logging.basicConfig(level=level)
    logger = logging.getLogger(name=name)
    handler = logging.FileHandler(log_export_path)
    handler.setLevel(level=level)

    format = '%(asctime)s %(levelname)s [%(name)s] %(funcName)s %(message)s'
    formatter = logging.Formatter(format)
    
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    return logger

関数の他にもget_message関数やmessageListを定義しておくとlog messageも簡単に設定することができます

message_list = {
    'TEST_01': 'Start Process {0}',
    'TEST_02': 'End Process',
    'TEST_03': 'Fail {}'
}

def get_message(log_id: str, *args: str):
    """configのログのmessageリストからメッセージを取得する

    Args:
        log_id (str): log_id -> message.pyを参照
        *args (str): logに出す可変長変数

    Returns:
        _type_: str
    """
    log_message = message_list[log_id]

    if(len(args) == 0):
        return log_message

    return log_message.format(*args)

↑を使用する場合

import get_logger, get_message

def logger_run():
    logger = get_logger(name=__name__, log_level='info', log_export_path='./log/test_log.log')
    logger.info(msg=get_message('TEST_01', 'test'))
    logger.info(msg=get_message('TEST_02'))

if __name__ == '__main__':
    logger_run()

log/test_log.logには以下のような形式で出力されている

2024-06-29 10:09:10,736 INFO run [__main__] Start Process test
2024-06-29 10:09:10,736 INFO run [__main__] End Process
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0