From 259c92b749959a63d12c95ad14d2e82b47c4e09e Mon Sep 17 00:00:00 2001 From: 0206pdh <0206pdh@naver.com> Date: Fri, 3 Jul 2026 14:07:42 +0900 Subject: [PATCH] fix(logging): emit explicit auth log line so user_email actually reaches stdout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로덕션 파드 로그를 직접 확인해보니 2시간 동안 JSON 포맷 로그가 0줄이었다. uvicorn CLI가 "uvicorn"/"uvicorn.access"/"uvicorn.error" 로거를 자체 핸들러+plain-text 포매터로 구성하면서 propagate=False로 설정하기 때문에, 모든 요청 로그(access log)가 OtelJsonFormatter가 붙은 root logger를 완전히 우회하고 있었다. 게다가 앱 코드 어디에도 요청 단위로 명시적인 logger.info() 호출이 없어서, span attribute(user.email)는 정상 기록되고 있었음에도(#95 이후) 그걸 실제 로그 줄로 내보내는 지점 자체가 없었다. get_current_user()에서 span attribute를 세팅한 직후(=span이 아직 살아있는 시점) 명시적으로 로그 한 줄을 남기도록 추가. 로컬에서 OtelJsonFormatter를 직접 실행해 user_email 필드가 정확히 찍히는 것까지 확인함. --- app/api/dependencies.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/api/dependencies.py b/app/api/dependencies.py index 434401a..501eeeb 100644 --- a/app/api/dependencies.py +++ b/app/api/dependencies.py @@ -1,5 +1,7 @@ """Reusable API dependencies shared across routers.""" +import logging + from fastapi import Depends, Header, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError @@ -13,6 +15,8 @@ from app.repositories.user import UserRepository from app.schemas.auth import TokenPayload, UserRead +logger = logging.getLogger(__name__) + # The token URL points to our JSON login endpoint so the OpenAPI docs still # describe where a client obtains bearer tokens. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") @@ -51,6 +55,11 @@ def get_current_user( span = trace.get_current_span() span.set_attribute("user.id", str(resolved.id)) span.set_attribute("user.email", resolved.email) + # uvicorn's own access logger bypasses OtelJsonFormatter entirely + # (propagate=False), so it never carries user.email — this app-level + # logger call, made while the span is still current, is what actually + # lands a searchable user_email field in stdout for Loki. + logger.info("authenticated request for %s", resolved.email) return resolved