From c40662bf7709e82c1636ae9badf005e72cfc0a72 Mon Sep 17 00:00:00 2001 From: 0206pdh <0206pdh@naver.com> Date: Fri, 3 Jul 2026 11:43:48 +0900 Subject: [PATCH] fix(logging): inject user_email into structured log output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VOC UI의 Pod Logs(Loki) 검색이 user.email로 조회되지 않는 문제 수정. 기존에는 user.email이 OTel span attribute로만 기록되어 OpenSearch 트레이스 검색에서만 사용 가능했고, stdout 로그 라인 텍스트에는 찍히지 않아 Loki의 |= 텍스트 검색으로는 찾을 수 없었다. get_current_user()에서 인증된 요청마다 ContextVar에 user_email을 저장하고, OtelJsonFormatter가 로그 엔트리에 이를 포함시키도록 함. --- app/api/dependencies.py | 2 ++ app/core/logging.py | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/app/api/dependencies.py b/app/api/dependencies.py index 434401a..d188e37 100644 --- a/app/api/dependencies.py +++ b/app/api/dependencies.py @@ -9,6 +9,7 @@ from app.core.db import get_db_session from app.core.enums import UserStatus from app.core.config import get_settings +from app.core.logging import set_request_user_email from app.core.security import decode_access_token from app.repositories.user import UserRepository from app.schemas.auth import TokenPayload, UserRead @@ -51,6 +52,7 @@ def get_current_user( span = trace.get_current_span() span.set_attribute("user.id", str(resolved.id)) span.set_attribute("user.email", resolved.email) + set_request_user_email(resolved.email) return resolved diff --git a/app/core/logging.py b/app/core/logging.py index cf6ddfa..70d3b52 100644 --- a/app/core/logging.py +++ b/app/core/logging.py @@ -4,9 +4,17 @@ import json import logging +from contextvars import ContextVar from opentelemetry import trace +_user_email_var: ContextVar[str] = ContextVar('user_email', default='') + + +def set_request_user_email(email: str) -> None: + _user_email_var.set(email) + + _SKIP_ATTRS = frozenset(( "name", "msg", "args", "levelname", "levelno", "pathname", "filename", "module", "exc_info", @@ -33,6 +41,10 @@ def format(self, record: logging.LogRecord) -> str: "span_id": format(ctx.span_id, "016x") if ctx.is_valid else "", } + user_email = _user_email_var.get() + if user_email: + entry["user_email"] = user_email + for key, val in record.__dict__.items(): if key not in _SKIP_ATTRS and not key.startswith("_"): entry[key] = val