celery 升级优化V2 | 日志调整 | 前端BUG修复
This commit is contained in:
@@ -21,7 +21,6 @@ MODULE_GENERATION_LOG_ROOT = os.path.join(LOG_BASE_DIR, "ModuleGeneration")
|
||||
AI_MODEL_LOG_ROOT = LOG_DIR
|
||||
SENSITIVE_KEY_PATTERNS = (
|
||||
"secret",
|
||||
"token",
|
||||
"authorization",
|
||||
"cookie",
|
||||
"credential",
|
||||
@@ -30,9 +29,28 @@ SENSITIVE_KEY_PATTERNS = (
|
||||
"access_key",
|
||||
"api_key",
|
||||
"apikey",
|
||||
"security-token",
|
||||
"x-tos-security-token",
|
||||
"security_token",
|
||||
)
|
||||
SENSITIVE_TOKEN_KEYS = {
|
||||
"token",
|
||||
"access_token",
|
||||
"refresh_token",
|
||||
"bearer_token",
|
||||
"security_token",
|
||||
"x_tos_security_token",
|
||||
}
|
||||
FILE_BASE64_KEYS = {
|
||||
"b64_json",
|
||||
"file_data",
|
||||
"file_base64",
|
||||
"content_base64",
|
||||
"image_base64",
|
||||
"video_base64",
|
||||
"audio_base64",
|
||||
}
|
||||
FILE_DATA_URI_MIME_PREFIXES = ("image/", "video/", "audio/")
|
||||
FILE_DATA_URI_MIME_TYPES = {"application/pdf", "application/octet-stream"}
|
||||
FILE_BASE64_PREVIEW_CHARS = 30
|
||||
|
||||
|
||||
def _safe_name(value: str | None, default: str = "unknown") -> str:
|
||||
@@ -49,7 +67,41 @@ def _mask_string(value: str) -> str:
|
||||
|
||||
def _is_sensitive_key(key: str) -> bool:
|
||||
lower = str(key).replace("-", "_").lower()
|
||||
return lower == "sign" or any(pattern in lower for pattern in SENSITIVE_KEY_PATTERNS)
|
||||
if lower == "sign" or lower in SENSITIVE_TOKEN_KEYS:
|
||||
return True
|
||||
return any(pattern in lower for pattern in SENSITIVE_KEY_PATTERNS)
|
||||
|
||||
|
||||
def _decoded_base64_size(value: str) -> int:
|
||||
compact = "".join(value.split())
|
||||
if not compact:
|
||||
return 0
|
||||
padding = 2 if compact.endswith("==") else (1 if compact.endswith("=") else 0)
|
||||
return max(0, (len(compact) * 3) // 4 - padding)
|
||||
|
||||
|
||||
def _file_base64_preview(value: str, key_path: tuple[str, ...]) -> str | None:
|
||||
data_uri = re.match(r"^data:([^;,]+);base64,(.*)$", value, flags=re.IGNORECASE | re.DOTALL)
|
||||
prefix = ""
|
||||
payload = value
|
||||
is_file = False
|
||||
if data_uri:
|
||||
mime = str(data_uri.group(1) or "").lower()
|
||||
is_file = mime.startswith(FILE_DATA_URI_MIME_PREFIXES) or mime in FILE_DATA_URI_MIME_TYPES
|
||||
prefix = value[: value.find(",") + 1]
|
||||
payload = data_uri.group(2)
|
||||
elif key_path and key_path[-1].replace("-", "_").lower() in FILE_BASE64_KEYS:
|
||||
# Raw base64 is treated as file content only for an explicit file field.
|
||||
is_file = len(value) >= 64 and bool(re.fullmatch(r"[A-Za-z0-9+/=\s]+", value))
|
||||
if not is_file:
|
||||
return None
|
||||
|
||||
compact = "".join(payload.split())
|
||||
preview = compact[:FILE_BASE64_PREVIEW_CHARS]
|
||||
total_bytes = _decoded_base64_size(compact)
|
||||
preview_bytes = min(total_bytes, (len(preview) * 3) // 4)
|
||||
remaining_bytes = max(0, total_bytes - preview_bytes)
|
||||
return f"{prefix}{preview}...<remaining_file_bytes:{remaining_bytes}>"
|
||||
|
||||
|
||||
def _sanitize_url(value: str) -> str:
|
||||
@@ -65,10 +117,13 @@ def _sanitize_url(value: str) -> str:
|
||||
return value
|
||||
|
||||
|
||||
def sanitize_log_value(value: Any) -> Any:
|
||||
def sanitize_log_value(value: Any, *, key_path: tuple[str, ...] = ()) -> Any:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
file_preview = _file_base64_preview(value, key_path)
|
||||
if file_preview is not None:
|
||||
return file_preview
|
||||
text = _sanitize_url(value) if value.startswith(("http://", "https://")) else value
|
||||
if len(text) > MAX_LOG_FIELD_LENGTH:
|
||||
return text[:MAX_LOG_FIELD_LENGTH] + f"...<truncated:{len(text) - MAX_LOG_FIELD_LENGTH}>"
|
||||
@@ -77,10 +132,14 @@ def sanitize_log_value(value: Any) -> Any:
|
||||
output: dict[str, Any] = {}
|
||||
for k, v in value.items():
|
||||
key = str(k)
|
||||
output[key] = "***" if _is_sensitive_key(key) else sanitize_log_value(v)
|
||||
output[key] = (
|
||||
"***"
|
||||
if _is_sensitive_key(key)
|
||||
else sanitize_log_value(v, key_path=(*key_path, key))
|
||||
)
|
||||
return output
|
||||
if isinstance(value, list):
|
||||
return [sanitize_log_value(v) for v in value]
|
||||
if isinstance(value, (list, tuple)):
|
||||
return [sanitize_log_value(v, key_path=(*key_path, str(index))) for index, v in enumerate(value)]
|
||||
return value
|
||||
|
||||
|
||||
@@ -262,6 +321,13 @@ def log_ai_model_event(
|
||||
*,
|
||||
event_type: str,
|
||||
module: str | None = None,
|
||||
step_code: str | None = None,
|
||||
call_id: str | None = None,
|
||||
event_phase: str | None = None,
|
||||
owner_type: str | None = None,
|
||||
owner_id: str | None = None,
|
||||
generation_attempt_no: int | None = None,
|
||||
latency_ms: int | None = None,
|
||||
event_status: str = "success",
|
||||
source: str | None = None,
|
||||
trace_id: str | None = None,
|
||||
@@ -314,8 +380,19 @@ def log_ai_model_event(
|
||||
)
|
||||
entry.update(
|
||||
{
|
||||
"call_id": call_id,
|
||||
"event_phase": event_phase or event_type,
|
||||
"step_code": step_code,
|
||||
"owner_type": owner_type,
|
||||
"owner_id": owner_id,
|
||||
"generation_attempt_no": generation_attempt_no,
|
||||
"latency_ms": latency_ms,
|
||||
# Preserve the legacy fields for existing log readers while also
|
||||
# exposing unambiguous configuration/provider model names.
|
||||
"model_name": model_config_name,
|
||||
"model_id": model_name,
|
||||
"model_config_name": model_config_name,
|
||||
"provider_model_name": model_name,
|
||||
"model_config_id": model_config_id,
|
||||
"provider": provider,
|
||||
"api_base": api_base,
|
||||
|
||||
Reference in New Issue
Block a user