
로그는 언제나 중요하다. 로컬에서 작업할 때는 몰라도, 배포 환경에서 벌어진 문제에 대응하기 위해서는 로그를 남겨두는 것이 무조건 필요하다.
이제 로그파일을 남겨두기 위해 코드를 작성해보자
우선은 가장 간단한 형태로 작성했다.
import logging
import os
from datetime import date
today = date.today()
LOG_FILE_PATH = f"logs/{today}.log"
os.makedirs(os.path.dirname(LOG_FILE_PATH), exist_ok=True)
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(LOG_FILE_PATH, mode="a", encoding="utf-8"),
logging.StreamHandler(),
],
)
logger = logging.getLogger("app_logger")
logger.info("Logging setup complete.")
위와 같이 작성해줬고, 아래와 같이 로그가 쌓인다.

2025-06-20 15:52:25,939 - app_logger - INFO - AUTH BY: USER-01JPVYFQ8WMZ2H20X04K18W8NH
하지만 이렇게만 했을 경우에는 몇가지 문제가 있다.
우선 1번부터 해결해보자.
from fastapi import FastAPI, Response, Request
from starlette.background import BackgroundTask
from starlette.types import Message
from app.utils.log_util import logger
app = FastAPI()
# ...
# 추가 초기 설정
# ...
def end_log_info(res_body: bytes):
logger.info(f"RESPONSE: {res_body.decode(errors='ignore')}")
def entry_log_info(endpoint: str):
logger.info(f"ENDPOINT: {endpoint}")
async def set_body(request: Request, body: bytes):
async def receive() -> Message:
return {"type": "http.request", "body": body}
request._receive = receive
@app.middleware("http")
async def logging_middleware(request: Request, call_next):
req_body = await request.body()
await set_body(request, req_body)
endpoint = f"{request.method} {request.url.path}"
entry_log_info(endpoint)
response = await call_next(request)
res_body = b""
async for chunk in response.body_iterator:
res_body += chunk
task = BackgroundTask(end_log_info, res_body)
return Response(
content=res_body,
status_code=response.status_code,
headers=dict(response.headers),
media_type=response.media_type,
background=task,
)
@app.middleware("http")HTTP Request와 Response를 처리할 때 이 미들웨어 함수를 중간에 끼워 넣겠다는 의미
- 클라이언트가 HTTP Request를 보냄
- FastAPI에서는
@app.middleware("http")에 등록된 메소드를 우선 실행- 이 함수에서
call_next(request)를 호출해야 실제 라우터 핸들러로 넘어감- 응답이 돌아오면 다시 이 미들웨어에서 응답을 가공하거나 처리할 수 있음
그래서 이 미들웨어 메소드를 가지고 HTTP Request가 들어왔을 때 로깅 처리를 해주려고 한다.
이중에서 또 볼 부분은 BackgroundTask이다.
BackgroundTask
- Response이후에 실행할 작업을 정의하는 유틸리티
- 라우터 함수가 끝나더라도 비동기적으로 백그라운드에서 실행됨
- 동시성(성능)과 응답속도 향상에 도움이 됨
- 따라서 로깅으로 인해 Response time에 영향을 주지 않음
혹시모를 로깅으로 인한 지연을 대비해 BackgroundTask를 적용해보았다.
import logging
import os
from datetime import date
from logging.handlers import TimedRotatingFileHandler
LOG_DIR = "logs"
os.makedirs(LOG_DIR, exist_ok=True)
logger = logging.getLogger("app_logger")
logger.setLevel(logging.DEBUG)
# Setting formatter for log messages
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# File Handler
file_handler = TimedRotatingFileHandler(
filename=os.path.join(LOG_DIR, f"{date.today()}.log"),
when="midnight",
interval=1,
encoding="utf-8",
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# Console Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(formatter)
# Reset handlers to avoid duplicate logs
if not logger.hasHandlers():
logger.addHandler(file_handler)
logger.addHandler(console_handler)
logger.debug("This is a DEBUG message.")
logger.info("Logging setup complete.")
위와 같이 TimeRotatingFileHandler를 이용하여 일별로 로그 관리를 하려고 작성했다.
이렇게 해두면 배포했을 때, 일별로 로그파일이 생성되며 관리하기 용이해진다.