Skip to content

Commit 17b2829

Browse files
authored
Merge pull request #22 from JobDri-Developer/fix/unmask-openai-usage-token-metrics
[Fix] OpenAI 사용량 토큰 로그 마스킹 예외 처리
2 parents 182db5e + b327e23 commit 17b2829

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

app/logging_utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@
2828
"token",
2929
"api_key",
3030
)
31+
NON_SENSITIVE_TOKEN_METRIC_FIELDS = {
32+
"inputtokens",
33+
"outputtokens",
34+
"totaltokens",
35+
"cachedinputtokens",
36+
"reasoningoutputtokens",
37+
}
3138
STANDARD_LOG_RECORD_FIELDS = set(logging.makeLogRecord({}).__dict__.keys()) | {"message", "asctime"}
3239
DEFAULT_LOG_CONTEXT: dict[str, Any] = {
3340
"requestId": None,
@@ -166,4 +173,6 @@ def _sanitize_value(key: str, value: Any) -> Any:
166173

167174
def _is_sensitive_key(key: str) -> bool:
168175
lowered = key.lower()
176+
if lowered in NON_SENSITIVE_TOKEN_METRIC_FIELDS:
177+
return False
169178
return any(token in lowered for token in SENSITIVE_FIELD_TOKENS)

tests/test_recovery_flow.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def __init__(self, *args, **kwargs) -> None:
100100
from app.api_client import SpringWorkerApiClient
101101
from app.config import settings
102102
from app.consumer import RabbitMqConsumer
103-
from app.logging_utils import WorkerContextFilter, bind_log_context
103+
from app.logging_utils import JsonLogFormatter, WorkerContextFilter, bind_log_context
104104
from app.openai_client import AnalysisOpenAiWorker
105105
from app.recovery import PendingDeliveryStore, TerminalMessageStore
106106
from app.schemas import (
@@ -527,6 +527,33 @@ def test_worker_context_filter_sets_defaults_for_missing_fields(self) -> None:
527527
self.assertIsNone(log_record.retryCount)
528528
self.assertEqual(log_record.logType, "application")
529529

530+
def test_json_log_formatter_does_not_mask_token_usage_metrics(self) -> None:
531+
log_record = logging.LogRecord(
532+
name="app.openai_client",
533+
level=logging.INFO,
534+
pathname=__file__,
535+
lineno=1,
536+
msg="OpenAI analysis 호출이 완료되었습니다.",
537+
args=(),
538+
exc_info=None,
539+
)
540+
log_record.event = "openai.generate.completed"
541+
log_record.inputTokens = 12000
542+
log_record.outputTokens = 1500
543+
log_record.totalTokens = 13500
544+
log_record.cachedInputTokens = 1024
545+
log_record.reasoningOutputTokens = 0
546+
log_record.openai_api_key = "sk-test"
547+
548+
payload = json.loads(JsonLogFormatter().format(log_record))
549+
550+
self.assertEqual(payload["inputTokens"], 12000)
551+
self.assertEqual(payload["outputTokens"], 1500)
552+
self.assertEqual(payload["totalTokens"], 13500)
553+
self.assertEqual(payload["cachedInputTokens"], 1024)
554+
self.assertEqual(payload["reasoningOutputTokens"], 0)
555+
self.assertEqual(payload["openai_api_key"], "***masked***")
556+
530557
def test_deserialize_message_reads_headers(self) -> None:
531558
consumer = RabbitMqConsumer(
532559
api_client=FakeApiClient(),

0 commit comments

Comments
 (0)