87 lines
3.2 KiB
Python
87 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from zipfile import ZIP_DEFLATED, ZipFile
|
|
|
|
from app.core.files import runtime_root, tail_lines
|
|
from app.services.detect_service import get_detect_status
|
|
from app.services.runtime_status_service import get_runtime_status
|
|
from app.services.settings_service import get_settings_payload
|
|
|
|
|
|
def _tail_api_runtime_log(filename: str, max_lines: int = 80) -> list[str]:
|
|
path = Path(__file__).resolve().parents[2] / "runtime" / "logs" / filename
|
|
if not path.exists():
|
|
return []
|
|
with path.open("r", encoding="utf-8", errors="replace") as handle:
|
|
return handle.read().splitlines()[-max_lines:]
|
|
|
|
|
|
def latest_logs() -> dict:
|
|
worker_lines = tail_lines("detect_worker.log", max_lines=80)
|
|
desktop_lines = tail_lines("logs/app.log", max_lines=60)
|
|
api_stdout_lines = _tail_api_runtime_log("domain-api.stdout.log", max_lines=60)
|
|
api_stderr_lines = _tail_api_runtime_log("domain-api.stderr.log", max_lines=60)
|
|
api_lines = api_stderr_lines + api_stdout_lines + desktop_lines
|
|
|
|
summary = "未发现显著异常"
|
|
level = "info"
|
|
if any("Redis订阅失败" in line for line in worker_lines):
|
|
summary = "检测端存在 Redis 订阅读超时重连,需要后续继续优化订阅策略。"
|
|
level = "warning"
|
|
elif any("无可用代理" in line for line in worker_lines):
|
|
summary = "代理池存在无可用代理情况,检测端当前可能回落直连或等待代理。"
|
|
level = "warning"
|
|
elif any("Traceback" in line or "ERROR:" in line for line in api_lines):
|
|
summary = "API 运行日志中发现异常堆栈,请优先检查 domain-api stderr 日志。"
|
|
level = "warning"
|
|
|
|
return {
|
|
"worker": worker_lines,
|
|
"api": api_lines,
|
|
"diagnostics": {
|
|
"summary": summary,
|
|
"level": level,
|
|
},
|
|
}
|
|
|
|
|
|
def build_diagnostic_bundle() -> tuple[Path, str]:
|
|
payload = latest_logs()
|
|
generated_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
|
bundle_dir = runtime_root() / "diagnostics"
|
|
bundle_dir.mkdir(parents=True, exist_ok=True)
|
|
zip_path = bundle_dir / f"diagnostic_bundle_{timestamp}.zip"
|
|
|
|
settings_payload = get_settings_payload()
|
|
detect_status = get_detect_status()
|
|
runtime_status = get_runtime_status()
|
|
|
|
with ZipFile(zip_path, "w", compression=ZIP_DEFLATED) as archive:
|
|
archive.writestr(
|
|
"summary.json",
|
|
json.dumps(
|
|
{
|
|
"generated_at": generated_at,
|
|
"diagnostics": payload["diagnostics"],
|
|
"runtime_status": runtime_status,
|
|
"detect_status": detect_status,
|
|
},
|
|
ensure_ascii=False,
|
|
indent=2,
|
|
default=str,
|
|
),
|
|
)
|
|
archive.writestr(
|
|
"settings_snapshot.json",
|
|
json.dumps(settings_payload, ensure_ascii=False, indent=2, default=str),
|
|
)
|
|
archive.writestr("logs/worker.log", "\n".join(payload["worker"]))
|
|
archive.writestr("logs/api.log", "\n".join(payload["api"]))
|
|
|
|
return zip_path, zip_path.name
|