Pythonのprintをやめてloggingでログをファイルに残す方法

公開日:
目次

pythonでデバッグする時にひとまず print() という選択肢が多いですが、定期実行するスクリプトなどのあとで確認したり見返したりしたい場合は結構不便です。

logging を使うと、print() とそこまで変わらない手間でログをファイルとして残せるようになります。

printのままだと何が困るのか

print() は標準出力に文字を吐くだけなので、画面を見ていないと情報が残りません。ターミナルを閉じたら消えますし、いつ出力されたかも分かりません。あと地味に効いてくるのが、本番で「デバッグ出力だけ黙らせたい」となったとき、print() だと1行ずつ消すかコメントアウトするしかない点です。

logging を使うと、この辺りがまとめて解決します。出力先をファイルにできますし、各行に時刻と重要度が付きます。さらに「INFO 以上だけ出す」のようにレベルでまとめて絞れます。

まずは最小構成でファイルに残す

一番手軽なのが logging.basicConfig()filename を渡す方法です。これだけで、以降のログ出力がそのファイルに追記されます。

app.py
import logging

logging.basicConfig(
    filename="app.log",
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
)

logging.info("処理を開始しました")
logging.warning("設定ファイルが見つからないのでデフォルトを使います")
logging.error("DBに接続できませんでした")

実行すると app.log にこう書かれます。

app.log
2026-06-01 09:12:03,481 INFO 処理を開始しました
2026-06-01 09:12:03,482 WARNING 設定ファイルが見つからないのでデフォルトを使います
2026-06-01 09:12:03,482 ERROR DBに接続できませんでした

print("処理を開始しました") と比べると、いつ・どの深刻度で起きたかが1行に乗っています。これがファイルに残るので、あとで見返すときに便利です。さらに、level=logging.INFO を指定しているので、DEBUG レベルのログは出ません。開発中は DEBUG も出しておいて、本番では INFO 以上だけにする、といった切り替えも簡単にできます。

なお basicConfig() はログ出力の前に1回だけ呼びます。logging.info() などを先に呼ぶと、その時点で勝手にデフォルト設定(出力先は sys.stderr、レベルは WARNING)で初期化されてしまい、あとから basicConfig() を呼んでも効きません。設定はスクリプトの頭で済ませておくのが無難です。

ファイルは上書き?追記?

basicConfig()filename 指定はデフォルトで追記(filemode="a")になります。実行のたびに前回のログが消えると困る場面が多いので、この挙動はありがたいです。逆に毎回まっさらにしたいなら filemode="w" を渡します。

レベルを使い分けると本番で便利

logging には深刻度のレベルがあります。下から DEBUG < INFO < WARNING < ERROR < CRITICAL の5段階です。

basicConfig(level=...) で指定したレベル以上だけが出力されます。たとえば level=logging.INFO にすると DEBUG は出ず、INFO 以上が残ります。開発中は DEBUG まで全部出して、本番では WARNING 以上だけにする、といった切り替えが1行でできます。print() を1個ずつ消して回る作業から解放されるのはこれのおかげです。

ちなみに何も指定しないと、デフォルトのレベルは WARNING になっています。logging.info(...) を書いたのに何も出ない、というハマりどころはたいていこれで、levelINFODEBUG に下げれば出るようになります[1]

ファイルとターミナル、両方に出したい

開発中は手元のターミナルでも見つつ、記録はファイルにも残したい、というのはよくあります。basicConfig() 一発だと出力先は基本1つなので、こういうときは handler を2つ足します。

app.py
import logging

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

fmt = logging.Formatter("%(asctime)s %(levelname)s %(message)s")

file_handler = logging.FileHandler("app.log", encoding="utf-8")
file_handler.setFormatter(fmt)

stream_handler = logging.StreamHandler()
stream_handler.setFormatter(fmt)

logger.addHandler(file_handler)
logger.addHandler(stream_handler)

logger.info("ファイルにもターミナルにも出ます")

FileHandler がファイル行き、StreamHandler がターミナル行きです。日本語をログに書くなら FileHandlerencoding="utf-8" を付けておくと文字化けを避けられます。

どれを使えばいいか

  • スクリプト1本でとりあえずログをファイルに残したい → basicConfig(filename=...)
  • 手元でも見つつファイルにも残したい → FileHandler + StreamHandler
  • 長期間動かしてログが膨らむのが心配 → logging.handlers.RotatingFileHandler(maxBytesbackupCount でサイズ上限と世代数を決めると、自動でローテーションしてくれる)

最後のローテーションは常駐サービス向けの話なので、デバッグ目的で書き捨てるスクリプトなら basicConfig で十分だと思います。自分も普段は basicConfig で済ませていて、ファイルが育ってきたなと感じたら RotatingFileHandler に切り替えています。print() をやめて困ったことは今のところありません。

脚注
  1. Logging HOWTO — Python 公式ドキュメント ↩︎

  2. logging — Logging facility for Python(basicConfig の encoding は 3.9 で追加) ↩︎