#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 调查 ETL 在 2026-02-14 后停止处理的原因 用法: cd C:/NeoZQYY python scripts/ops/_investigate_etl_gap.py """ import os import sys from datetime import datetime from pathlib import Path import glob # 添加项目根目录到 Python 路径 project_root = Path(__file__).parent.parent.parent sys.path.insert(0, str(project_root)) # 加载环境变量 from dotenv import load_dotenv load_dotenv(project_root / ".env") def main(): """调查 ETL 处理中断的原因""" log_dir = Path(os.environ["SYSTEM_LOG_ROOT"]) print("🔍 调查 ETL 在 2026-02-14 后停止处理的原因") print("=" * 60) # 1. 检查 ETL 日志目录 etl_log_root = os.environ.get("LOG_ROOT") if etl_log_root: etl_log_dir = Path(etl_log_root) print(f"📂 ETL 日志目录: {etl_log_dir}") if etl_log_dir.exists(): # 查找最近的日志文件 log_files = list(etl_log_dir.glob("*.log")) if log_files: # 按修改时间排序 log_files.sort(key=lambda f: f.stat().st_mtime, reverse=True) print(f"📋 找到 {len(log_files)} 个日志文件") # 检查最近 5 个日志文件 for i, log_file in enumerate(log_files[:5]): mtime = datetime.fromtimestamp(log_file.stat().st_mtime) size = log_file.stat().st_size print(f" {i+1}. {log_file.name} - {mtime.strftime('%Y-%m-%d %H:%M:%S')} ({size} bytes)") # 读取最新日志的尾部 latest_log = log_files[0] print(f"\\n📄 最新日志文件内容 ({latest_log.name}):") try: with open(latest_log, 'r', encoding='utf-8') as f: lines = f.readlines() # 显示最后 20 行 tail_lines = lines[-20:] if len(lines) > 20 else lines for line in tail_lines: print(f" {line.rstrip()}") except Exception as e: print(f" ❌ 读取日志失败: {e}") else: print("❌ 未找到 ETL 日志文件") else: print(f"❌ ETL 日志目录不存在: {etl_log_dir}") # 2. 数据库发现总结 print(f"\\n📊 数据库调查结果:") print(f" - ODS 层: 有 2026-02-15 后的数据 (89 条 2026-02-24/25 的记录)") print(f" - DWD 层: 没有 2026-02-15 后的数据 (最晚到 2026-02-14 00:21:21)") print(f" - 缺失数据: 2026-02-24 (80条) + 2026-02-25 (9条) = 89 条记录") # 3. 可能的原因分析 print(f"\\n🔍 可能的原因:") print(f" 1. ETL DWD 任务在 2026-02-14 后没有运行") print(f" 2. DWD 任务运行了但处理失败 (错误/异常)") print(f" 3. DWD 任务的时间窗口配置问题") print(f" 4. 数据质量检查导致数据被过滤") print(f" 5. 春节期间手动停止了 ETL 调度") # 4. 建议的解决步骤 print(f"\\n🔧 建议解决步骤:") print(f" 1. 检查 ETL 调度状态 (cron/systemd/手动)") print(f" 2. 手动运行 DWD_LOAD_FROM_ODS 任务处理缺失数据") print(f" 3. 检查 ETL 配置中的时间窗口设置") print(f" 4. 验证数据质量规则是否过于严格") # 5. 生成调查报告 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") report_file = log_dir / f"etl_gap_investigation_{timestamp}.md" with open(report_file, "w", encoding="utf-8") as f: f.write("# ETL 数据处理中断调查报告\\n\\n") f.write(f"**调查时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\\n") f.write("## 🎯 问题描述\\n\\n") f.write("ETL 流程在 2026-02-14 后停止将 ODS 数据处理到 DWD 层,导致 SPI 任务基于不完整数据产生警告。\\n\\n") f.write("## 📊 数据差异分析\\n\\n") f.write("| 层级 | 最晚数据时间 | 2026-02-15后记录数 |\\n") f.write("|------|-------------|------------------|\\n") f.write("| ODS | 2026-02-25 03:14:45 | 89 条 |\\n") f.write("| DWD | 2026-02-14 00:21:21 | 0 条 |\\n\\n") f.write("### 缺失数据明细\\n") f.write("- **2026-02-24**: 80 条记录未处理\\n") f.write("- **2026-02-25**: 9 条记录未处理\\n\\n") f.write("## 🔍 可能原因\\n\\n") f.write("1. **ETL 调度中断**: 春节期间可能手动停止了 ETL 调度\\n") f.write("2. **任务执行失败**: DWD 任务遇到错误但未被发现\\n") f.write("3. **配置问题**: 时间窗口或数据过滤规则问题\\n") f.write("4. **资源问题**: 数据库连接、磁盘空间等资源限制\\n\\n") f.write("## 🔧 解决方案\\n\\n") f.write("### 立即措施\\n") f.write("```bash\\n") f.write("# 手动运行 DWD 任务处理缺失数据\\n") f.write("cd apps/etl/connectors/feiqiu\\n") f.write("python -m cli.main --tasks DWD_LOAD_FROM_ODS\\n") f.write("```\\n\\n") f.write("### 验证措施\\n") f.write("1. 检查 DWD 层是否有新数据\\n") f.write("2. 重新运行 SPI 任务验证警告是否消失\\n") f.write("3. 建立 ETL 数据延迟监控\\n\\n") f.write("## 📋 后续预防\\n\\n") f.write("1. **监控告警**: 建立 DWD 数据延迟监控\\n") f.write("2. **自动恢复**: 配置 ETL 任务自动重试机制\\n") f.write("3. **日志审计**: 定期检查 ETL 运行日志\\n") f.write("4. **业务日历**: 集成业务日历避免误判\\n") print(f"\\n📋 调查报告已生成: {report_file}") # 6. 提供手动修复命令 print(f"\\n🚀 立即修复命令:") print(f"cd apps/etl/connectors/feiqiu") print(f"python -m cli.main --tasks DWD_LOAD_FROM_ODS") if __name__ == "__main__": main()