Files
video-gen/video-gen-api/app/services/virtual_portrait_v3/logging_service.py
T
root 0c511f3451 1、增加调用 AI 视频生成能力和虚拟素材库管理的对外api
2、增加后台apikkey管理
3、增加apikey单独的模型定价
4、增加apikey调用情况
5、完善所有数据的注释增加
2026-08-06 13:13:28 +08:00

185 lines
5.0 KiB
Python

"""VP V3 虚拟素材库专用日志服务。
统一记录所有 VP V3 相关操作日志到 logs/virtual_portrait_v3/ 目录。
按天分文件,便于管理和排查问题。
"""
import json
import logging
import os
from datetime import datetime, timezone
from app.config import settings
# === 日志目录 ===
BASE_LOG_DIR = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))),
"log", "virtual_portrait_v3",
)
os.makedirs(BASE_LOG_DIR, exist_ok=True)
class _DailyFileHandler(logging.Handler):
"""按天写入不同日志文件的处理器。"""
def __init__(self, log_dir: str):
super().__init__()
self.log_dir = log_dir
self._current_date = None
self._file_handler = None
self._open_file()
def _open_file(self):
"""打开当天的日志文件。"""
now = datetime.now(timezone.utc)
date_str = now.strftime("%Y-%m-%d")
if date_str == self._current_date and self._file_handler:
return
if self._file_handler:
self._file_handler.close()
self._current_date = date_str
filepath = os.path.join(self.log_dir, f"{date_str}.log")
self._file_handler = open(filepath, "a", encoding="utf-8")
def emit(self, record):
try:
self._open_file()
msg = self.format(record)
self._file_handler.write(msg + "\n")
self._file_handler.flush()
except Exception:
self.handleError(record)
def close(self):
if self._file_handler:
self._file_handler.close()
super().close()
def _create_logger(name: str, filename: str | None = None) -> logging.Logger:
"""创建专用 Logger。"""
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
# 避免重复添加 handler
if logger.handlers:
return logger
# 按天写入文件
handler = _DailyFileHandler(BASE_LOG_DIR)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter(
"%(asctime)s | %(levelname)s | %(name)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
handler.setFormatter(formatter)
logger.addHandler(handler)
# 不向上传播到 root logger(避免重复输出到控制台)
logger.propagate = False
return logger
# === 专用 Logger 实例 ===
asset_logger = _create_logger("vp_v3.asset")
project_logger = _create_logger("vp_v3.project")
quota_logger = _create_logger("vp_v3.quota")
api_logger = _create_logger("vp_v3.api")
def log_asset_event(
event_type: str,
api_key_id: str,
asset_id: str | None = None,
project_id: str | None = None,
status: str | None = None,
detail: dict | None = None,
error: str | None = None,
):
"""记录素材相关事件。"""
log_data = {
"event_type": event_type,
"api_key_id": api_key_id,
"asset_id": asset_id,
"project_id": project_id,
"status": status,
"detail": detail or {},
}
if error:
log_data["error"] = error
asset_logger.error(json.dumps(log_data, ensure_ascii=False, default=str))
else:
asset_logger.info(json.dumps(log_data, ensure_ascii=False, default=str))
def log_project_event(
event_type: str,
api_key_id: str,
project_id: str | None = None,
status: str | None = None,
detail: dict | None = None,
error: str | None = None,
):
"""记录项目相关事件。"""
log_data = {
"event_type": event_type,
"api_key_id": api_key_id,
"project_id": project_id,
"status": status,
"detail": detail or {},
}
if error:
log_data["error"] = error
project_logger.error(json.dumps(log_data, ensure_ascii=False, default=str))
else:
project_logger.info(json.dumps(log_data, ensure_ascii=False, default=str))
def log_quota_event(
event_type: str,
api_key_id: str,
quota_type: str,
amount: float,
quota_before: float | None = None,
quota_after: float | None = None,
detail: dict | None = None,
):
"""记录配额相关事件。"""
log_data = {
"event_type": event_type,
"api_key_id": api_key_id,
"quota_type": quota_type,
"amount": amount,
"quota_before": quota_before,
"quota_after": quota_after,
"detail": detail or {},
}
quota_logger.info(json.dumps(log_data, ensure_ascii=False, default=str))
def log_api_request(
method: str,
path: str,
api_key_id: str,
status_code: int,
duration_ms: int,
error: str | None = None,
):
"""记录 API 请求。"""
log_data = {
"method": method,
"path": path,
"api_key_id": api_key_id,
"status_code": status_code,
"duration_ms": duration_ms,
}
if error:
log_data["error"] = error
api_logger.error(json.dumps(log_data, ensure_ascii=False, default=str))
else:
api_logger.info(json.dumps(log_data, ensure_ascii=False, default=str))