I recently released Meziantou.GitHubActionsTracing, a tool that converts GitHub Actions workflow runs into trace data.
In this post, I show how to use it to debug slow or flaky CI pipelines with timeline-based analysis instead of raw logs only.
#Why GitHub Actions runs are hard to diagnose
GitHub Actions logs are great for understanding what happened, but not for quickly seeing where time is spent.
When a workflow is slow, you usually need answers such as:
- Which job or step consumed most of the total duration?
- Was the build slow, or were tests slow?
- Is this a one-off issue, or a recurring pattern?
- Is it an issue with GitHub infrastructure, or something in my workflow?
With plain logs, this investigation is mostly manual. You jump between jobs, scroll, and estimate durations by hand.
This tool converts a workflow run into trace data so you can inspect it as a timeline, filter noisy spans, and focus on what matters.
#What Meziantou.GitHubActionsTracing exports
The export command accepts:
- A GitHub Actions workflow run URL
- A local run-info folder (or zip) previously downloaded with the tool
The generated trace includes jobs, steps, log groups, warnings and errors, MSBuild targets and tasks from binlog data, and TRX/JUnit test results found in artifacts.
You can then export this model in multiple formats:
- Chromium trace JSON
- Speedscope JSON
- Interactive HTML viewer
- OpenTelemetry (collector or file)
#Installation and requirements
Requirements:
- .NET 10 SDK
- GitHub authentication using
GITHUB_TOKEN or the GitHub CLI (gh token show)
Install the global tool:
Shell
dotnet tool update --global Meziantou.GitHubActionsTracing
#Quick start: export a run to a timeline
Start with a workflow run URL:
Shell
Meziantou.GitHubActionsTracing export https://github.com/OWNER/REPO/actions/runs/123456 --format html
By default, output files are created in the current directory:
trace-<runId>.chromium.json for Chromiumtrace-<runId>.speedscope.json for Speedscopetrace-<runId>.otel.json for OpenTelemetry file outputtrace-<runId>.html for HTML
#Choosing an export format
chromium: timeline exploration in Chromium trace viewers (about://tracing / https://ui.perfetto.dev/).speedscope: profile-style, flamegraph-oriented analysis.html: a self-contained interactive viewer with swimlanes and flamegraph.otel: sends data to an OTLP endpoint. Useful for integration with OpenTelemetry-based monitoring and analysis tools.otel-file: writes OpenTelemetry data to a local file.
Examples:
Shell
Meziantou.GitHubActionsTracing export https://github.com/OWNER/REPO/actions/runs/123456 --format chromium
Meziantou.GitHubActionsTracing export https://github.com/OWNER/REPO/actions/runs/123456 --format speedscope
Meziantou.GitHubActionsTracing export https://github.com/OWNER/REPO/actions/runs/123456 --format html
Meziantou.GitHubActionsTracing export https://github.com/OWNER/REPO/actions/runs/123456 --format otel-file
Meziantou.GitHubActionsTracing export https://github.com/OWNER/REPO/actions/runs/123456 --otel-endpoint http://localhost:4317 --otel-protocol grpc
Sample HTML output:
Interactive HTML swimlane view generated from a GitHub Actions workflow run
If you use OpenTelemetry exporter environment variables, use the EXPORTER_ prefix (for example, EXPORTER_OTEL_EXPORTER_OTLP_ENDPOINT).
#Filtering noisy data
Large workflows can produce a lot of spans. Keep traces readable by filtering short entries and disabling specific span categories.
Shell
Meziantou.GitHubActionsTracing export https://github.com/OWNER/REPO/actions/runs/123456 --format html --minimum-test-duration 00:00:01 --minimum-binlog-duration 00:00:01 --include-binlog --include-tests
If you only want high-level workflow timing, disable detailed build and test spans:
Shell
Meziantou.GitHubActionsTracing export https://github.com/OWNER/REPO/actions/runs/123456 --format chromium --include-binlog false --include-tests false
#Reusing downloaded run information
If you need multiple exports from the same workflow run, download once and reuse locally:
Shell
Meziantou.GitHubActionsTracing download-run-info https://github.com/OWNER/REPO/actions/runs/123456 --output ./run-info
Meziantou.GitHubActionsTracing export ./run-info --chromium-path trace.chromium.json --speedscope-path trace.speedscope.json --html-path trace.html --otel-file-path trace.otel.json
This is useful for offline analysis and for comparing filters and formats without re-downloading artifacts.
#Automate trace export when a workflow completes
You can also automate this in GitHub Actions. Create a workflow triggered by workflow_run with types: [completed], then export traces and upload them as artifacts.
The repository contains a complete example:
The workflow does the following:
- Builds the workflow run URL from
github.event.workflow_run.id. - Downloads run metadata, logs, and artifacts using
download-run-info. - Runs
export to generate Chromium, Speedscope, and OpenTelemetry files. - Uploads generated trace files as workflow artifacts.
This pattern gives you ready-to-open trace files for every completed run without manual steps.
#Optional: process runs automatically with the webhook receiver
The repository also contains a webhook receiver app in Meziantou.GitHubActionsTracing.Server/.
Run from source:
PowerShell
dotnet run --project .\Meziantou.GitHubActionsTracing.Server\
A Docker image is available on GitHub Container Registry:
Shell
docker pull ghcr.io/meziantou/meziantou-git-hub-actions-tracing:latest
The service supports:
POST /webhooks/github for GitHub workflow_run eventsPOST /workflow-runs to queue a run manually
Configuration is under GitHubActionsTracingWebhook and includes:
WebhookSecret for X-Hub-Signature-256 validation- Output settings (
OtelEndpoint, OtelPath, ChromiumPath, SpeedscopePath, HtmlPath) - Filtering settings (
MinimumTestDuration, MinimumBinlogDuration, IncludeBinlog, IncludeTests) - Repository allow-lists (
AllowedRepositoriesExact, AllowedRepositoriesPatterns)
#Additional resources
Do you have a question or a suggestion about this post? Contact me!