微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
146
scripts/ops/_investigate_etl_gap.py
Normal file
146
scripts/ops/_investigate_etl_gap.py
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user