登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go test -json 怎样把测试事件导出给日志系统

来源:17golang原创

时间:2026-10-28 22:44:35 152浏览 收藏

在项目根目录执行 go test -json ./...,把 stdout 保存成 NDJSON 文件,再让日志采集器按“一行一个 JSON 对象”读取,就能导出 Go 测试事件。实际接入时还应单独保存 stderr,并保留 go test 的退出码,否则日志上传成功可能掩盖测试失败。

官方地址:https://pkg.go.dev/cmd/go

# 在当前模块导出事件;stderr 单独存放,避免污染 JSON 文件。
go test -json -count=1 ./... > test-events.ndjson 2> test-command.stderr

这条命令适合手动开始;在 CI 中需要下面的完整退出码处理。本文把接入方式从“解析人类文本”调整为“消费结构化事件”,借用提案解读的方式说明改动理由和兼容成本,不将现有参数说成刚发布的新能力。

为什么应替换按 PASS 文本搜索的旧采集方式

普通测试输出方便人读,但用 grep PASS 决定成败,会把测试打印的文字、单个测试结果和整个包的结果混在一起。多包同时测试时,按屏幕位置猜测消息属于哪个测试也不可靠。

事件采集应保留两个层次:进程退出码决定本次命令结果,结构化事件用于检索与解释。日志系统负责索引,不应成为另一套用字符串猜测结果的测试框架。建议把原始 NDJSON 当作可重放的输入,在额外字段中加入 CI 运行信息,而不是修改 Go 自带字段的含义。

新接入规则:一行一条,先看 Action 再解释字段

官方 test2json 文档定义了逐行测试事件。Action 描述事件类别,例如 run、output、pass、fail。Package 标识测试包,Test 可标识具体测试;包级事件没有 Test。采集器应根据实际事件是否包含字段解释它,不能要求所有记录都有测试名和耗时。

当前 Go 命令文档还规定构建事件会混入此流,以 build-output、build-fail 区分,构建包标识使用 ImportPath。它不是 TestEvent.Package 的简单替代。处理构建失败时应保留这一标识,避免把失败依赖错误地归到某个测试函数。

检索用途保留字段或外加字段接入规则
本次 CI 执行run_id由 CI 提供,区别不同运行
事件类别Action保留原值,未知值也保留原事件
测试归属Package、Test缺少 Test 时不要随意补前一个测试名
构建归属ImportPath保留为独立字段
可读消息Output保留字符串,不用其内容推断成败
命令诊断独立 stderr 文件、退出码作为同次运行的另一类记录归档
日志信封与测试和构建事件字段的静态结构
结构说明图:原始事件与运行标识分层保存,测试归属字段和构建归属字段各有作用域;连线表示字段组成关系。

代码对比:导出日志后仍让 CI 收到失败

下面是可保存为 collect-tests.sh 的 Bash 示例。调用时给它一个本次运行专用的输出目录,例如 bash collect-tests.sh artifacts/job-472-attempt-1。路径只是示例,应由 CI 生成唯一目录,避免并发任务互相覆盖。

#!/usr/bin/env bash
# 输出目录由 CI 分配;每次运行使用独立目录。
set -u
set -o pipefail

out_dir="${1:?请提供本次运行的独立输出目录}"
mkdir -p "$out_dir" || exit 2

# 记录工具链版本;记录失败时停止,不伪装采集成功。
go version > "$out_dir/go-version.txt" 2>&1 || exit 2

# if 分支允许获取真实退出码,不受调用环境的 errexit 干扰。
if go test -json -count=1 ./... \
    > "$out_dir/events.ndjson" 2> "$out_dir/command.stderr"; then
    test_rc=0
else
    test_rc=$?
fi

# 单独保存命令结果;没有 JSON 事件时也有可诊断的记录。
printf '%s\n' "$test_rc" > "$out_dir/test-exit-code.txt" || exit 2

