Files
video-gen/video-gen-api/app/services/module_generation_log_service.py
T
2026-06-10 15:17:51 +08:00

141 lines
4.2 KiB
Python

from __future__ import annotations
import json
import os
import re
from datetime import datetime
from typing import Any
from app.services.log_config import LOG_DATE_FORMAT, LOG_DIR, is_enabled
MAX_LOG_FIELD_LENGTH = 20000
MODULE_LOG_ROOT = os.path.join(os.path.dirname(LOG_DIR), "ModuleGeneration")
def _safe_module_name(module: str | None) -> str:
value = str(module or "unknown_module").strip() or "unknown_module"
value = re.sub(r"[^a-zA-Z0-9_.-]+", "_", value)
return value[:120] or "unknown_module"
def _safe_dump_value(value: Any) -> Any:
"""限制单字段长度,避免超长 base64 / 响应体把日志打爆。"""
if value is None:
return None
if isinstance(value, str):
if len(value) > MAX_LOG_FIELD_LENGTH:
return value[:MAX_LOG_FIELD_LENGTH] + f"...<truncated:{len(value) - MAX_LOG_FIELD_LENGTH}>"
return value
if isinstance(value, dict):
return {str(k): _safe_dump_value(v) for k, v in value.items()}
if isinstance(value, list):
return [_safe_dump_value(v) for v in value]
return value
def _append_module_log(module: str, entry: dict[str, Any]) -> None:
if not is_enabled():
return
try:
module_dir = os.path.join(MODULE_LOG_ROOT, _safe_module_name(module))
os.makedirs(module_dir, exist_ok=True)
today = datetime.now().strftime(LOG_DATE_FORMAT)
log_file = os.path.join(module_dir, f"{today}.log")
with open(log_file, "a", encoding="utf-8") as f:
f.write(json.dumps(entry, ensure_ascii=False, default=str) + "\n")
except Exception:
# 日志失败绝不能影响业务主流程。
pass
def log_module_event_file(
*,
module: str,
event_type: str,
project_id: str | None = None,
step_id: str | None = None,
user_id: str | None = None,
message: str | None = None,
detail: dict[str, Any] | None = None,
error: str | None = None,
) -> None:
"""记录模块流程事件到 JSONL 文件。
统一落盘目录:log/ModuleGeneration/{module}/YYYY-MM-DD.log
不再写 module_generation_events 表。
"""
entry = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"log_type": "module_event",
"module": module,
"event_type": event_type,
"project_id": project_id,
"step_id": step_id,
"user_id": user_id,
"message": message,
"detail": _safe_dump_value(detail or {}),
"error": error,
}
_append_module_log(module, entry)
def log_module_prompt_event(
*,
event_type: str,
project_id: str,
step_id: str,
user_id: str,
module: str,
prompt_type: str,
request: dict[str, Any] | None = None,
response: dict[str, Any] | None = None,
token_usage: dict[str, Any] | None = None,
error: str | None = None,
) -> None:
"""记录模块 AI 提词请求/响应到 JSONL 文件。
与模块事件共用同一个服务,但按 module 分目录,方便按模块排查。
"""
entry = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"log_type": "module_prompt",
"module": module,
"event_type": event_type,
"prompt_type": prompt_type,
"project_id": project_id,
"step_id": step_id,
"user_id": user_id,
"request": _safe_dump_value(request or {}),
"response": _safe_dump_value(response or {}),
"token_usage": _safe_dump_value(token_usage or {}),
"error": error,
}
_append_module_log(module, entry)
def log_module_error(
*,
module: str,
event_type: str,
project_id: str | None = None,
step_id: str | None = None,
user_id: str | None = None,
message: str | None = None,
detail: dict[str, Any] | None = None,
error: str | None = None,
) -> None:
"""记录模块异常日志。"""
entry = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"log_type": "module_error",
"module": module,
"event_type": event_type,
"project_id": project_id,
"step_id": step_id,
"user_id": user_id,
"message": message,
"detail": _safe_dump_value(detail or {}),
"error": error,
}
_append_module_log(module, entry)