Fastapiを使うときに、どういうログが必要なのか
結論はアプリログ、強いていうならエラーログも分けて出しておくと見やすいかもしれない。
ただ、個人的にはアプリログは、どっちも出した方がいいんじゃないかとは思っています。
変に切り分けるとどの正常処理の後に落ちた箇所なのかの追い方が少し面倒な気がしているので。
今回はアプリログ一つに出します。
Fastapi自体に届くアクセスログ自体はアプリ側では管理しなくていいのかなというところなので。
種類
アクセスログ
INFO: 127.0.0.1:53818 - "GET /boom HTTP/1.1" 500 Internal Server Error
uvicornが出している誰が何を叩いたかの記録
middlewareのlogger.*とは別
これは基本的にFastAPIでは今回は意図的には出さない。
一応練習としてアプリのmiddleware側でログを出すのである種アクセスログに近いものは出す。
アプリログ
API Error: request_id=... path=/boom Boom!
Traceback ...
アプリコードが出している
なぜそうなったのかの記録
こっちはアプリケーションに関してのログなのでアプリ側で管理して出していきたい。
方法
appディレクトリの配下に諸々ある想定
ロガーの設定
ロガーをcore配下で設定しmain.pyで読み込む。
そうすると各々のモジュールで共通で設定は流用できる。
今季は吐き出し先はlogs/app.log
core/logging.py
import logging
from logging import Logger
from pathlib import Path
LOG_DIR = Path("logs")
LOG_FILE = LOG_DIR / "app.log"
def setup_logger() -> Logger:
LOG_DIR.mkdir(exist_ok=True)
formatter = logging.Formatter(
"%(asctime)s %(levelname)s %(name)s %(message)s"
)
# ターミナルへ出力するハンドラ
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
# ファイルへ出力するハンドラ
file_handler = logging.FileHandler(LOG_FILE)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
root_logger: Logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
if not root_logger.hasHandlers():
root_logger.addHandler(console_handler)
root_logger.addHandler(file_handler)
return root_logger
関連ファイル
main.py
import logging
from fastapi import FastAPI
from pydantic import BaseModel
from app.middlewares.application_middleware import ApplicationMiddleware
from app.core.logging import setup_logger
setup_logger()
app = FastAPI()
app.add_middleware(ApplicationMiddleware)
class UserIn(BaseModel):
age: int
@app.get("/health")
async def health():
return {"ok": True}
@app.get("/boom")
async def boom():
raise Exception("Boom!")
@app.post("/users/")
async def create_user(user: UserIn):
return user
middleware/authention_middleware.py
import uuid
import traceback
from starlette.middleware.base import BaseHTTPMiddleware
from logging import getLogger
from fastapi import Request, status
from fastapi import HTTPException
from fastapi.responses import JSONResponse
logger = getLogger(__name__)
REQUEST_ID_HEADER = "X-Request-ID"
class ApplicationMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
request_id = request.headers.get(
REQUEST_ID_HEADER) or str(uuid.uuid4())
try:
response = await call_next(request)
response.headers[REQUEST_ID_HEADER] = request_id
logger.info(
"API request_id=%s method=%s path=%s status=%s",
request_id,
request.method,
request.url.path,
response.status_code,
)
return response
except Exception as e:
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
detail = "Internal Server Error"
headers = {"www-Authenticate": "Internal Server Error"}
if isinstance(e, HTTPException):
status_code = e.status_code
detail = e.detail
headers = e.headers
headers = dict(headers)
headers[REQUEST_ID_HEADER] = request_id
logger.exception(
"API Error: request_id=%s path=%s %s\n%s",
request_id,
request.url.path,
e,
traceback.format_exc(),
)
return JSONResponse(
status_code=status_code,
content={"detail": detail},
headers=headers,
)
## 結果
エンドポイントを叩いてみるとこんな感じになります。
2026-01-24 16:53:05,596 ERROR app.middlewares.application_middleware API Error: request_id=2e054e8d-df12-40da-b1fc-618ab165aaf7 path=/boom Boom!
Traceback (most recent call last):
File "/hogehoge/app/middlewares/application_middleware.py", line 20, in dispatch
response = await call_next(request)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next
raise app_exc from app_exc.__cause__ or app_exc.__context__
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro
await self.app(scope, receive_or_disconnect, send_no_error)
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
raise exc
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "/hogehoge/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
await self.app(scope, receive, send)
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__
await self.middleware_stack(scope, receive, send)
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app
await route.handle(scope, receive, send)
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle
await self.app(scope, receive, send)
File "/hogehoge/venv/lib/python3.12/site-packages/fastapi/routing.py", line 115, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
raise exc
File "/hogehoge/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "/hogehoge/venv/lib/python3.12/site-packages/fastapi/routing.py", line 101, in app
response = await f(request)
^^^^^^^^^^^^^^^^
File "/hogehoge/venv/lib/python3.12/site-packages/fastapi/routing.py", line 355, in app
raw_response = await run_endpoint_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/hogehoge/venv/lib/python3.12/site-packages/fastapi/routing.py", line 243, in run_endpoint_function
return await dependant.call(**values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/hogehoge/app/main.py", line 24, in boom
raise Exception("Boom!")
Exception: Boom!
2026-01-24 17:09:46,552 INFO app.middlewares.application_middleware API request_id=799f27c9-fbfd-472d-a8a6-9767f60a333d method=GET path=/health status=200