From 75fb6a3a01013c3a244ea5a060d87dc42c0f5b19 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Fri, 17 Jul 2026 11:06:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(compare):=20=E6=A0=B8=E5=AF=B9=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E5=86=99=E5=85=A5=20logs/=20=E5=B9=B6=E4=B8=BA=20114?= =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=E5=BC=80=E6=9C=BA=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - compare.py: 新增 write_report(),每次运行把报告以日志形式写到 logs/compare__<日期>.log(含生成时间 + 汇总头部), --report 仍兼容作为额外输出路径 - main.py: compare 子命令改用 write_report,默认落 logs/,stdout 仍打印 - run_compare_ids.cmd: 114 主机开机计划任务包装脚本(ID 级核对) Co-Authored-By: WorkBuddy --- main.py | 17 +++++++++------ run_compare_ids.cmd | 3 +++ src/sync/compare.py | 51 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 run_compare_ids.cmd diff --git a/main.py b/main.py index 34e3dc7..c63cef3 100644 --- a/main.py +++ b/main.py @@ -17,6 +17,7 @@ was launched or whether the venv already has ``src`` on its path. from __future__ import annotations import argparse +import logging import os import sys @@ -27,9 +28,10 @@ from sync.config import load_config from sync.logging_setup import setup_logging from sync.fullsync import full_sync from sync import service -from sync.compare import compare, any_mismatch, format_report +from sync.compare import compare, any_mismatch, format_report, write_report CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.yaml") +log = logging.getLogger("main") def _parse_args(argv): @@ -99,11 +101,14 @@ def main(argv=None) -> int: if args.command == "compare": results = compare(cfg, granularity=args.granularity, db_filter=args.db, table_filter=args.table) - report = format_report(results, args.granularity) - print(report) - if args.report: - with open(args.report, "w", encoding="utf-8") as f: - f.write(report + "\n") + # Persist the report as a dated log file under the logging directory + # (logs/ by default); --report still allows an extra custom path. + log_dir = os.path.dirname((cfg.logging or {}).get("path", "sync.log")) or "." + written = write_report(results, args.granularity, log_dir=log_dir, + extra_path=args.report) + print(format_report(results, args.granularity)) + log.info("compare finished (granularity=%s) report=%s mismatch=%s", + args.granularity, written, any_mismatch(results)) return 1 if any_mismatch(results) else 0 return 2 # unreachable: argparse requires a subcommand diff --git a/run_compare_ids.cmd b/run_compare_ids.cmd new file mode 100644 index 0000000..cbfa530 --- /dev/null +++ b/run_compare_ids.cmd @@ -0,0 +1,3 @@ +@echo off +cd /d C:\Users\peng\Projects\ProductionDataBaseSync_DataMacro +.venv\Scripts\python.exe main.py compare --granularity ids >> logs\compare_task_stdout.log 2>&1 diff --git a/src/sync/compare.py b/src/sync/compare.py index 75c5b0c..83ff563 100644 --- a/src/sync/compare.py +++ b/src/sync/compare.py @@ -11,7 +11,9 @@ as full sync -- empirically confirming the two stay aligned. """ from __future__ import annotations +import datetime as _dt import logging +import os from dataclasses import dataclass, field from .config import FileMapping, SyncConfig @@ -139,3 +141,52 @@ def format_report(results: list[TableResult], granularity: str = "count") -> str else: lines.append(f"{base} access={r.access_count} sql={r.sql_count} [{r.status.upper()}]") return "\n".join(lines) + + +def summarize(results: list[TableResult]) -> dict: + """Tally the outcome buckets: match / mismatch / skipped / error.""" + s = {"match": 0, "mismatch": 0, "skipped": 0, "error": 0} + for r in results: + s[r.status] = s.get(r.status, 0) + 1 + return s + + +def write_report(results: list[TableResult], granularity: str = "count", + log_dir: str = ".", run_dt: _dt.datetime | None = None, + extra_path: str | None = None) -> str: + """Render the full report and persist it as a dated log file under *log_dir*. + + Always writes ``/compare__.log``, + overwriting the day's previous run (one report per day; the logging system's + per-day archival later moves yesterday's file into ``logs/Archive/``). When + *extra_path* is given (the ``--report`` CLI option) the identical content is + also written there for backward compatibility. Returns the primary log path. + """ + run_dt = run_dt or _dt.datetime.now() + stats = summarize(results) + body = format_report(results, granularity) + header = ( + "============================================================\n" + " 数据一致性核对报告 / Compare Report\n" + f" 生成时间 : {run_dt.strftime('%Y-%m-%d %H:%M:%S')}\n" + f" 粒度 : {granularity}\n" + f" 比对表合计 : {stats['match'] + stats['mismatch']}\n" + f" 一致 MATCH : {stats['match']}\n" + f" 不一致 MISMATCH : {stats['mismatch']}\n" + f" 跳过 SKIPPED : {stats['skipped']}\n" + f" 错误 ERROR : {stats['error']}\n" + "============================================================\n" + ) + report = header + body + "\n" + + log_dir = log_dir or "." + os.makedirs(log_dir, exist_ok=True) + primary = os.path.join( + log_dir, f"compare_{granularity}_{run_dt.strftime('%Y-%m-%d')}.log" + ) + with open(primary, "w", encoding="utf-8") as f: + f.write(report) + if extra_path: + with open(extra_path, "w", encoding="utf-8") as f: + f.write(report) + return primary