feat(logging): 全局打印对外端点入参与缩略
This commit is contained in:
1 parent
9a91f7dea0
commit
bee1a00472
2 files changed
+116
-1
No files matched your search
@@ -15,6 +15,30 @@ from app.services.token.manager import get_token_manager
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _mask_token_for_log(token: str) -> str:
|
||||
raw = str(token or "").strip()
|
||||
if not raw:
|
||||
return ""
|
||||
if len(raw) <= 12:
|
||||
return raw
|
||||
return f"{raw[:6]}...{raw[-6:]}"
|
||||
|
||||
|
||||
def _log_nsfw_enable_payload(api_name: str, data: dict) -> None:
|
||||
single = str(data.get("token") or "").strip()
|
||||
batch = data.get("tokens")
|
||||
token_list = [str(t).strip() for t in batch] if isinstance(batch, list) else []
|
||||
token_list = [t for t in token_list if t]
|
||||
preview = [_mask_token_for_log(t) for t in token_list[:5]]
|
||||
payload = {
|
||||
"token": _mask_token_for_log(single) if single else "",
|
||||
"tokens_count": len(token_list),
|
||||
"tokens_preview": preview,
|
||||
"raw_keys": sorted(list(data.keys())) if isinstance(data, dict) else [],
|
||||
}
|
||||
logger.info(f"{api_name} request payload: {payload}")
|
||||
|
||||
|
||||
@router.get("/tokens", dependencies=[Depends(verify_app_key)])
|
||||
async def get_tokens():
|
||||
"""获取所有 Token"""
|
||||
@@ -255,6 +279,7 @@ async def batch_cancel(task_id: str):
|
||||
async def enable_nsfw(data: dict):
|
||||
"""批量开启 NSFW (Unhinged) 模式"""
|
||||
try:
|
||||
_log_nsfw_enable_payload("tokens/nsfw/enable", data)
|
||||
mgr = await get_token_manager()
|
||||
|
||||
tokens = []
|
||||
@@ -316,6 +341,7 @@ async def enable_nsfw(data: dict):
|
||||
@router.post("/tokens/nsfw/enable/async", dependencies=[Depends(verify_app_key)])
|
||||
async def enable_nsfw_async(data: dict):
|
||||
"""批量开启 NSFW (Unhinged) 模式(异步批量 + SSE 进度)"""
|
||||
_log_nsfw_enable_payload("tokens/nsfw/enable/async", data)
|
||||
mgr = await get_token_manager()
|
||||
|
||||
tokens = []
|
||||
|
||||
@@ -7,12 +7,99 @@ Response Middleware
|
||||
|
||||
import time
|
||||
import uuid
|
||||
import json
|
||||
from typing import Any
|
||||
from urllib.parse import parse_qs
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.requests import Request
|
||||
|
||||
from app.core.logger import logger
|
||||
|
||||
|
||||
SENSITIVE_KEYS = {
|
||||
"authorization",
|
||||
"api_key",
|
||||
"app_key",
|
||||
"public_key",
|
||||
"token",
|
||||
"tokens",
|
||||
"password",
|
||||
"cookie",
|
||||
}
|
||||
|
||||
|
||||
def _short_text(value: Any, max_len: int = 240) -> Any:
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
text = value.strip()
|
||||
if len(text) <= max_len:
|
||||
return text
|
||||
return f"{text[:max_len]}...(len={len(text)})"
|
||||
|
||||
|
||||
def _sanitize_payload(value: Any, depth: int = 0) -> Any:
|
||||
if depth > 4:
|
||||
return "..."
|
||||
if isinstance(value, dict):
|
||||
result = {}
|
||||
for key, item in value.items():
|
||||
k = str(key)
|
||||
if k.lower() in SENSITIVE_KEYS:
|
||||
result[k] = "<redacted>"
|
||||
else:
|
||||
result[k] = _sanitize_payload(item, depth + 1)
|
||||
return result
|
||||
if isinstance(value, list):
|
||||
if len(value) > 20:
|
||||
return [_sanitize_payload(v, depth + 1) for v in value[:20]] + [f"...(len={len(value)})"]
|
||||
return [_sanitize_payload(v, depth + 1) for v in value]
|
||||
if isinstance(value, str):
|
||||
return _short_text(value)
|
||||
return value
|
||||
|
||||
|
||||
async def _extract_request_payload(request: Request) -> dict[str, Any]:
|
||||
query_data = {k: v if len(v) > 1 else v[0] for k, v in parse_qs(request.url.query, keep_blank_values=True).items()}
|
||||
payload: dict[str, Any] = {"query": _sanitize_payload(query_data)}
|
||||
|
||||
if request.method in ("GET", "HEAD", "OPTIONS"):
|
||||
return payload
|
||||
|
||||
content_type = (request.headers.get("content-type") or "").lower()
|
||||
raw = await request.body()
|
||||
if not raw:
|
||||
payload["body"] = None
|
||||
return payload
|
||||
|
||||
if "application/json" in content_type:
|
||||
try:
|
||||
payload["body"] = _sanitize_payload(json.loads(raw.decode("utf-8", errors="ignore")))
|
||||
return payload
|
||||
except Exception:
|
||||
payload["body"] = _short_text(raw.decode("utf-8", errors="ignore"))
|
||||
return payload
|
||||
|
||||
if "application/x-www-form-urlencoded" in content_type:
|
||||
form = parse_qs(raw.decode("utf-8", errors="ignore"), keep_blank_values=True)
|
||||
payload["body"] = _sanitize_payload({k: v if len(v) > 1 else v[0] for k, v in form.items()})
|
||||
return payload
|
||||
|
||||
if "multipart/form-data" in content_type:
|
||||
payload["body"] = {
|
||||
"_type": "multipart/form-data",
|
||||
"_size": len(raw),
|
||||
"_preview": _short_text(raw[:400].decode("utf-8", errors="ignore")),
|
||||
}
|
||||
return payload
|
||||
|
||||
payload["body"] = {
|
||||
"_type": content_type or "unknown",
|
||||
"_size": len(raw),
|
||||
"_preview": _short_text(raw[:400].decode("utf-8", errors="ignore")),
|
||||
}
|
||||
return payload
|
||||
|
||||
|
||||
class ResponseLoggerMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
请求日志/响应追踪中间件
|
||||
@@ -41,12 +128,14 @@ class ResponseLoggerMiddleware(BaseHTTPMiddleware):
|
||||
return await call_next(request)
|
||||
|
||||
# 记录请求信息
|
||||
req_payload = await _extract_request_payload(request)
|
||||
logger.info(
|
||||
f"Request: {request.method} {request.url.path}",
|
||||
f"Request: {request.method} {request.url.path} payload={req_payload}",
|
||||
extra={
|
||||
"traceID": trace_id,
|
||||
"method": request.method,
|
||||
"path": request.url.path,
|
||||
"payload": req_payload,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user