ログの出力

2025年8月9日

はじめに

Python におけるログの出力について。

環境

Python 3.10.17

ログの出力

Python におけるログの出力には、標準ライブラリの logging が使える。

log_test.py

import logging

# ログレベルの設定
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# ログの出力
logger.debug("デバッグ情報")
logger.info("一般的な情報")
logger.warning("警告メッセージ")
logger.error("エラーメッセージ")
logger.critical("重大なエラー")

実行

$ python log_test.py
INFO:__main__:一般的な情報
WARNING:__main__:警告メッセージ
ERROR:__main__:エラーメッセージ
CRITICAL:__main__:重大なエラー

ファイルへの出力

log_test2.py

import logging
import sys

# ロガーの設定
def setup_logger(name, log_file=None, level=logging.INFO):
   logger = logging.getLogger(name)
   logger.setLevel(level)

   # フォーマットの設定
   formatter = logging.Formatter(
       '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
   )

   # コンソール出力
   console_handler = logging.StreamHandler(sys.stdout)
   console_handler.setFormatter(formatter)
   logger.addHandler(console_handler)

   # ファイル出力
   if log_file:
       file_handler = logging.FileHandler(log_file, encoding='utf-8')
       file_handler.setFormatter(formatter)
       logger.addHandler(file_handler)

   return logger

logger = setup_logger(__name__, 'app.log')
logger.info("アプリケーションが開始されました")

これは、指定した名前でロガーを作成して、コンソール出力のハンドラと、ファイル出力のハンドラを設定している。

実行

$ python log_test2.py
2025-07-16 14:38:38,056 - __main__ - INFO - アプリケーションが開始されました

app.logにも書き込まれる。

ミリ秒の小数点をピリオドにする

ログの時間のミリ秒の小数点がカンマになっているのが気になるなら、以下のようにしてピリオドに変更できる。

   # フォーマットの設定
   formatter = logging.Formatter(
       '%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s - %(message)s',
       datefmt='%Y-%m-%d %H:%M:%S'
   )

実行

$ python log_test2.py
2025-07-16 14:43:37.982 - __main__ - INFO - アプリケーションが開始されました

コンフィグファイル

セットアップ部分を外部ファイルにすることもできる。

log_test3.py

import logging.config

logging.config.fileConfig('logging.conf')
logger = logging.getLogger('app')

logger.info("アプリケーションが開始されました")

logging.conf

[loggers]
keys=root,app

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_app]
level=INFO
handlers=consoleHandler,fileHandler
qualname=app
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=('app.log', 'a', 'utf-8')

[formatter_simpleFormatter]
format=%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S

上の例では、ロガーが root と app の2つ設定されている。root はルートロガーで、最上位ロガーである。logger_root で root の処理レベルやハンドラを設定している。ここで level=DEBUG というのは、DEBUG 以上のメッセージを出力するという意味である (NOTSET というすべて表示するものもあるが、DEBUG と変わらない)。

logger_app で app の設定をしている。qualname はロガーの名前で、getLogger() で指定するものである。propagate は上位ロガーに伝播させるかどうかのスイッチで、0 なら伝播させない。1 にすると root の方でも処理される。それぞれのハンドラで処理されるので、上の例の場合は伝播させるとログがコンソールに 2 つずつ出てくる。ただ、app のほうでレベルに INFO を指定しているので、DEBUG 出力は上位にも行かず、どこにも出てこないことになる。

もし、app 以外の適当な名前でロガーを作成した場合は、root の設定が用いられる。

ログローテーション

log_test4.py

import logging
from logging.handlers import TimedRotatingFileHandler
import os

def setup_daily_logger(name, log_dir='logs'):
   # ログディレクトリの作成
   os.makedirs(log_dir, exist_ok=True)

   logger = logging.getLogger(name)
   logger.setLevel(logging.INFO)

   # 日付ごとのローテーションハンドラー
   handler = TimedRotatingFileHandler(
       filename=os.path.join(log_dir, 'app.log'),
       when='midnight',  # 毎日午前0時にローテーション
       interval=1,       # 1日間隔
       backupCount=30,   # 30日分のバックアップを保持
       encoding='utf-8'
   )

   # ログファイル名にタイムスタンプを追加
   handler.suffix = '%Y%m%d'

   formatter = logging.Formatter(
       '%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s - %(message)s',
       datefmt='%Y-%m-%d %H:%M:%S'
   )
   handler.setFormatter(formatter)
   logger.addHandler(handler)

   return logger

logger = setup_daily_logger(__name__)
logger.info("アプリケーションが開始されました")

logs ディレクトリが作成され、中に app.log ができる。古いものは "app.log.20250710" といった名前になる。

logging2.conf

[loggers]
keys=root,app

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_app]
level=INFO
handlers=consoleHandler,fileHandler
qualname=app
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=('app.log', 'a', 'utf-8')

[formatter_simpleFormatter]
format=%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S

CSV 出力

CSV で出力したい場合は、次のようにする。

logging3.conf

[loggers]
keys=root,app

[handlers]
keys=consoleHandler,timedRotatingFileHandler

[formatters]
keys=csvFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_app]
level=INFO
handlers=consoleHandler,timedRotatingFileHandler
qualname=app
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=csvFormatter
args=(sys.stdout,)

[handler_timedRotatingFileHandler]
class=logging.handlers.TimedRotatingFileHandler
level=DEBUG
formatter=csvFormatter
args=('logs/app.csv', 'midnight', 1, 30, 'utf-8')

[formatter_csvFormatter]
format="%(asctime)s.%(msecs)03d","%(name)s","%(levelname)s","%(message)s"
datefmt=%Y-%m-%d %H:%M:%S