105 lines
3.1 KiB
Python
105 lines
3.1 KiB
Python
"""重跑 4 个失败任务(v3),捕获完整错误输出"""
|
||
import os, sys, subprocess, time
|
||
from pathlib import Path
|
||
from datetime import datetime
|
||
from dotenv import load_dotenv
|
||
from zoneinfo import ZoneInfo
|
||
|
||
load_dotenv(Path(__file__).resolve().parents[2] / ".env")
|
||
|
||
TZ = ZoneInfo("Asia/Shanghai")
|
||
ETL_CWD = Path(__file__).resolve().parents[2] / "apps" / "etl" / "connectors" / "feiqiu"
|
||
|
||
TASKS = [
|
||
"DWS_FINANCE_INCOME_STRUCTURE",
|
||
"DWS_ASSISTANT_MONTHLY",
|
||
"DWS_RELATION_INDEX",
|
||
"DWS_SPENDING_POWER_INDEX",
|
||
]
|
||
|
||
COMMON_ARGS = [
|
||
"--window-start", "2025-11-01 00:00:00",
|
||
"--window-end", "2026-02-27 00:00:00",
|
||
"--window-split-days", "30",
|
||
"--force-full",
|
||
]
|
||
|
||
|
||
def run_task(task: str) -> dict:
|
||
layer = "INDEX" if task.endswith("_INDEX") else "DWS"
|
||
cmd = [
|
||
"uv", "run", "--package", "etl-feiqiu",
|
||
"python", "-m", "cli.main",
|
||
"--layers", layer,
|
||
"--tasks", task,
|
||
*COMMON_ARGS,
|
||
]
|
||
start = time.time()
|
||
proc = subprocess.run(
|
||
cmd, cwd=str(ETL_CWD),
|
||
capture_output=True, text=True,
|
||
encoding="utf-8", errors="replace",
|
||
timeout=300,
|
||
)
|
||
elapsed = time.time() - start
|
||
output = proc.stdout + proc.stderr
|
||
|
||
has_error = any(
|
||
kw in line for line in output.splitlines()
|
||
for kw in ("ERROR", "CRITICAL", "Traceback")
|
||
)
|
||
return {
|
||
"task": task, "exit_code": proc.returncode,
|
||
"elapsed": elapsed, "success": proc.returncode == 0 and not has_error,
|
||
"output": output,
|
||
}
|
||
|
||
|
||
def main():
|
||
now = datetime.now(TZ)
|
||
print(f"{'='*60}")
|
||
print(f"失败任务重跑 v3 | {now.isoformat()}")
|
||
print(f"{'='*60}\n")
|
||
|
||
results = []
|
||
for i, task in enumerate(TASKS, 1):
|
||
print(f"[{i}/{len(TASKS)}] {task} ...", end=" ", flush=True)
|
||
try:
|
||
r = run_task(task)
|
||
results.append(r)
|
||
icon = "OK" if r["success"] else "FAIL"
|
||
print(f"{icon} ({r['elapsed']:.0f}s)")
|
||
if not r["success"]:
|
||
# 打印最后 30 行
|
||
lines = r["output"].splitlines()
|
||
for line in lines[-30:]:
|
||
print(f" | {line}")
|
||
except subprocess.TimeoutExpired:
|
||
results.append({"task": task, "success": False, "elapsed": 300,
|
||
"exit_code": -1, "output": "TIMEOUT"})
|
||
print("TIMEOUT")
|
||
|
||
ok = [r for r in results if r["success"]]
|
||
fail = [r for r in results if not r["success"]]
|
||
print(f"\n{'='*60}")
|
||
print(f"结果: {len(ok)}/{len(results)} 成功")
|
||
|
||
# 保存日志
|
||
log_root = os.environ.get("SYSTEM_LOG_ROOT")
|
||
if not log_root:
|
||
raise RuntimeError("SYSTEM_LOG_ROOT 未设置")
|
||
log_dir = Path(log_root)
|
||
log_dir.mkdir(parents=True, exist_ok=True)
|
||
log_file = log_dir / f"{now.strftime('%Y%m%d')}_rerun_v3.log"
|
||
with open(log_file, "w", encoding="utf-8") as f:
|
||
for r in results:
|
||
icon = "OK" if r["success"] else "FAIL"
|
||
f.write(f"[{icon}] {r['task']} ({r['elapsed']:.0f}s)\n")
|
||
f.write(f"{r['output']}\n{'='*60}\n")
|
||
print(f"日志: {log_file}")
|
||
sys.exit(0 if not fail else 1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|