日志改为摘要 + 有界预览:单次请求日志 6940KB → 2.8KB

/convert/start 原先 logger.info 直接 dump 整份结果(json.dumps(indent=2)):实测
1MB 输入 → 20188 条 → 6.9MB 日志,是原文的 15%,indent 还要再乘 1.4。转几份族谱
日志文件就会被撑爆。

改为:
- 一行摘要:输入字数、结果条数、类型分布、耗时(格式与 batch 接口的日志保持一致)
- 前 LOG_PREVIEW_ITEMS(=10) 条结果预览,体积有硬上界
- 新增 _result_summary() helper

类型分布一行能看出解析形态(如 {'干支纪年': 15624, '古代纪年': 147, ...}),
排查"整批变成未匹配"这类回归比翻全量结果还快。明细请用接口返回值。

验证:单次 1MB 请求日志 2.8KB(降 2428x);/convert/start、/convert/batch、
403 鉴权、空文本四条路径均正常;pytest 260 passed。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
sansen
2026-09-23 17:42:42 +08:00
co-authored by Claude Code
parent b264073f2b
commit 4e68372518
+23 -1
View File
@@ -1,4 +1,5 @@
import json import json
from collections import Counter
from time import perf_counter from time import perf_counter
from fastapi import FastAPI, HTTPException, Header, Request from fastapi import FastAPI, HTTPException, Header, Request
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
@@ -20,12 +21,26 @@ app.add_middleware(
# batch 接口单次请求的条目上限(防滥用) # batch 接口单次请求的条目上限(防滥用)
BATCH_MAX_ITEMS = 1000 BATCH_MAX_ITEMS = 1000
# 日志里最多预览多少条结果。完整结果序列化后体积可达数 MB
# (实测 1MB 输入 → 20188 条 → 6.9MB JSON,是原文的 15%,含缩进还要再乘 1.4),
# 全量落盘会迅速撑爆日志文件。
LOG_PREVIEW_ITEMS = 10
def _check_auth(Authorization: str): def _check_auth(Authorization: str):
if Authorization != "3FFA6A4B073CF065969630692331A873": if Authorization != "3FFA6A4B073CF065969630692331A873":
raise HTTPException(status_code=403, detail="未授权的操作") raise HTTPException(status_code=403, detail="未授权的操作")
def _result_summary(res: list) -> str:
"""一行摘要:结果条数 + 类型分布(如 {'古代纪年': 12, '干支纪年': 3})。
/convert/start 的日志只记摘要与有界预览,不记全量结果——看明细请用接口返回值。
"""
kinds = Counter(r["类型"] for r in res)
return f"{len(res)} 条, 类型分布 {dict(kinds)}"
def _parse_urlencoded_content(body: bytes) -> str: def _parse_urlencoded_content(body: bytes) -> str:
"""手动解析 x-www-form-urlencoded 的 content 字段:绕过 starlette 1MB 表单限制""" """手动解析 x-www-form-urlencoded 的 content 字段:绕过 starlette 1MB 表单限制"""
from urllib.parse import parse_qs from urllib.parse import parse_qs
@@ -47,8 +62,15 @@ async def convert_str(
try: try:
content = _parse_urlencoded_content(await request.body()) content = _parse_urlencoded_content(await request.body())
t0 = perf_counter()
res = convert_text(content) res = convert_text(content)
logger.info(f"转换结果: {json.dumps(res, ensure_ascii=False, indent=2)}") dt = perf_counter() - t0
# 只记摘要 + 前 N 条预览(格式与 batch 接口的日志保持一致)
logger.info(f"转换完成: 输入 {len(content)} 字, {_result_summary(res)}, "
f"耗时 {dt * 1000:.0f}ms")
if res:
head = res[:LOG_PREVIEW_ITEMS]
logger.info(f"结果前 {len(head)} 条: {json.dumps(head, ensure_ascii=False)}")
return JSONResponse(content={"code": 0, "result": res}) return JSONResponse(content={"code": 0, "result": res})
except Exception as e: except Exception as e:
logger.error(f"转换异常: {str(e)}") logger.error(f"转换异常: {str(e)}")