Skip to content

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)

ロギング設定のポイント

  1. ログレベルの階層管理

    • Uvicorn標準ログ: uvicorn, uvicorn.access, uvicorn.error
    • アプリケーションログ: 別途appロガーを作成
  2. 本番環境考慮事項

    • ファイルローテーション: RotatingFileHandlerでディスク容量管理
    python
    'file': {
        'class': 'logging.handlers.RotatingFileHandler',
        'filename': 'app.log',
        'maxBytes': 10 * 1024 * 1024,  # 10MB
        'backupCount': 5
    }
    • JSONフォーマット: ログ管理システムとの連携に最適
  3. ライフサイクル管理 起動・終了時のログを確実に取得:

    python
    @app.on_event("startup")
    async def startup_event():
        logger.info("アプリケーション起動開始")
    
    @app.on_event("shutdown")
    async def shutdown_event():
        logger.info("アプリケーション終了")

トラブルシューティング

  • ログが表示されない場合

    1. Uvicornのログレベル確認(--log-level debug
    2. ロガー名の一致確認(uvicorn.errorが推奨)
    3. ログ伝搬設定(propagate=Falseになっていないか)
  • プロダクション環境の注意点

    • Gunicorn使用時: workerプロセスごとにロガー初期化が必要
    • Docker環境: ログをstdoutに出力するよう設定

パフォーマンス最適化

大量ログ出力時のボトルネック回避策:

python
# デバッグログは条件付きで実行
if logger.isEnabledFor(logging.DEBUG):
    logger.debug(f"重い処理: {expensive_debug_info()}")

結論

最適なロギング戦略は環境によって異なります:

環境推奨方法利点
開発環境uvicorn.errorロガー簡単設定・即時確認可能
本番環境カスタムロガー+JSONフォーマット解析容易・長期保存向け
コンテナ環境stdout出力+ログドライバークラウドサービス連携容易

重要: 本番環境では必ずログレベルをINFO以上に設定し、パフォーマンス監視を行いましょう。