L14 ch03 — ログミドルウェア
テクノロジ系 / L14 middleware — ミドルウェア
L14 ch03 — ログミドルウェア
リクエスト/レスポンスを記録して「動いた・落ちた」を可視化する
ゴール
loggingモジュールの最小設定ができる- リクエスト毎にログを出すミドルウェアを書ける
- ステータスコード・処理時間を記録できる
なぜログが必要か
print で済むのは開発初期だけ:
- 時刻 が付かない
- 重要度 (INFO / WARNING / ERROR) を区別できない
- 本番では ファイル or 集約基盤 に流したい
→ Python 標準の logging モジュールを使う
logging 最小例
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
logger = logging.getLogger(__name__)
logger.info("server started")
# → 2026-06-24 10:00:00,123 INFO server startedログレベル
| レベル | 使いどころ |
|---|---|
DEBUG | 開発時の詳細トレース |
INFO | 通常の動作ログ (リクエスト処理など) |
WARNING | 想定内だが注意すべき事象 |
ERROR | 例外・処理失敗 |
CRITICAL | サービス停止級 |
basicConfig(level=...) で出力閾値を変えられる。
FastAPI のミドルウェアとは
リクエスト処理の 前後 に共通処理を挟む仕組み。
[ Request ]
↓
[ Middleware (前処理) ]
↓
[ Route handler ]
↓
[ Middleware (後処理) ]
↓
[ Response ]ログ・認証・CORS・計測などで使う。
最小のログミドルウェア
import time
import logging
from fastapi import FastAPI, Request
logging.basicConfig(level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
app = FastAPI()
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = time.time()
response = await call_next(request)
elapsed = (time.time() - start) * 1000
logger.info(
f"{request.method} {request.url.path} "
f"-> {response.status_code} ({elapsed:.1f}ms)"
)
return response動作イメージ
uv run uvicorn main:app --reload
別ターミナルで:
curl http://localhost:8000/hello
サーバー側ログ:
2026-06-24 10:00:00,123 INFO GET /hello -> 200 (1.2ms)
ポイント解説
@app.middleware("http")で HTTP リクエスト全体をフックawait call_next(request)で本来のルートを実行- その前後で
time.time()を測って差分 = 処理時間 request.method/request.url.path/response.status_codeが取れる
エラー時のログ
例外発生時もログに残したい:
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = time.time()
try:
response = await call_next(request)
except Exception as e:
logger.exception(f"{request.method} {request.url.path} failed")
raise
elapsed = (time.time() - start) * 1000
logger.info(f"... -> {response.status_code} ({elapsed:.1f}ms)")
return responselogger.exception はスタックトレースも自動で記録。
print vs logger
| 比較項目 | print | logger |
|---|---|---|
| 時刻 | なし | あり (format で制御) |
| レベル | なし | DEBUG/INFO/WARN/ERROR |
| 出力先切替 | stdout 固定 | ファイル・syslog 等 |
| 本番運用 | NG | OK |
→ サーバーコードでは 必ず logger を使う
drill
drill/ — ログミドルウェアの空欄を埋める
拡張課題
- ログをファイルに書く:
logging.FileHandler("app.log")を追加 - JSON ログ:
python-json-loggerで構造化ログ - request ID:
uuid.uuid4()を生成してログに含め、リクエスト追跡可能に
よくある詰まり
| 症状 | 原因 | 対処 |
|---|---|---|
| ログが出ない | basicConfig を呼んでいない | logging.basicConfig(level=logging.INFO) を追加 |
await を忘れる | call_next は async | response = await call_next(request) |
| 処理時間が 0 ms | time.time() の差分を取り忘れ | start と end を両方記録 |
次に学ぶ領域
| 領域 | 続けて読む |
|---|---|
| 例外ハンドリング | exception/ |
| テスト | test/ |
| 本番デプロイ | deploy/ |
力試ししたい方は クイズ形式の演習(任意) もあります。読むだけでも十分です。