十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Aspire GitHub Actions 失败分析工具链:DownloadFailingJobLogs 与 Heartbeat 实战指南

Aspire GitHub Actions 失败分析工具链:DownloadFailingJobLogs 与 Heartbeat 实战指南 Aspire GitHub Actions 失败分析工具链DownloadFailingJobLogs 与 Heartbeat 实战指南【免费下载链接】aspireAspire is the tool for code-first, extensible, observable dev and deploy.项目地址: https://gitcode.com/GitHub_Trending/as/aspire本文面向 .NET Aspire 开源仓库的贡献者与 CI 维护者系统讲解 tools/scripts 目录下两个关键诊断工具DownloadFailingJobLogs.cs自动下载 GitHub Actions 失败任务日志与测试产物与Heartbeat.cs跨平台系统资源监控用于定位 Runner 挂起问题。读完本文你将掌握如何从一次失败的 CI 运行中快速提取失败测试、错误堆栈与.trx测试结果也能学会在 workflow 中嵌入心跳监控以诊断 Runner 挂起与磁盘空间问题。目录背景一组面向 CI 故障排查的脚本工具在 Aspire 这样规模庞大的仓库中CI 每天会运行成千上万个测试任务失败信息散落在 GitHub Actions 的日志与产物中手动排查效率极低。tools/scripts 目录专门存放面向 GitHub Actions 故障分析与诊断的脚本目前包含DownloadFailingJobLogs.cs —— 基于 .NET 的文件式程序file-based program自动下载失败任务的日志与测试产物Heartbeat.cs —— 跨平台系统心跳监控持续输出 CPU、内存、网络、磁盘与 Docker 统计replace-text.cs —— 文本替换辅助脚本README 未展开说明可自行查阅源码。其中前两个工具由 tools/scripts/README.md 完整记录它们都运行在 .NET 10 之上并通过#:project指令引用 tools/Aspire.TestTools/Aspire.TestTools.csprojTargetFramework为net10.0复用了其中的 GitHub API 封装类。DownloadFailingJobLogs自动拉取失败任务日志与产物它能做什么该工具面向快速调查 GitHub Actions 测试失败这一场景通过一条命令自动完成以下工作找出 GitHub Actions workflow run 中所有失败的任务job下载每个失败任务的原始日志用正则从日志中提取失败测试名、错误消息与异常堆栈依据 workflow 的命名约定从任务名推断产物artifact名称下载包含.trx文件与测试日志的测试产物将产物解压到本地目录供进一步检查。前置条件条件说明.NET SDK.NET 10 SDK 或更高版本GitHub CLIgh已安装并完成认证gh auth login仓库权限能够访问 microsoft/aspire 仓库或目标仓库由于工具底层完全通过gh api调用 GitHub REST API认证态、权限模型与ghCLI 完全一致只要本机gh能访问的目标仓库工具就能读取。使用方法以 GitHub Actions run ID 作为唯一参数运行dotnet tools/scripts/DownloadFailingJobLogs.cs run-id示例dotnet tools/scripts/DownloadFailingJobLogs.cs 19846215629run ID 可以从 GitHub Actions 的 URL 中获取即/actions/runs/之后的那一段数字https://github.com/microsoft/aspire/actions/runs/19846215629 ^^^^^^^^^^ run ID注意源码入口处还会对参数做严格校验——参数缺失时打印用法提示参数无法被long.TryParse解析时输出Invalid run id run-id.并退出避免把非法输入直接透传给 GitHub API。输出产物工具会在当前工作目录生成以下文件failed_job_n_job-name.log—— GitHub Actions 原始任务日志artifact_n_testname_os.zip—— 下载的产物压缩包artifact_n_testname_os/—— 解压后的产物目录内含.trx测试结果文件测试日志构建二进制日志.binlogDCPDistributed Application诊断日志。日志文件名中的job-name经过正则清洗[^a-zA-Z0-9_-]一律替换为_因此即使任务名包含空格、斜杠或括号也能安全落盘。最终输出文件全部存放在当前目录便于后续批量归档或上传。真实运行输出示例Finding failed jobs for run 19846215629... Found 100 total jobs Found 1 failed jobs Failed Job 1/1 Name: Tests / Integrations macos (Hosting.Azure) / Hosting.Azure (macos-latest) ID: 56864254427 URL: https://github.com/microsoft/aspire/actions/runs/19846215629/job/56864254427 Downloading job logs... Saved job logs to: failed_job_0_Tests___Integrations_macos__Hosting_Azure____Hosting_Azure__macos-latest_.log (354209 characters) Searching for test failures in job logs... Errors found (2): - System.InvalidOperationException: Step provision-api-service-website failed: No output for AZURE_APP_SERVICE_DASHBOARD_URI ... Attempting to download artifact: logs-Hosting.Azure-macos-latest Found 282 total artifacts Found artifact ID: 4732859962 Downloaded artifact to: artifact_0_Hosting.Azure_macos-latest.zip Extracted artifact to: artifact_0_Hosting.Azure_macos-latest Found 1 .trx file(s): - artifact_0_Hosting.Azure_macos-latest/testresults/Hosting.Azure_net8.0_20251202034715.trx Summary Total jobs: 100 Failed jobs: 1 Logs downloaded: 1 All logs saved in current directory with pattern: failed_job_*.log从输出可见工具对失败测试与错误消息做了数量截断失败测试最多列出 5 条超出显示... and N more错误最多列出 3 条且单条错误超过 200 字符时截断显示——这是源码中刻意的设计避免海量错误刷屏同时保证最关键的信息可读。工作原理七步流水线DownloadFailingJobLogs.cs 的执行流程可拆解为七个阶段与 README 描述一一对应任务发现Job Discovery通过gh api查询 workflow run 的全部任务并手动分页以处理 200 产物的大规模运行失败检测Failure Detection按conclusion failure不区分大小写过滤失败任务日志下载Log Download通过 GitHub API 下载每个失败任务的原始日志错误提取Error Extraction用正则模式定位失败测试名、错误消息与异常堆栈产物匹配Artifact Matching解析任务名得到产物名命名模式为logs-{testShortName}-{os}产物下载Artifact Download下载包含测试结果的匹配产物解压Extraction使用System.IO.Compression.ZipFile解压产物。源码级深入关键实现细节分页与 API 封装分页逻辑位于 tools/Aspire.TestTools/GitHubActionsApi.csListJobsAsync与ListArtifactsAsync都以per_page100逐页拉取当返回数组长度小于 100 时停止若不足 100 条时提前终止则无需额外 API 调用。ListJobsAsync还支持可选的runAttempt参数对应 API 路径/actions/runs/{runId}/attempts/{attempt}/jobs为重试re-run场景预留了能力。日志下载中的 ANSI 转义序列处理一个容易踩坑的细节CLI 端到端测试的日志内嵌了原始终端录制内容经常包含 ANSI 控制字符。gh api在非 TTY 的 stdout 上会拒绝输出这类内容并直接报错the response contains terminal escape sequences; pass --allow-escape-sequences to output it anyway因此 GitHubActionsApi.cs 在下载任务日志时显式传入了allowEscapeSequences: true由 GitHubCli.cs 拼装--allow-escape-sequences参数。若缺少这一处理任何终端驱动的测试失败都会导致工具无法归档日志。错误提取的三种正则源码中的提取模式非常具体模式正则用途失败测试Failed\s(.?)\s*\[匹配Failed 测试名 [耗时]形式的测试失败行错误消息Error Message:\s*(.?)(?:\r?\n\|$)匹配 xUnit 输出中的Error Message:行异常堆栈(System\.\wException:.?)(?:\r?\n at\|\r?\n\r?\n\|$)匹配以System.*Exception开头的异常及首帧堆栈失败测试名会做去重Distinct忽略大小写异常若与已有错误重复则不再追加保证输出整洁。安全防护Zip Slip 防护README 未提及、但源码中非常重要的一处是ValidateZipEntries静态方法解压前先遍历 zip 内每个条目用Path.GetFullPath计算目标路径若不以目标目录为前缀则抛出InvalidOperationExceptionZip entry ... would extract outside the target directory有效防御 Zip Slip 路径穿越攻击——这在 CI 环境下尤其重要因为产物内容可能来自不可信的构建环境。解压时使用ZipFile.ExtractToDirectory(..., overwriteFiles: true)并在解压前删除同名旧目录。复用库与测试夹具API 调用统一收敛在 tools/Aspire.TestTools/GitHubCli.cs 中所有gh子进程默认 5 分钟超时超时后Kill(entireProcessTree: true)产物下载通过流式写入文件实现先写文件再在失败时删除以规避 Windows 文件句柄占用问题。此外GitHubCli支持通过环境变量ASPIRE_FAILING_TEST_ISSUE_FIXTURE_DIR注入本地夹具来模拟gh api的响应这让工具可以在完全没有网络的环境中被端到端测试。故障排查没有发现失败任务No failed jobs found核对 run ID 是否正确确认该 workflow run 中确实存在失败任务确保对目标仓库有访问权限。产物未找到Artifact not found产物可能已过期GitHub Actions 产物通常保留 90 天任务可能未上传产物例如在测试运行前就失败。权限被拒Permission denied先执行gh auth login完成认证确认对目标仓库有读权限。Heartbeat跨平台系统心跳监控它能做什么Heartbeat.cs是一个跨平台的系统监控工具按固定间隔输出 CPU、内存、网络、磁盘与 Docker 统计专为诊断 GitHub Actions Runner 在测试期间的挂起问题设计。其核心能力包括CPU 监控—— 系统级 CPU 利用率百分比内存追踪—— 已用/总内存及百分比网络连接计数—— ESTABLISHED、LISTEN、TIME_WAIT 三类连接数Docker 统计—— 容器数量及聚合 CPU/内存占用DCP 进程追踪—— 查找所有以 dcp 开头的进程并报告其内存占用Top 进程—— 列出 CPU 占用最高的进程源码新增能力磁盘空间—— 关键挂载点/盘符的使用率源码新增能力。说明README 中的工具简介只覆盖前 5 项但从源码看GetDiskUsage与GetTopProcesses已实际实现并被纳入每条心跳输出用于诊断 Runner 挂起与磁盘空间耗尽两类典型问题。前置条件.NET 10 SDK 或更高版本Docker可选仅 Linux 下容器统计需要。使用方法# 使用默认间隔 dotnet tools/scripts/Heartbeat.cs # 自定义 10 秒间隔 dotnet tools/scripts/Heartbeat.cs 10间隔参数说明以源码为准README 示例写作默认 5 秒但 Heartbeat.cs 实际定义的默认间隔为60 秒传入参数需为 1的正整数否则回退到默认值。读者在复用时建议显式传入间隔例如dotnet tools/scripts/Heartbeat.cs 5避免以为默认很快、实际 60 秒才打点一次的错觉。输出格式[2025-12-12T10:30:00Z] HEARTBEAT | Starting system monitor (interval: 5s) [2025-12-12T10:30:00Z] HEARTBEAT | Platform: Linux 5.15.0-1053-azure [2025-12-12T10:30:00Z] HEARTBEAT | CPU: 45.2% | Mem: 4.2/8.0 GB (52%) | Net: 24 est, 12 listen, 5 tw | Docker: 3 containers (CPU: 120.5%, Mem: 45.2%) | DCP: none [2025-12-12T10:30:05Z] HEARTBEAT | CPU: 67.8% | Mem: 5.1/8.0 GB (64%) | Net: 28 est, 12 listen, 8 tw | Docker: 3 containers (CPU: 156.2%, Mem: 52.1%) | DCP: 2 procs (245MB) [dcp(1234):120MB, dcp-api(5678):125MB]每条心跳使用 ISO 8601 时间戳UTC各指标以|分隔。值得注意的两点源码细节每行输出后立即Console.Out.Flush()脚本顶部注释明确指出Disable output buffering for real-time visibility in CI logs保证在 CI 日志流中每个心跳点实时可见不会因缓冲堆积在进程结束时才一次性刷出——这对观察测试卡在哪个时间点至关重要CPU 首轮显示calculating...CPU 利用率需要两次采样计算差值因此第一条心跳的 CPU 字段是占位符属预期行为。此外完整的输出还包含Disk:与Top:两个字段GetDiskUsage与GetTopProcesses的实现分别报告关键挂载点使用率和 CPU 占用最高的进程。GitHub Actions 集成完整 YAML 示例在 workflow 开始时启动心跳、结束时停止并打印日志是最典型的用法Linux/macOS- name: Start heartbeat monitor run: | nohup dotnet tools/scripts/Heartbeat.cs heartbeat.log 21 echo $! heartbeat.pid # ... run tests ... - name: Stop heartbeat and show logs if: always() run: | if [ -f heartbeat.pid ]; then kill $(cat heartbeat.pid) 2/dev/null || true fi echo Heartbeat Log cat heartbeat.log 2/dev/null || echo No heartbeat log foundWindows- name: Start heartbeat monitor shell: pwsh run: | Start-Process -FilePath dotnet -ArgumentList tools/scripts/Heartbeat.cs -RedirectStandardOutput heartbeat.log -NoNewWindow # ... run tests ... - name: Stop heartbeat and show logs if: always() shell: pwsh run: | Get-Process -Name dotnet -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -like *Heartbeat* } | Stop-Process -Force -ErrorAction SilentlyContinue if (Test-Path heartbeat.log) { Get-Content heartbeat.log }两个关键设计其一停止步骤都使用if: always()确保即使测试步骤失败也能回收心跳进程并输出日志其二Linux 侧用nohup ... 加 PID 文件管理后台进程Windows 侧用Start-Process启动、按命令行特征匹配后强制停止。平台支持与实现机制平台CPU内存网络DockerDCP / Top 进程Linux解析/proc/stat解析/proc/meminfonetstatdocker statsps auxmacOStop -l 1vm_statsysctlnetstatdocker statsps auxWindowsP/InvokeGetSystemTimesP/InvokeGlobalMemoryStatusExnetstatdocker statsProcess.GetProcesses()README 的平台表标注 Windows 使用wmic但当前源码已全面改用 P/InvokeNativeMethods.GetSystemTimes与GlobalMemoryStatusEx直接调用kernel32.dll磁盘则用DriveInfo.GetDrives()——源码注释明确说明这样做的动机是不再派生 PowerShell 子进程no external process spawns needed既减少开销又规避 CI 环境的进程策略限制。CPU 计算上Linux 读取/proc/stat的cpu行user/nice/system/idle/iowait 等字段求和Windows 按kernel user为 total、total - idle为 busy 计算macOS 则解析top输出的 idle 百分比后取100 - idle。RunCommand辅助方法统一管理外部命令执行重定向 stdout/stderr、超时默认 3 秒Docker 查询放宽到 5~10 秒、超时后Kill(entireProcessTree: true)并返回(false, timeout, )。每个指标都有独立的 try/catch单个指标失败不影响整条心跳输出——这正是Stats 显示 N/A/unavailable这类兜底行为的来源。优雅关闭心跳进程对以下信号做出响应并打印HEARTBEAT | Monitor stopped后退出CtrlCSIGINT—— 通过Console.CancelKeyPress取消进程终止SIGTERM—— 通过AppDomain.CurrentDomain.ProcessExit取消父进程退出。实现上统一通过CancellationTokenSource驱动主循环检查cts.Token.IsCancellationRequestedTask.Delay抛出的OperationCanceledException被捕获后正常退出保证日志完整收尾。故障排查CPU 首轮显示 calculating...属预期行为。CPU 利用率需要两次采样计算差值。Docker 显示 unavailableDocker 守护进程未运行当前用户无访问 Docker socket 的权限Docker CLI 未安装。统计显示 N/A底层命令失败或超时检查所需工具netstat、ps 等是否可用。DCP 显示 none当前没有以 dcp 开头的进程在运行Aspire 测试未运行时这是正常现象。测试与验证工具自身的质量保障这两个工具并非一次性脚本而是有端到端测试护航的工程化工具。测试位于 tests/Infrastructure.Tests/DownloadFailingJobLogs/DownloadFailingJobLogsToolTests.cs通过 xUnit 的IClassFixture定位脚本与仓库内 dotnet hostDownloadFailingJobLogsFixture.cs 使用仓库根目录的 dotnet.sh 运行工具DownloadsLogsAndExtractsArtifactsFromFixtureRun注入包含任务、产物、日志文本与 zip 的夹具断言工具能输出Found 1 failed jobs、落盘failed_job_0_*.log、下载并解压产物、识别到.trx文件——覆盖完整的成功路径ReportsMissingArtifactWithoutFailing构造产物缺失的夹具断言工具输出Artifact logs-Sample-linux-latest not found for this run.且进程退出码为 0——保证单个任务缺产物不会让整个工具崩溃。测试通过环境变量ASPIRE_FAILING_TEST_ISSUE_FIXTURE_DIR指向夹具目录利用 GitHubCli.cs 的 fixture 解析逻辑按端点路径哈希成文件名分别匹配.json/.txt/.err/.bin后缀离线模拟 GitHub API 响应zip 夹具由 tests/Infrastructure.Tests/Shared/TestTrxBuilder.cs 构建可生成带失败用例、错误消息与堆栈的标准.trx文件。小结把 CI 失败排查从手动翻日志变成一条命令DownloadFailingJobLogs把发现失败任务 → 下载日志 → 提取错误 → 匹配并下载产物 → 解压.trx整条链路自动化配合--allow-escape-sequences与 Zip Slip 防护是处理大规模 CI 失败的第一件武器Heartbeat以 60 秒可配间隔持续输出系统资源快照配合if: always()的停止步骤为Runner 何时挂起、磁盘何时打满、DCP 进程是否泄漏内存提供了时间线上的关键证据两者都由 tools/Aspire.TestTools 库支撑、由 tests/Infrastructure.Tests 中的端到端测试守护可作为仓库其他故障分析工具的参考范式GitHubCli还提供了创建 Issue、搜索已有失败测试 Issue、重开与评论等能力服务于更完整的失败自动上报链路。如果你正在维护 Aspire 仓库的 CI或在自己的 GitHub Actions 项目中遭遇测试挂起、失败信息难查的困境这两把工具可以直接移植复用。【免费下载链接】aspireAspire is the tool for code-first, extensible, observable dev and deploy.项目地址: https://gitcode.com/GitHub_Trending/as/aspire创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表