为什么需要定制化报告?
JMeter生成的HTML报告虽然功能完善,但关键信息不突出、展示不直观
如何实现
编写一个符合预期的shell脚本,在jenkins中通过shell步骤执行shell脚本来生成定制化html报告,后续可以通过归档至主节点,来进行展示等
shell脚本逻辑流程图:
shell脚本:
#!/bin/bash
# 【配置区】与 Jenkins HTML Publisher 配置对齐
# 工作空间根路径
WORKSPACE="/work/jenkins/workspace/Automated-testing/AgentJmeter-PublicAgent-release"
# JMeter 相关路径
JMETER_JMX="$WORKSPACE/PublicAgent-release.jmx" # 测试计划文件
JTL_FILE="$WORKSPACE/PublicAgent-release.jtl" # JMeter 结果文件
HTML_REPORT_DIR="$WORKSPACE/html_report" # JMeter 默认 HTML 报告输出目录
# 自定义报告路径(与 HTML Publisher 配置一致)
CUSTOM_REPORT_DIR="$WORKSPACE/result_html"
CUSTOM_HTML="$CUSTOM_REPORT_DIR/resultTotal.html" # 索引页:resultTotal.html
# JTL 文件字段列号(从 1 开始,与实际 JTL 表头严格对齐)
SUCCESS_COL=8 # success 字段所在列(第 8 列)
AGENT_NAME_COL=6 # 智能体名称所在列(threadName 列,第 6 列)
RESPONSE_MSG_COL=5 # responseMessage 字段所在列(第 5 列)
FAILURE_MSG_COL=9 # failureMessage 字段所在列(第 9 列)
# 成功/失败标识值
SUCCESS_VALUE="true"
FAILURE_VALUE="false"
# 【函数区】模块化封装核心逻辑
# 1. 日志打印函数(带时间戳,便于调试)
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 2. 清理旧文件/目录(健壮性处理:rm -rf 避免目录不存在报错)
clean_old_resources() {
log "开始清理旧文件/目录..."
rm -rf "$HTML_REPORT_DIR"/* # 清理 JMeter 默认报告
rm -rf "$JTL_FILE" # 清理旧 JTL 文件
rm -rf "$CUSTOM_REPORT_DIR" # 强制删除自定义报告目录(无则忽略)
mkdir -p "$CUSTOM_REPORT_DIR" # 重建自定义报告目录(确保父目录存在)
log "旧文件/目录清理完成"
}
# 3. 运行 JMeter 生成测试结果 + 默认 HTML 报告
run_jmeter_test() {
log "开始运行 JMeter 测试..."
jmeter -n -t "$JMETER_JMX" \
-l "$JTL_FILE" \
-e \
-o "$HTML_REPORT_DIR"
# 检查 JMeter 执行返回码(非 0 则失败)
if [ $? -ne 0 ]; then
log "× JMeter 执行失败,脚本终止"
exit 1
fi
log "JMeter 测试执行完成,JTL 和默认报告已生成"
}
# 4. 解析 JTL 统计成功/失败数(处理多行字段)
parse_jtl_summary() {
log "开始解析 JTL 统计成功/失败数..."
# 使用 awk 解析 JTL,注意分隔符和引号处理
local result=$(awk -v FPAT='([^,]*)|("[^"]*")' -v s_col="$SUCCESS_COL" -v s_val="$SUCCESS_VALUE" -v f_val="$FAILURE_VALUE" '
NR > 1 {
gsub(/^"|"$/, "", $s_col); # 移除字段前后引号(JTL 带引号时保留)
if ($s_col == s_val) count_success++
else if ($s_col == f_val) count_failure++
}
END {
print count_success+0, count_failure+0 # 强制转数字,避免空值
}' "$JTL_FILE")
# 新增调试日志,查看 awk 输出
log "DEBUG: awk 返回的 success/failure 数: [$result]"
# 按空格分割 awk 输出(awk 用 print 输出时默认空格分隔)
IFS=' ' read -r SUCCESS_COUNT FAILURE_COUNT <<< "$result"
# 计算总数(此时 SUCCESS_COUNT/FAILURE_COUNT 应为纯数字)
TOTAL_COUNT=$((SUCCESS_COUNT + FAILURE_COUNT))
log "成功数: $SUCCESS_COUNT, 失败数: $FAILURE_COUNT, 总数: $TOTAL_COUNT"
}
# 5. 计算成功率
calculate_success_rate() {
log "计算成功率..."
if [ "$TOTAL_COUNT" -eq 0 ]; then
SUCCESS_RATE="0.00"
else
SUCCESS_RATE=$(awk -v s="$SUCCESS_COUNT" -v t="$TOTAL_COUNT" 'BEGIN { printf "%.2f", (s/t)*100 }')
fi
log "成功率: $SUCCESS_RATE%"
}
# 6. 提取失败详情
parse_failure_details() {
awk -v FPAT='([^,]*)|("[^"]*")' -v s_col="$SUCCESS_COL" -v f_val="$FAILURE_VALUE" -v agent_col="$AGENT_NAME_COL" -v msg_col="$FAILURE_MSG_COL" '
NR == 1 { next } # 跳过表头行
{
success_val = $s_col;
gsub(/^"|"$/, "", success_val);
if (success_val == f_val) {
agent_name = $agent_col;
gsub(/^"|"$/, "", agent_name);
split(agent_name, parts, "^");
failure_msg = $msg_col;
gsub(/^"|"$/, "", failure_msg);
gsub(/\n/, "<br>", failure_msg);
gsub(/&/, "&", failure_msg);
gsub(/</, "<", failure_msg);
gsub(/>/, ">", failure_msg);
printf "<tr class=\"failure-row\"><td>%s</td><td>%s</td><td>%s</td></tr>\n", parts[1], parts[2], failure_msg;
}
}' "$JTL_FILE"
}
# 7. 提取成功详情
parse_success_details() {
awk -v FPAT='([^,]*)|("[^"]*")' -v s_col="$SUCCESS_COL" -v s_val="$SUCCESS_VALUE" -v agent_col="$AGENT_NAME_COL" -v msg_col="$RESPONSE_MSG_COL" '
NR == 1 { next } # 跳过表头行
{
success_val = $s_col;
gsub(/^"|"$/, "", success_val);
if (success_val == s_val) {
agent_name = $agent_col;
gsub(/^"|"$/, "", agent_name);
split(agent_name, parts, "^");
response_msg = $msg_col;
gsub(/^"|"$/, "", response_msg);
gsub(/\n/, "<br>", response_msg);
gsub(/&/, "&", response_msg);
gsub(/</, "<", response_msg);
gsub(/>/, ">", response_msg);
printf "<tr class=\"success-row\"><td>%s</td><td>%s</td><td>%s</td></tr>\n", parts[1], parts[2], response_msg;
}
}' "$JTL_FILE"
}
# 8. 生成自定义 HTML 报告
generate_custom_report() {
log "开始生成自定义报告: $CUSTOM_HTML"
# 使用 HEREDOC 生成 HTML 内容,嵌入统计变量和详情
cat <<EOF > "$CUSTOM_HTML"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>执行结果统计</title>
<style>
# css样式
body { font-family: Arial, sans-serif; padding: 20px; background-color: #f9f9f9; }
h2 { color: #333; border-bottom: 2px solid #ddd; padding-bottom: 10px; }
table { border-collapse: collapse; width: 90%; margin: 20px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.1); background-color: white; }
th, td { padding: 12px; text-align: left; border: 1px solid #ddd; }
th { background-color: #f2f2f2; color: #333; font-weight: bold; }
.success-row { background-color: #d4f4dd; color: green; }
.failure-row { background-color: #f8cccc; color: red; }
.total-row { background-color: #e6e6fa; font-weight: bold; }
.rate-row { background-color: #fffacd; }
.details-table { width: 100%; }
.details-table td, .details-table th { vertical-align: top; }
pre { white-space: pre-wrap; word-wrap: break-word; background-color: #f0f0f0; padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
</style>
</head>
<body>
<h2>执行结果统计</h2>
<table>
<tr class="success-row">
<th>统计项</th>
<th>统计值</th>
</tr>
<tr class="success-row">
<td>成功数</td>
<td>$SUCCESS_COUNT</td>
</tr>
<tr class="failure-row">
<td>失败数</td>
<td>$FAILURE_COUNT</td>
</tr>
<tr class="total-row">
<td>总数</td>
<td>$TOTAL_COUNT</td>
</tr>
<tr class="rate-row">
<td>成功率</td>
<td>$SUCCESS_RATE%</td>
</tr>
</table>
<h2>执行结果数据</h2>
<table class="details-table">
<tr>
<th>智能体名称</th>
<th>Query</th>
<th style="width:60%">详细信息</th>
</tr>
$(parse_failure_details)$(parse_success_details)
</table>
</body>
</html>
EOF
# 确保报告文件权限(Jenkins 可访问)
chmod 644 "$CUSTOM_HTML"
log "自定义报告生成完成:$CUSTOM_HTML"
}
# 9. 检验报告文件是否生成
verify_report() {
if [ -f "$CUSTOM_HTML" ]; then
log "报告验证通过:$CUSTOM_HTML 存在"
else
log "错误:$CUSTOM_HTML 生成失败"
exit 1
fi
}
# 【主流程】按顺序执行函数
main() {
clean_old_resources
run_jmeter_test
parse_jtl_summary
calculate_success_rate
generate_custom_report
verify_report
}
# 启动主流程
main