0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python loggingの基本とファイルや出力先の分割について

Posted at

始めに

Pythonで開発する際、ログの書き方や設定など毎回調べながら書いているので一度よく使用する設定をまとめておこうと思います

まずloggingの基本的な書き方を紹介したあとに、ファイル出力のより詳細なカスタマイズの方法についてサンプルコードを提示しながらざっくりとご紹介させていただきます

テストを行った環境

  • OS: Windows 11
  • Python: 3.13.5

ログの記録を始めるための基本の書き方

logging --- Python用のログ記録手段にも書かれている通りなのですが、複数のモジュールから、特定のファイルにログを記録していきたいとき、細かいことは気にせずにとりあえずログを取っていきたいときは以下のように書けばOKだと考えています

main.py
import logging
import mylib

logger = logging.getLogger(__name__)

logging.basicConfig(
    filename="myapp.log",
    format="{asctime} [{levelname}] {name}: {message}",
    datefmt="%Y-%m-%d %H:%M:%S",
    style="{",
    level=logging.INFO,
    encoding="utf-8",
)

def main():
    logger.info("Started")
    mylib.do_something()
    logger.info("Finished")

if __name__ == "__main__":
    main()
mylib.py
import logging

logger = logging.getLogger(__name__)

def do_something():
    logger.info("Doing something")
myapp.log
2025-08-22 10:30:36 [INFO] __main__: Started
2025-08-22 10:30:36 [INFO] mylib: Doing something
2025-08-22 10:30:36 [INFO] __main__: Finished

ロギングのポイント

意識するポイントは大きく3つくらいかな?と思います

1. ロガーをモジュールごとに分けて作成する

各モジュールの先頭でgetLogger(name)を使用してロガーのインスタンスを作成しましょう

logger = logging.getLogger(__name__)

2. ログの出力はlogger + レベルで行う

上記のように作成したloggerインスタンスとレベルに適した関数によってログを出力します
出力したログは3. で説明するファイル/ ストリームに出力されます

logger.debug("開発者向けの情報")
logger.info("想定通りのことが起こったことの確認")
logger.warning("ソフトウェアは期待通り動作するが、想定外の事象・将来の問題を示唆")
logger.error("重大な問題により、ソフトウェアがある機能を実行できない")
logger.critical("プログラムが実行を続けられないほどのエラー")

3. basicConfigはエントリーポイントの1箇所だけに書く

basicConfigのサンプルは以下のとおりです

logging.basicConfig(
    filename="myapp.log", # 出力したいファイル名
    format="{asctime} [{levelname}] {name}: {message}", # ログのフォーマット
    datefmt="%Y-%m-%d %H:%M:%S", # 日時のフォーマット
    style="{", # formatの変数埋め込みのスタイル 他にも $ や % を使った表記あり
    level=logging.INFO, # 指定したレベル以上のログのみ出力
    encoding="utf-8",
)

このbasicConfigですが、あるモジュールに関してログの一部だけフォーマットを変えたい、出力先を変えたい、などの理由からプロジェクト内で2回以上logging.basicConfigを実行しないようにしましょう
basicConfigの役割はルートロガーの基本的な構成を行う関数で、標準出力やファイル出力を行うためのハンドラーをルートロガーに追加します
そしてルートロガーにハンドラーが既に設定されている場合は(force=Trueにしない限り)何もされません
とはいうもののモジュールの読み込み順などの関係で、意図しないフォーマットや出力先にログが出力される可能性がありますので注意しましょう
フォーマットについてはこちら

出力先やフォーマットを分けたい場合は…

basicConfigは使用せずに、logging.FilHandlerを使用して細かいログ出力設定を行いましょう
※ ルートロガーには全てのログが渡されるので、basicConfigを使用するとログが2重3重で出力されるので注意しましょう

以下ハンドラーを使用した出力設定のサンプル

main.py
import logging
import mylib

logger = logging.getLogger(__name__)
# ファイル出力用のハンドラーを作成
file_handler = logging.FileHandler("myapp.log")
file_handler.setFormatter(
    logging.Formatter("{asctime} [{levelname}]  {name}: {message}", style="{")
)
# ハンドラー追加(複数個も可能)
logger.addHandler(file_handler)
logger.setLevel(logging.INFO)
mylib.py
import logging

logger = logging.getLogger(__name__)
# 出力先は同じところ
file_handler = logging.FileHandler("myapp.log")
#フォーマットをちょっとだけ変更
file_handler.setFormatter(logging.Formatter("[{levelname}]: {message}", style="{"))
logger.addHandler(file_handler)
logger.setLevel(logging.INFO)

def do_something():
    logger.info("Doing something")
myapp.log
2025-08-22 11:43:24,741 [INFO]  __main__: Started
[INFO]: Doing something
2025-08-22 11:43:24,741 [INFO]  __main__: Finished

ファイルサイズ・期間でログを分割するためのローテーティング

この章ではファイルのサイズや期間毎に分割されたログの出力や、一定期間・一定数以上のログの削除の方法について紹介します
使用するのはRotatingFileHandlerTimedRotatingFileHandlerの2つのHandlerです

RotatingFileHandler(サイズベースのローテーション)

特徴

  • ファイルのサイズが指定したサイズに達したタイミングで、新しいファイルに切り替える
  • 古いログファイルはxxx.1, xxx.2, ...のように保存される

以下のサンプルを実行するとapp.log, app.log.1, app.log.2, app.log.3の4つのログファイルが作成されます
バックアップの際に設定されるファイル名のフォーマットは多分変更できない?(例えばapp.log.1 --> 1_app.logのように)

また新しいファイルが作成されるタイミングではバックアップ用のファイルはリネームされ、backuupCountを超えたファイルは削除されます

rotating_sample.py
import logging
from logging.handlers import RotatingFileHandler

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

hander = RotatingFileHandler("app.log", maxBytes=1000, backupCount=3)
hander.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))

logger.addHandler(hander)

for i in range(100):
    logger.info("rotation sample")

TimedRotatingFileHandler(時間ベースのローテーション)

特徴

  • 時間の経過に応じてログファイルを切り替える
  • ファイル名に日付や時間が付加される

ログファイルを分割するインターバルの設定についてはこちらをご確認ください

halder.suffix = ...でファイル名の末尾につける日時のフォーマットを変更できるのですが、私の環境だとコメントアウトしてある形式以外でしたときはbackupCountを超えてもファイルの削除が実行されませんでしたので基本的に設定しなくても良いと思います

timed_rotating_sample.py
from logging.handlers import TimedRotatingFileHandler
import logging
import time

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

handler = TimedRotatingFileHandler(
    "app.log", when="S", interval=3, backupCount=2, encoding="utf-8"
)
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
handler.setFormatter(formatter)
# handler.suffix = "%Y-%m-%d_%H-%M-%S"
logger.addHandler(handler)

for i in range(10):
    logger.info("time rotation sample")
    time.sleep(1)

まとめ

  • Pythonでログの記録を行うための基本について紹介しました
  • FileHandler, (Timed)RotatingFileHandlerについて使い方を紹介しました
  • 今回紹介した内容ではログファイルの作成と削除を行えます
  • 今回紹介した内容の他にもコマンドライン上に出力したり、ネットワークソケットに送信したりなども行うことができますので、興味があればlogging.handlersで調べてみてください

ご不明な点や間違っている内容がありましたらアドバイスいただけますと幸いです

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?