FastAPI logging - APIRoute, Middleware

Y_Sevinยท2023๋…„ 6์›” 18์ผ
3

๐Ÿ˜… ํ•„์ž๊ฐ€ ๊ฐœ์ธ์ ์œผ๋กœ ๊ณต๋ถ€ํ•˜๊ณ  ๋‚จ๊ธฐ๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ณด๊ฐ€ ๋ฏธํกํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž˜๋ชป๋œ ๋‚ด์šฉ์ด ์žˆ์„๊ฒฝ์šฐ ์ง€์ ํ•ด์ฃผ์‹ ๋‹ค๋ฉด ๊ฐ์‚ฌํžˆ ๋ฐ›๊ฒ ์Šต๋‹ˆ๋‹ค.

FastAPI์—์„œ API Request์™€ Response๋ฅผ ๋กœ๊ทธ๋กœ ๋‚จ๊ฒจ์•ผ ํ–ˆ๋‹ค.
FastAPI๋กœ loggingํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋Œ€ํ‘œ์ ์œผ๋กœ 2๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•œ๋‹ค.

  • Middleware
  • APIRoute

์ด๋ฒˆ ๊ธ€์€ ์ด ๋‘˜์„ ์ด์šฉํ•ด log๋ฅผ ๋‚จ๊ธฐ๋Š” ๋ฐฉ๋ฒ•์„ ๊ณต์œ ํ•˜๊ณ ์ž ํ•œ๋‹ค.


1. Middleware

1. Middleware๋ž€?

Middleware๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ HTTP ์š”์ฒญ ๋ฐ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ • ์ค‘๊ฐ„์— ๊ฐœ์ž…ํ•˜์—ฌ ์ถ”๊ฐ€์ ์ธ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด ์ปดํฌ๋„ŒํŠธ๋‹ค. FastAPI๋Š” MiddlewareํŒจํ„ด์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค. APIํ˜ธ์ถœ ์ „๊ณผ ํ›„์— ํ•˜๋‚˜์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๊ณ  API๋กœ ์š”์ฒญ์„ ๋„˜๊ธด๋‹ค. ์ด๋•Œ ์ด ์ฝœ๋ฐฑํ•จ์ˆ˜๋Š” Request์™€ Response์˜ ์ฒ˜๋ฆฌ ๊ณผ์ •์— ์‚ฌ์šฉ์ž ์ •์˜ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.

2. FastAPI Middleware๋ฅผ ํ™œ์šฉํ•œ ๋กœ๊น…

FastAPI์—์„œ ์ œ๊ณตํ•˜๋Š” Middleware๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ API Request์™€ Response์˜ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

from fastapi import FastAPI, Response, Request
from starlette.background import BackgroundTask
from typing import Dict, Any
import logging

app = FastAPI()
# ๋กœ๊ทธ ํŒŒ์ผ์— ์ž‘์„ฑ (์ƒ๊ฐ๋ณด๋‹ค ๋กœ๊ทธ๋กœ ์„œ๋ฒ„๊ฐ€ ์ž์ฃผ ํ„ฐ์ง€๋”๋ผ๊ตฌ์š”..ใ…Ž)
logging.basicConfig(filename='info.log', level=logging.DEBUG)

def log_info(req_body, res_body):
    logging.info(req_body)
    logging.info(res_body)

@app.middleware('http')
async def some_middleware(request: Request, call_next):
    req_body = await request.body()
    response = await call_next(request)

    res_body = b''
    async for chunk in response.body_iterator:
        res_body += chunk

    task = BackgroundTask(log_info, req_body, res_body)
    return Response(content=res_body, status_code=response.status_code,
                    headers=dict(response.headers), media_type=response.media_type, background=task)


@app.post('/')
def main(payload: Dict[Any, Any]):
    return {"Hello": "World"}
    