# 由 CI 的始终执行步骤归档上述文件,然后使用这里的状态判定测试结果。
exit "$test_rc"

与把命令直接管道接到上传器相比,这个版本先落盘,明确保留命令状态。它没有调用任何真实日志服务,也没有隐含“导出即上传”的行为。CI 需要把归档或日志采集设置为失败后仍执行;如果测试失败便取消后续所有步骤,文件虽然存在,也不会自动进入日志系统。

不要把 2>&1 放到事件文件的采集命令中。官方文档提示,某些早期严重错误仍可能以非 JSON 文字写到命令 stderr。测试自身的输出又可能作为事件的 Output 存在,两者不能据“都是错误文字”合并解析。

给日志系统添加信封,而不覆盖原始事件

如果现有采集器能直接解析 NDJSON,可配置外加运行字段后采集原文件。需要预处理时,下面的 jq 示例生成独立文件,要求已安装 jq;CI_RUN_ID 由 CI 注入,不是 Go 事件自带字段。

# 为每条事件外包运行标识,原始对象完整留在 event 字段。
# out_dir 指向刚才采集目录;以下转换与上传需在失败后仍执行的 CI 步骤中运行。
out_dir="artifacts/job-472-attempt-1"
jq -c --arg run_id "${CI_RUN_ID:?请设置本次运行标识}" \
  '{source:"go-test", run_id:$run_id, event:.}' \
  "$out_dir/events.ndjson" > "$out_dir/log-events.ndjson"

这里没有使用 -s,不会先把整份文件聚合成一个 JSON 数组。日志系统按每行解码后,可将 event.Action 建成类别字段;查询具体测试时组合 run_id、event.Package 和 event.Test。同一测试在一次执行中重复运行时,这组字段只适合检索归组,不能直接充当事件唯一键。

若要进行重试去重,可由采集侧保存文件身份与原始记录序号,具体索引方案由日志系统决定。不要拿采集时间覆盖事件时间,也不要因为某条记录缺少时间就丢弃它。stderr、命令退出码和事件索引都应带相同 run_id,以便回到同一次 CI 执行。

测试采集目录与日志归档责任之间的静态关系
结构说明图:事件文件、命令 stderr 和退出码文件属于同次运行;CI 始终执行步骤负责归档,日志系统可通过 run_id 关联这些对象。

兼容注意:并行、缓存与新增事件

多包与并行测试的事件可能交错,接入器不能把整个文件当成单个测试连续文本。若需要还原可读输出,应按所需范围保留 Output 片段顺序,而不是按字母重新排序。JSON 字符串中的转义换行不应被采集器当成新的外层记录切开。

示例用 -count=1 避免使用缓存的成功测试结果;它不等于禁用构建缓存,也不等于覆盖了所有测试场景。若保留缓存行为,需要接受缓存记录可能省略时间这一情况,另存采集时间时明确其不同含义。

不同工具链的字段和构建诊断形式可能变化。接入解析应容忍额外字段与未知 Action,归档实际 Go 版本;碰到不能解析的原始记录应单独记录采集错误并保存原文,不能静默跳过后宣布完整导出。命令退出码非零时,检查 stderr 和构建事件,即使没有任何具体测试 fail 事件也不能算测试通过。

还需要再管道到 go tool test2json 吗?

正常使用 go test -json 时不需要重复转换。官方建议优先使用这一入口;单独运行测试二进制时才考虑 test2json 的独立用法。

能只统计所有 fail 事件得到失败测试数吗?

不能直接这样统计。包级失败与具体测试失败作用域不同,构建失败也需分开,先定义统计对象再聚合。

日志平台收到了数据,CI 就可以返回成功吗?

不可以。上传是否成功和测试是否成功是两项结果。保留测试原退出码;上传失败则另外报告采集问题。建议先采用原始文件归档,再增量加入字段索引和可视化。

参考:Go test 参数与构建事件格式、test2json 事件格式。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>