FastAPIとUvicornのロギング統合
FastAPIアプリケーションでカスタムログメッセージ(起動時のログやエンドポイント内のログ)がUvicornで表示されない問題が発生します。これはUvicornが独自のロギングシステムを使用しており、標準のPythonロギングが自動的に統合されないためです。
基本解決策:Uvicornロガーの活用
Uvicornが提供するロガーを直接使用するのが最もシンプルな方法です。uvicorn.error
ロガーを使用すると、Uvicornのロギングシステムと統合されます。
python
from fastapi import FastAPI
import uvicorn
import logging
app = FastAPI(title="api")
logger = logging.getLogger("uvicorn.error") # Uvicornロガーを取得
@app.get("/")
async def main():
logger.info("GET / にアクセスされました")
return {"status": "OK"}
実行時にログレベルを指定:
bash
uvicorn main:app --reload --log-level debug
特徴
logger.debug()
,logger.info()
,logger.error()
でUvicornのログに統合--log-level
で全ログのレベル制御可能- Uvicornのアクセスログと共存可能(
--no-access-log
で無効化可)
プログラムからの高度な設定
UvicornをPythonコードから起動する場合の設定例:
python
if __name__ == "__main__":
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
log_level="debug", # ログレベル設定
access_log=True # アクセスログの有効化
)
カスタムロガーの作成(本番環境向け)
本番環境ではログのフォーマットや出力先を柔軟に制御する必要があります。ロギング設定辞書を使った拡張例:
python
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
'json': {
'()': 'utils.CustomJSONFormatter' # カスタムJSONフォーマッタ
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'standard',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'app.log',
'maxBytes': 10485760, # 10MB
'backupCount': 3,
'formatter': 'json'
}
},
'loggers': {
'uvicorn': {'handlers': ['console'], 'level': 'INFO'},
'uvicorn.error': {'handlers': ['file'], 'level': 'DEBUG'},
'app': {'handlers': ['console', 'file'], 'level': 'DEBUG'}
}
}
python
import logging
import json
class CustomJSONFormatter(logging.Formatter):
"""JSON形式でログを出力するカスタムフォーマッタ"""
def format(self, record):
log_data = {
"time": self.formatTime(record),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
"module": record.module
}
return json.dumps(log_data)
python
from fastapi import FastAPI
import uvicorn
import logging
from config import LOGGING_CONFIG
app = FastAPI()
logger = logging.getLogger("app") # カスタムロガー
@app.get("/")
async def main():
logger.info("ルートエンドポイントアクセス")
return {"message": "OK"}
if __name__ == "__main__":
uvicorn.run(app, log_config=LOGGING_CONFIG)
ロギング設定のポイント
ログレベルの階層管理
- Uvicorn標準ログ:
uvicorn
,uvicorn.access
,uvicorn.error
- アプリケーションログ: 別途
app
ロガーを作成
- Uvicorn標準ログ:
本番環境考慮事項
- ファイルローテーション:
RotatingFileHandler
でディスク容量管理
python'file': { 'class': 'logging.handlers.RotatingFileHandler', 'filename': 'app.log', 'maxBytes': 10 * 1024 * 1024, # 10MB 'backupCount': 5 }
- JSONフォーマット: ログ管理システムとの連携に最適
- ファイルローテーション:
ライフサイクル管理 起動・終了時のログを確実に取得:
python@app.on_event("startup") async def startup_event(): logger.info("アプリケーション起動開始") @app.on_event("shutdown") async def shutdown_event(): logger.info("アプリケーション終了")
トラブルシューティング
ログが表示されない場合
- Uvicornのログレベル確認(
--log-level debug
) - ロガー名の一致確認(
uvicorn.error
が推奨) - ログ伝搬設定(
propagate=False
になっていないか)
- Uvicornのログレベル確認(
プロダクション環境の注意点
- Gunicorn使用時: workerプロセスごとにロガー初期化が必要
- Docker環境: ログをstdoutに出力するよう設定
パフォーマンス最適化
大量ログ出力時のボトルネック回避策:
python
# デバッグログは条件付きで実行
if logger.isEnabledFor(logging.DEBUG):
logger.debug(f"重い処理: {expensive_debug_info()}")
結論
最適なロギング戦略は環境によって異なります:
環境 | 推奨方法 | 利点 |
---|---|---|
開発環境 | uvicorn.error ロガー | 簡単設定・即時確認可能 |
本番環境 | カスタムロガー+JSONフォーマット | 解析容易・長期保存向け |
コンテナ環境 | stdout出力+ログドライバー | クラウドサービス連携容易 |
重要: 本番環境では必ずログレベルをINFO
以上に設定し、パフォーマンス監視を行いましょう。