概要(loggingは「あとから原因を特定できる記録」を残すための標準手法)
printは一瞬の確認、loggingは運用と原因追跡のための記録です。ログレベルで重要度を切り替え、フォーマットで「いつ・どこで・何が起きたか」を残し、出力先(コンソールやファイル)を柔軟に選べます。まずはbasicConfigで最短設定→レベルの意味→フォーマット→ファイル出力→例外の記録→ローテーションの順に身につけると、初心者でも実務品質のログ設計ができます。
基本の使い方(ここが重要)
最短の設定と出力
import logging
logging.basicConfig(
level=logging.INFO, # 出す最低レベル
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s"
)
logging.debug("デバッグ用の詳細") # INFOなので表示されない
logging.info("アプリ起動")
logging.warning("設定が見つかりません")
logging.error("読み込みに失敗しました")
logging.critical("致命的なエラー")
Python- レベル: DEBUG/INFO/WARNING/ERROR/CRITICAL。levelで「それ以上の重要度のみ」出ます。
- フォーマット: 日付、レベル、ロガー名、本文などを自動付与できます。
モジュールごとのロガー(推奨パターン)
import logging
logger = logging.getLogger(__name__) # ファイル単位でロガーを持つ
def run():
logger.info("処理開始")
logger.debug({"step": 1, "status": "ok"})
Python- ロガー名: nameで「どのモジュールのログか」識別しやすく。
- 粒度: 共通設定は一度、各モジュールではgetLoggerで使うのが自然。
ログレベルと設計(何をどのレベルで書くか)
レベルを使い分ける指針
- DEBUG: 変数の中身、分岐結果、実験的な詳細。開発時のみ出す前提。
- INFO: 処理の開始・終了、成功メッセージ、重要イベント。運用でも常に出す。
- WARNING: 想定外だが継続可能な事象(非推奨API使用、再試行で回復)。
- ERROR: 失敗(機能が完了できない)。例外と合わせて原因特定に必要な文脈も残す。
- CRITICAL: システム停止級(設定欠落、DB接続不能など)。
実務で効く例
import logging
logger = logging.getLogger(__name__)
def load_config(path):
logger.info("設定読み込み: %s", path)
try:
# 読み込み…
return {"ok": True}
except FileNotFoundError:
logger.error("設定ファイルがありません: %s", path)
raise
Python- ラベル: 何を試み、何が失敗したかを簡潔に。
- 構造: 値を埋め込むと再現・再現条件が取りやすくなる。
フォーマットと出力先(見やすく、残す)
フォーマットで文脈を付ける
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(module)s:%(lineno)d %(message)s"
)
Python- 日時: asctimeで時刻。
- 位置: moduleとlinenoで「どこから出たか」。
- トレース: 例外はexc_info=Trueでスタック含めて記録。
ファイルへ出力(basicConfigで簡易)
import logging
logging.basicConfig(
level=logging.INFO,
filename="app.log",
filemode="a", # 追記
format="%(asctime)s [%(levelname)s] %(name)s %(message)s"
)
logging.info("ファイルへ記録開始")
Python- filemode: a(追記)/ w(上書き)。
- 権限: 書き込み可能なパスを選び、ローテーションや容量対策を考える。
コンソールとファイルを同時に(ハンドラ設計)
import logging
logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)
# コンソール
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
# ファイル
fh = logging.FileHandler("app.log", encoding="utf-8")
fh.setLevel(logging.DEBUG)
fh.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s:%(lineno)d %(message)s"))
logger.addHandler(ch)
logger.addHandler(fh)
logger.info("画面にもファイルにも出る")
logger.debug("ファイルだけに出る詳細")
Python- 複数ハンドラ: 出力先ごとにレベルやフォーマットを変えられます。
- 重複対策: basicConfigと併用時の重複を避ける(ハンドラを一意にする)。
例外・トレース・再試行(原因を最短で掴む)
例外のスタックトレースを記録
import logging
logger = logging.getLogger(__name__)
try:
1 / 0
except Exception:
logger.exception("計算に失敗") # = logger.error(..., exc_info=True)
Python- logger.exception: 失敗の文脈+スタックを一行で。原因追跡の最重要パターン。
リトライ時のログ(過度なノイズを抑える)
import logging, time
logger = logging.getLogger(__name__)
def fetch_with_retry(tries=3, base=0.5):
for i in range(tries):
try:
# 通信…
logger.info("取得成功")
return True
except Exception as e:
wait = base * (2 ** i)
logger.warning("失敗(%s)。%ss後に再試行 [%d/%d]", e, wait, i+1, tries)
time.sleep(wait)
logger.error("再試行尽きた。中断")
return False
Python- 段階: 成功/失敗/再試行/断念の節目をINFO/WARNING/ERRORで明確化。
- 量: 失敗が連続する場合、メッセージを簡潔に。
ログローテーション・設定外出し(運用の型)
ログローテーションで肥大化対策
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
fh = RotatingFileHandler("app.log", maxBytes=5_000_000, backupCount=3, encoding="utf-8")
fh.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
logger.addHandler(fh)
logger.info("ローテーション開始")
Python- maxBytes: サイズ上限、超えたら切替。
- backupCount: 世代数(古いものを削除)。
時刻ベースのローテーション(日次など)
from logging.handlers import TimedRotatingFileHandler
import logging
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
fh = TimedRotatingFileHandler("app.log", when="midnight", interval=1, backupCount=7, encoding="utf-8")
fh.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
logger.addHandler(fh)
logger.info("日次ローテーション開始")
Python- when: “S”, “M”, “H”, “D”, “midnight”など。
- backupCount: 何世代残すか。
設定ファイルで集中管理(規模が大きくなったら)
# logging.conf(例)
[loggers]
keys=root,app
[handlers]
keys=console,file
[formatters]
keys=simple,detail
[logger_app]
level=DEBUG
handlers=console,file
qualname=app
propagate=0
[handler_console]
class=StreamHandler
level=INFO
formatter=simple
args=(sys.stdout,)
[handler_file]
class=FileHandler
level=DEBUG
formatter=detail
args=("app.log","a","utf-8")
[formatter_simple]
format=%(asctime)s [%(levelname)s] %(message)s
[formatter_detail]
format=%(asctime)s [%(levelname)s] %(name)s:%(lineno)d %(message)s
Pythonimport logging, logging.config
import sys
logging.config.fileConfig("logging.conf")
logger = logging.getLogger("app")
logger.info("設定ファイルで制御")
Python- 利点: コードから設定を分離し、環境ごとに差し替えやすくなる。
- 代替: dictConfigでPython辞書から設定も可能。
例題で身につける(定番から実務まで)
例題1:APIクライアントの最小ログ
import logging, requests
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger("client")
def fetch(url):
logger.info("GET: %s", url)
try:
r = requests.get(url, timeout=5)
r.raise_for_status()
logger.info("OK: %s (%d)", url, r.status_code)
return r.json()
except requests.HTTPError:
logger.exception("HTTPエラー: %s", url)
return None
fetch("https://httpbin.org/json")
Python例題2:コンソールINFO+ファイルDEBUGの二重出力
import logging
logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
fh = logging.FileHandler("debug.log", encoding="utf-8")
fh.setLevel(logging.DEBUG)
fh.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s:%(lineno)d %(message)s"))
logger.addHandler(ch); logger.addHandler(fh)
logger.info("画面向けの要約")
logger.debug({"detail": "ファイル向けの詳細"})
Python例題3:ローテーション+例外トレース
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
fh = RotatingFileHandler("app.log", maxBytes=1_000_000, backupCount=5, encoding="utf-8")
fh.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s:%(lineno)d %(message)s"))
logger.addHandler(fh)
try:
raise RuntimeError("テスト例外")
except Exception:
logger.exception("例外発生")
Python例題4:dictConfigで一括設定(JSONやYAMLにも馴染む)
import logging, logging.config
cfg = {
"version": 1,
"formatters": {
"std": {"format": "%(asctime)s [%(levelname)s] %(name)s:%(lineno)d %(message)s"}
},
"handlers": {
"console": {"class": "logging.StreamHandler", "level": "INFO", "formatter": "std"},
"file": {"class": "logging.FileHandler", "level": "DEBUG", "formatter": "std", "filename": "app.log", "encoding": "utf-8"}
},
"loggers": {
"app": {"level": "DEBUG", "handlers": ["console", "file"], "propagate": False}
}
}
logging.config.dictConfig(cfg)
logger = logging.getLogger("app")
logger.info("起動")
logger.debug("詳細設定が反映されています")
Pythonまとめ
loggingは「いつ・どこで・何が起きたか」を継続的に残し、原因追跡と運用を楽にします。レベルを設計して情報量を制御し、フォーマットで文脈を付与し、出力先を使い分ける。例外はlogger.exceptionでスタック込み、ファイルはローテーションで肥大化を防ぐ。設定はコード内のbasicConfigから始め、ハンドラ・dictConfig・設定ファイルへ広げる。これらを型として身につけると、初心者でも短いコードで「見える・追える・崩れない」ログが手に入ります。
