提交 2a6ecd8a authored 作者: 陈泽健's avatar 陈泽健

fix(perf): 修复 AI 报告资源使用分析为空

读取嵌套 stats 中的 CPU/内存平均值与峰值,并兼容旧版平铺字段;补充资源章节展示及峰值瓶颈识别。
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
上级 2093c083
......@@ -1014,6 +1014,28 @@ class PerformanceAiService:
# ==================== 规则文档章节构建 ====================
@staticmethod
def _extract_resource_stats(resource_summary: dict) -> dict:
"""
从资源汇总数据中提取 CPU/内存 平均值与峰值。
兼容两种结构:
- 新结构(executor 产出):{"stats": {"avgCpuPercent", "maxCpuPercent",
"avgMemPercent", "maxMemPercent", ...}}
- 旧平铺结构:{"cpu_avg"/"cpu_average"/"memory_avg"/"memory_average", ...}
Returns:
dict: {"cpu_avg", "cpu_max", "mem_avg", "mem_max"}(值可能为 None)
"""
if not resource_summary:
return {"cpu_avg": None, "cpu_max": None, "mem_avg": None, "mem_max": None}
stats = resource_summary.get("stats") or {}
cpu_avg = stats.get("avgCpuPercent") or resource_summary.get("cpu_avg") or resource_summary.get("cpu_average")
cpu_max = stats.get("maxCpuPercent") or resource_summary.get("cpu_max") or resource_summary.get("cpu_max_percent")
mem_avg = stats.get("avgMemPercent") or resource_summary.get("memory_avg") or resource_summary.get("memory_average")
mem_max = stats.get("maxMemPercent") or resource_summary.get("memory_max") or resource_summary.get("memory_max_percent")
return {"cpu_avg": cpu_avg, "cpu_max": cpu_max, "mem_avg": mem_avg, "mem_max": mem_max}
def _build_rule_document_sections(self, report_data: dict) -> list:
"""
基于规则构建 7 章节正式文档结构
......@@ -1058,17 +1080,17 @@ class PerformanceAiService:
start_time = summary.get("start_time") or "未记录"
end_time = summary.get("end_time") or "未记录"
# 资源数据
cpu_usage = None
mem_usage = None
target_cpu = None
target_mem = None
if resource_summary:
cpu_usage = resource_summary.get("cpu_avg") or resource_summary.get("cpu_average")
mem_usage = resource_summary.get("memory_avg") or resource_summary.get("memory_average")
if target_resource_summary:
target_cpu = target_resource_summary.get("cpu_avg") or target_resource_summary.get("cpu_average")
target_mem = target_resource_summary.get("memory_avg") or target_resource_summary.get("memory_average")
# 资源数据(兼容 stats 嵌套与旧平铺键,同时取平均/峰值)
exec_stats = self._extract_resource_stats(resource_summary)
tgt_stats = self._extract_resource_stats(target_resource_summary)
cpu_usage = exec_stats["cpu_avg"]
cpu_max = exec_stats["cpu_max"]
mem_usage = exec_stats["mem_avg"]
mem_max = exec_stats["mem_max"]
target_cpu = tgt_stats["cpu_avg"]
target_cpu_max = tgt_stats["cpu_max"]
target_mem = tgt_stats["mem_avg"]
target_mem_max = tgt_stats["mem_max"]
# ========== 一、测试概述 ==========
mode_label = {"concurrent": "并发模式", "qps": "QPS 模式", "step": "阶梯模式"}.get(mode, mode or "未设置")
......@@ -1224,26 +1246,38 @@ class PerformanceAiService:
resource_rows = []
if cpu_usage is not None:
flag = "超过阈值" if cpu_usage > THRESHOLD_CPU_HIGH else "正常"
resource_content_parts.append(f"执行机 CPU 平均使用率 {cpu_usage:.1f}%,{flag}。")
peak_desc = f",峰值 {cpu_max:.1f}%" if cpu_max is not None else ""
resource_content_parts.append(f"执行机 CPU 平均使用率 {cpu_usage:.1f}%{peak_desc},{flag}。")
resource_rows.append(["执行机", "CPU 平均", f"{cpu_usage:.1f}%", flag])
if cpu_max is not None:
resource_rows.append(["执行机", "CPU 峰值", f"{cpu_max:.1f}%", "超过阈值" if cpu_max > THRESHOLD_CPU_HIGH else "正常"])
else:
resource_content_parts.append("执行机 CPU 数据未采集。")
if mem_usage is not None:
flag = "超过阈值" if mem_usage > THRESHOLD_MEMORY_HIGH else "正常"
resource_content_parts.append(f"执行机内存平均使用率 {mem_usage:.1f}%,{flag}。")
peak_desc = f",峰值 {mem_max:.1f}%" if mem_max is not None else ""
resource_content_parts.append(f"执行机内存平均使用率 {mem_usage:.1f}%{peak_desc},{flag}。")
resource_rows.append(["执行机", "内存平均", f"{mem_usage:.1f}%", flag])
if mem_max is not None:
resource_rows.append(["执行机", "内存峰值", f"{mem_max:.1f}%", "超过阈值" if mem_max > THRESHOLD_MEMORY_HIGH else "正常"])
else:
resource_content_parts.append("执行机内存数据未采集。")
if target_cpu is not None:
flag = "超过阈值" if target_cpu > THRESHOLD_CPU_HIGH else "正常"
resource_content_parts.append(f"目标机 CPU 平均使用率 {target_cpu:.1f}%,{flag}。")
peak_desc = f",峰值 {target_cpu_max:.1f}%" if target_cpu_max is not None else ""
resource_content_parts.append(f"目标机 CPU 平均使用率 {target_cpu:.1f}%{peak_desc},{flag}。")
resource_rows.append(["目标机", "CPU 平均", f"{target_cpu:.1f}%", flag])
if target_cpu_max is not None:
resource_rows.append(["目标机", "CPU 峰值", f"{target_cpu_max:.1f}%", "超过阈值" if target_cpu_max > THRESHOLD_CPU_HIGH else "正常"])
else:
resource_content_parts.append("目标机 CPU 数据未采集。")
if target_mem is not None:
flag = "超过阈值" if target_mem > THRESHOLD_MEMORY_HIGH else "正常"
resource_content_parts.append(f"目标机内存平均使用率 {target_mem:.1f}%,{flag}。")
peak_desc = f",峰值 {target_mem_max:.1f}%" if target_mem_max is not None else ""
resource_content_parts.append(f"目标机内存平均使用率 {target_mem:.1f}%{peak_desc},{flag}。")
resource_rows.append(["目标机", "内存平均", f"{target_mem:.1f}%", flag])
if target_mem_max is not None:
resource_rows.append(["目标机", "内存峰值", f"{target_mem_max:.1f}%", "超过阈值" if target_mem_max > THRESHOLD_MEMORY_HIGH else "正常"])
else:
resource_content_parts.append("目标机内存数据未采集。")
if not resource_rows:
......@@ -1264,17 +1298,29 @@ class PerformanceAiService:
bottleneck_rows.append(["错误率", "应用层", "high", f"错误率 {error_rate:.2f} 超过 {THRESHOLD_ERROR_RATE}% 阈值", f"错误率={error_rate:.2f}%"])
bottleneck_content_parts.append(f"错误率瓶颈:错误率 {error_rate:.2f}% 超过 {THRESHOLD_ERROR_RATE}% 阈值。")
if cpu_usage is not None and cpu_usage > THRESHOLD_CPU_HIGH:
bottleneck_rows.append(["资源", "执行机 CPU", "high", f"CPU {cpu_usage:.1f}% 超过 {THRESHOLD_CPU_HIGH}%", f"CPU={cpu_usage:.1f}%"])
bottleneck_rows.append(["资源", "执行机 CPU", "high", f"CPU 平均 {cpu_usage:.1f}% 超过 {THRESHOLD_CPU_HIGH}%", f"CPU平均={cpu_usage:.1f}%"])
bottleneck_content_parts.append(f"执行机 CPU 瓶颈:平均使用率 {cpu_usage:.1f}%。")
elif cpu_max is not None and cpu_max > THRESHOLD_CPU_HIGH:
bottleneck_rows.append(["资源", "执行机 CPU(峰值)", "medium", f"CPU 峰值 {cpu_max:.1f}% 超过 {THRESHOLD_CPU_HIGH}%", f"CPU峰值={cpu_max:.1f}%"])
bottleneck_content_parts.append(f"执行机 CPU 峰值达 {cpu_max:.1f}%,需关注。")
if mem_usage is not None and mem_usage > THRESHOLD_MEMORY_HIGH:
bottleneck_rows.append(["资源", "执行机内存", "high", f"内存 {mem_usage:.1f}% 超过 {THRESHOLD_MEMORY_HIGH}%", f"内存={mem_usage:.1f}%"])
bottleneck_rows.append(["资源", "执行机内存", "high", f"内存平均 {mem_usage:.1f}% 超过 {THRESHOLD_MEMORY_HIGH}%", f"内存平均={mem_usage:.1f}%"])
bottleneck_content_parts.append(f"执行机内存瓶颈:平均使用率 {mem_usage:.1f}%。")
elif mem_max is not None and mem_max > THRESHOLD_MEMORY_HIGH:
bottleneck_rows.append(["资源", "执行机内存(峰值)", "medium", f"内存峰值 {mem_max:.1f}% 超过 {THRESHOLD_MEMORY_HIGH}%", f"内存峰值={mem_max:.1f}%"])
bottleneck_content_parts.append(f"执行机内存峰值达 {mem_max:.1f}%,需关注。")
if target_cpu is not None and target_cpu > THRESHOLD_CPU_HIGH:
bottleneck_rows.append(["资源", "目标机 CPU", "high", f"CPU {target_cpu:.1f}% 超过 {THRESHOLD_CPU_HIGH}%", f"CPU={target_cpu:.1f}%"])
bottleneck_rows.append(["资源", "目标机 CPU", "high", f"CPU 平均 {target_cpu:.1f}% 超过 {THRESHOLD_CPU_HIGH}%", f"CPU平均={target_cpu:.1f}%"])
bottleneck_content_parts.append(f"目标机 CPU 瓶颈:平均使用率 {target_cpu:.1f}%。")
elif target_cpu_max is not None and target_cpu_max > THRESHOLD_CPU_HIGH:
bottleneck_rows.append(["资源", "目标机 CPU(峰值)", "medium", f"CPU 峰值 {target_cpu_max:.1f}% 超过 {THRESHOLD_CPU_HIGH}%", f"CPU峰值={target_cpu_max:.1f}%"])
bottleneck_content_parts.append(f"目标机 CPU 峰值达 {target_cpu_max:.1f}%,需关注。")
if target_mem is not None and target_mem > THRESHOLD_MEMORY_HIGH:
bottleneck_rows.append(["资源", "目标机内存", "high", f"内存 {target_mem:.1f}% 超过 {THRESHOLD_MEMORY_HIGH}%", f"内存={target_mem:.1f}%"])
bottleneck_rows.append(["资源", "目标机内存", "high", f"内存平均 {target_mem:.1f}% 超过 {THRESHOLD_MEMORY_HIGH}%", f"内存平均={target_mem:.1f}%"])
bottleneck_content_parts.append(f"目标机内存瓶颈:平均使用率 {target_mem:.1f}%。")
elif target_mem_max is not None and target_mem_max > THRESHOLD_MEMORY_HIGH:
bottleneck_rows.append(["资源", "目标机内存(峰值)", "medium", f"内存峰值 {target_mem_max:.1f}% 超过 {THRESHOLD_MEMORY_HIGH}%", f"内存峰值={target_mem_max:.1f}%"])
bottleneck_content_parts.append(f"目标机内存峰值达 {target_mem_max:.1f}%,需关注。")
if not bottleneck_rows:
bottleneck_content_parts.append("本次压测未发现超过阈值的明显瓶颈,系统在当前压力下表现稳定。")
bottleneck_rows.append(["—", "—", "—", "未发现超过阈值的瓶颈", "—"])
......@@ -1644,18 +1690,17 @@ class PerformanceAiService:
"evidence": "错误率 = {:.2f}%(阈值 {}%)".format(error_rate, THRESHOLD_ERROR_RATE),
})
# 资源瓶颈
cpu_usage = None
mem_usage = None
target_cpu = None
target_mem = None
if resource_summary:
cpu_usage = resource_summary.get("cpu_avg") or resource_summary.get("cpu_average")
mem_usage = resource_summary.get("memory_avg") or resource_summary.get("memory_average")
if target_resource_summary:
target_cpu = target_resource_summary.get("cpu_avg") or target_resource_summary.get("cpu_average")
target_mem = target_resource_summary.get("memory_avg") or target_resource_summary.get("memory_average")
# 资源瓶颈(兼容 stats 嵌套与旧平铺键,同时取平均/峰值)
exec_stats = self._extract_resource_stats(resource_summary)
tgt_stats = self._extract_resource_stats(target_resource_summary)
cpu_usage = exec_stats["cpu_avg"]
cpu_max = exec_stats["cpu_max"]
mem_usage = exec_stats["mem_avg"]
mem_max = exec_stats["mem_max"]
target_cpu = tgt_stats["cpu_avg"]
target_cpu_max = tgt_stats["cpu_max"]
target_mem = tgt_stats["mem_avg"]
target_mem_max = tgt_stats["mem_max"]
if cpu_usage is not None and cpu_usage > THRESHOLD_CPU_HIGH:
bottlenecks.append({
......@@ -1689,6 +1734,39 @@ class PerformanceAiService:
"description": "目标机内存平均使用率 {:.1f}%,超过 {}% 阈值".format(target_mem, THRESHOLD_MEMORY_HIGH),
"evidence": "目标机内存使用率 = {:.1f}%(阈值 {}%)".format(target_mem, THRESHOLD_MEMORY_HIGH),
})
# 峰值资源瓶颈(平均未超阈值但峰值超阈时提醒)
if (cpu_usage is None or cpu_usage <= THRESHOLD_CPU_HIGH) and cpu_max is not None and cpu_max > THRESHOLD_CPU_HIGH:
bottlenecks.append({
"type": "资源",
"location": "执行机 CPU(峰值)",
"severity": "medium",
"description": "执行机 CPU 峰值使用率 {:.1f}%,超过 {}% 阈值".format(cpu_max, THRESHOLD_CPU_HIGH),
"evidence": "CPU 峰值 = {:.1f}%(阈值 {}%)".format(cpu_max, THRESHOLD_CPU_HIGH),
})
if (mem_usage is None or mem_usage <= THRESHOLD_MEMORY_HIGH) and mem_max is not None and mem_max > THRESHOLD_MEMORY_HIGH:
bottlenecks.append({
"type": "资源",
"location": "执行机内存(峰值)",
"severity": "medium",
"description": "执行机内存峰值使用率 {:.1f}%,超过 {}% 阈值".format(mem_max, THRESHOLD_MEMORY_HIGH),
"evidence": "内存峰值 = {:.1f}%(阈值 {}%)".format(mem_max, THRESHOLD_MEMORY_HIGH),
})
if (target_cpu is None or target_cpu <= THRESHOLD_CPU_HIGH) and target_cpu_max is not None and target_cpu_max > THRESHOLD_CPU_HIGH:
bottlenecks.append({
"type": "资源",
"location": "目标机 CPU(峰值)",
"severity": "medium",
"description": "目标机 CPU 峰值使用率 {:.1f}%,超过 {}% 阈值".format(target_cpu_max, THRESHOLD_CPU_HIGH),
"evidence": "目标机 CPU 峰值 = {:.1f}%(阈值 {}%)".format(target_cpu_max, THRESHOLD_CPU_HIGH),
})
if (target_mem is None or target_mem <= THRESHOLD_MEMORY_HIGH) and target_mem_max is not None and target_mem_max > THRESHOLD_MEMORY_HIGH:
bottlenecks.append({
"type": "资源",
"location": "目标机内存(峰值)",
"severity": "medium",
"description": "目标机内存峰值使用率 {:.1f}%,超过 {}% 阈值".format(target_mem_max, THRESHOLD_MEMORY_HIGH),
"evidence": "目标机内存峰值 = {:.1f}%(阈值 {}%)".format(target_mem_max, THRESHOLD_MEMORY_HIGH),
})
# 优化建议
suggestions = []
......@@ -1726,21 +1804,21 @@ class PerformanceAiService:
# 资源分析
resource_parts = []
if cpu_usage is not None:
resource_parts.append("**执行机 CPU**:平均使用率 {:.1f}%{}".format(
cpu_usage, " ⚠️ 超过阈值" if cpu_usage > THRESHOLD_CPU_HIGH else ""
))
peak_txt = ",峰值 {:.1f}%".format(cpu_max) if cpu_max is not None else ""
warn = " ⚠️ 超过阈值" if cpu_usage > THRESHOLD_CPU_HIGH else ""
resource_parts.append("**执行机 CPU**:平均使用率 {:.1f}%{}{}".format(cpu_usage, peak_txt, warn))
if mem_usage is not None:
resource_parts.append("**执行机内存**:平均使用率 {:.1f}%{}".format(
mem_usage, " ⚠️ 超过阈值" if mem_usage > THRESHOLD_MEMORY_HIGH else ""
))
peak_txt = ",峰值 {:.1f}%".format(mem_max) if mem_max is not None else ""
warn = " ⚠️ 超过阈值" if mem_usage > THRESHOLD_MEMORY_HIGH else ""
resource_parts.append("**执行机内存**:平均使用率 {:.1f}%{}{}".format(mem_usage, peak_txt, warn))
if target_cpu is not None:
resource_parts.append("**目标机 CPU**:平均使用率 {:.1f}%{}".format(
target_cpu, " ⚠️ 超过阈值" if target_cpu > THRESHOLD_CPU_HIGH else ""
))
peak_txt = ",峰值 {:.1f}%".format(target_cpu_max) if target_cpu_max is not None else ""
warn = " ⚠️ 超过阈值" if target_cpu > THRESHOLD_CPU_HIGH else ""
resource_parts.append("**目标机 CPU**:平均使用率 {:.1f}%{}{}".format(target_cpu, peak_txt, warn))
if target_mem is not None:
resource_parts.append("**目标机内存**:平均使用率 {:.1f}%{}".format(
target_mem, " ⚠️ 超过阈值" if target_mem > THRESHOLD_MEMORY_HIGH else ""
))
peak_txt = ",峰值 {:.1f}%".format(target_mem_max) if target_mem_max is not None else ""
warn = " ⚠️ 超过阈值" if target_mem > THRESHOLD_MEMORY_HIGH else ""
resource_parts.append("**目标机内存**:平均使用率 {:.1f}%{}{}".format(target_mem, peak_txt, warn))
if not resource_parts:
resource_parts.append("无资源监控数据,建议开启目标机/执行机资源监控以获得更全面的分析")
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论