3. ์ฝ”๋“œ ๐Ÿ’ป

  • some_middleware: Request์™€ Response์— ๋Œ€ํ•œ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๋ฏธ๋“ค์›จ์–ด ํด๋ž˜์Šค
  • logger: uvicorn ๋กœ๊ฑฐ๋ฅผ ์„ค์ •ํ•˜์—ฌ ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ INFO๋กœ ์ง€์ •
  • BackgroundTask: API ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ธฐ์œ„ํ•ด ์‘๋‹ต์„ ๋จผ์ € ๋ณด๋‚ด์ฃผ๊ณ  ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋‚˜๋จธ์ง€ ์ž‘์—…์„ ์ˆ˜ํ–‰

4. ๊ฒฐ๊ณผ

Middleware๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Request๊ฐ€ ์—”๋“œํฌ์ธํŠธ์— ๋„๋‹ฌํ•˜๊ธฐ ์ „์— ์ค‘๊ฐ„ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ณ  ์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋ฐ˜ํ™˜ํ•œ Response๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌ๋˜๊ธฐ ์ „์— ์ค‘๊ฐ„ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋ฉด์„œ Request์™€ Response์— ๋Œ€ํ•œ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.


APIRoute

1. APIRoute๋ž€?

APIRoute๋Š” FastAPI์—์„œ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋” ์„ธ๋ถ€์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํด๋ž˜์Šค์ด๋‹ค. ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”๋“œํฌ์ธํŠธ์˜ ๋™์ž‘์„ ๋”์šฑ ์œ ์—ฐํ•˜๊ฒŒ ์ •์˜ํ•˜๊ณ  ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ๋‹ค.

from fastapi import FastAPI, HTTPException, APIRouter, Request, Depends
from fastapi.responses import JSONResponse
import logging

app = FastAPI()
router = APIRouter()

# ๋กœ๊น…์„ ์œ„ํ•œ ํ•จ์ˆ˜ ์ •์˜
def custom_logger(request: Request, response: JSONResponse, logger: logging.Logger = Depends()):
    logger.info(f"Request: {request.method} {request.url}")
    logger.info(f"Response: {response.status_code}")
    return response

# APIRoute๋ฅผ ์‚ฌ์šฉํ•œ ์—”๋“œํฌ์ธํŠธ ์ •์˜
@router.get("/", response_class=JSONResponse, dependencies=[Depends(custom_logger)])
async def read_root():
    return {"Hello": "World"}

# FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— APIRouter ๋“ฑ๋ก
app.include_router(router, prefix="/api")

# ๋กœ๊ฑฐ ์„ค์ •
logger = logging.getLogger("uvicorn")
logger.setLevel(logging.INFO)

3. ์ฝ”๋“œ

  • custom_logger: Request์™€ Response์— ๋Œ€ํ•œ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ํ•จ์ˆ˜
  • response_class: ์—”๋“œํฌ์ธํŠธ์˜ ์‘๋‹ต ํด๋ž˜์Šค๋ฅผ ์ง€์ •

4. ๊ฒฐ๊ณผ

์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋ฉด์„œ APIRoute๋ฅผ ์‚ฌ์šฉํ•œ ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ Request์™€ Response์— ๋Œ€ํ•œ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

ํŠนํžˆ APIRoute๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ ๋กœ๊น…์„ ๋”์šฑ ์„ธ๋ฐ€ํ•˜๊ฒŒ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๊ณ , ๊ฐ ์—”๋“œํฌ์ธํŠธ๋งˆ๋‹ค ๋‹ค๋ฅธ ๋กœ๊น… ๋กœ์ง์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

profile
๋งค์ผ์€ ์•„๋‹ˆ๋”๋ผ๋„ ๊พธ์ค€ํžˆ ์˜ฌ๋ฆฌ์ž๋Š” ๋งˆ์Œ์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฐœ๋ฐœ๋ธ”๋กœ๊ทธ๐Ÿ˜Ž

2๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2024๋…„ 4์›” 1์ผ

์•ˆ๋…•ํ•˜์„ธ์š”! ๋‘ ๋ฐฉ๋ฒ•์„ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ fastapi์—์„œ ์—†๋‚˜์š”?

1๊ฐœ์˜ ๋‹ต๊ธ€