# How to Debug Workflow Execution Using the Logs Command and Parsing in gh-aw

> Debug GitHub Actions workflow execution with the gh aw logs command. Easily parse logs and generate markdown reports for faster troubleshooting.

- Repository: [GitHub/gh-aw](https://github.com/github/gh-aw)
- Tags: how-to-guide
- Published: 2026-02-16

---

**The `gh aw logs` command downloads GitHub Actions artifacts and optionally executes engine-specific JavaScript parsers to generate human-readable markdown reports for debugging agentic workflow execution.**

Debugging agentic workflows requires visibility into how runs actually executed in GitHub Actions. The `github/gh-aw` repository provides a comprehensive logs command that not only retrieves raw artifacts but also applies intelligent parsing to transform verbose execution data into actionable insights. This guide explains how to debug workflow execution using the logs command and parsing capabilities implemented in the Go source code.

## Command Architecture and Flag Handling

The entry point for log retrieval resides in [`pkg/cli/logs_command.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_command.go), which defines the Cobra command `gh aw logs [workflow]`. This file handles workflow name resolution via `workflow.FindWorkflowName` and validates engine parameters against `workflow.GetGlobalEngineRegistry`.

Key flags include:

- `--start-date`: Filter runs by execution timeframe
- `--engine`: Target specific engines (copilot, claude, codex)
- `--parse`: Execute JavaScript log parsers after download
- `--json`: Output structured data instead of formatted tables
- `--tool-graph`: Generate Mermaid diagrams of tool interactions

After validation, the command invokes `DownloadWorkflowLogs` at lines 94-95, passing all parsed parameters to the orchestration layer.

## Downloading and Organizing Artifacts

The orchestration logic in [`pkg/cli/logs_orchestrator.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_orchestrator.go) manages the complete retrieval pipeline. It queries the GitHub API for workflow runs matching the specified filters, then processes each run individually.

For every run, the orchestrator:

1. Creates a dedicated subdirectory (`run-<id>/`)
2. Downloads all artifacts including [`aw_info.json`](https://github.com/github/gh-aw/blob/main/aw_info.json), `agent_output/`, and `workflow-logs/`
3. Calls `flattenSingleFileArtifacts` to simplify single-file artifacts (referenced in [`logs_command.go`](https://github.com/github/gh-aw/blob/main/logs_command.go) lines 28-30)
4. Generates [`summary.json`](https://github.com/github/gh-aw/blob/main/summary.json) aggregating duration, token usage, cost calculations, tool usage counts, and MCP failure counts

When the `--parse` flag is active, the orchestrator triggers the parsing engine after artifact retrieval completes.

## Engine Detection and Log Discovery

Before parsing can occur, the system must identify which engine produced the logs and locate the correct file. The [`pkg/cli/logs_parsing_core.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_parsing_core.go) file implements this discovery logic.

The `extractEngineFromAwInfo` function (lines 80-108) reads the [`aw_info.json`](https://github.com/github/gh-aw/blob/main/aw_info.json) artifact, unmarshals the engine identifier, and retrieves the corresponding implementation from the global registry. If resolution fails, the system logs a warning and skips parsing for that run.

Once the engine is identified, `findAgentLogFile` (lines 38-140) handles the complex task of locating the actual log file:

- Queries `engine.GetLogFileForParsing()` to get the engine-specific filename
- Handles three directory layouts: pre-flattened artifacts in `agent_output/`, flattened paths translated from `/tmp/gh-aw/` temporary directories, and legacy recursive scans for `session*.log` or `process*.log` files
- Falls back to `agent-stdio.log` when no engine-specific file is defined

## Executing JavaScript Log Parsers

The actual transformation from raw logs to readable markdown occurs in [`pkg/cli/logs_parsing_javascript.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_parsing_javascript.go). The `parseAgentLog` function (lines 26-53) orchestrates this process.

Execution flow:

1. Validates engine presence (aborts if `engine == nil`)
2. Invokes `findAgentLogFile` to locate the source logs
3. Retrieves the parser script ID via `engine.GetLogParserScriptId()` (e.g., `"copilot_log_parser"`)
4. Fetches the actual script source from `workflow.GetLogParserScript`
5. Writes raw logs to a temporary file and generates a Node.js bootstrap script
6. Executes the parser via `exec.Command("node", "parser.js")`
7. Writes the resulting markdown to [`log.md`](https://github.com/github/gh-aw/blob/main/log.md) in the run directory

The bootstrap script creates a mock `@actions/core` API, allowing the same parser code used in production GitHub Actions to execute locally without modification.

## Practical Debugging Workflow

Combine the various flags to create targeted debugging sessions:

| Step | Command | Purpose |
|------|---------|---------|
| **1. Pull recent runs** | `gh aw logs --start-date -1w -c 5` | Downloads the last 5 runs from the past week |
| **2. Focus on an engine** | `gh aw logs --engine copilot --parse` | Downloads runs, executes the Copilot JavaScript parser, and writes [`log.md`](https://github.com/github/gh-aw/blob/main/log.md) |
| **3. Filter by firewall** | `gh aw logs --firewall` | Shows only runs where the firewall tool was enabled |
| **4. Inspect raw JSON** | `gh aw logs --json > runs.json` | Exports the full [`summary.json`](https://github.com/github/gh-aw/blob/main/summary.json) structure for scripting or `jq` queries |
| **5. Analyze tool usage** | `gh aw logs --tool-graph > graph.mmd` | Generates a Mermaid diagram of tool calls across runs |
| **6. Limit to a branch** | `gh aw logs --ref main` | Restricts to runs that executed on the `main` branch |

All flags are defined in [`pkg/cli/logs_command.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_command.go) (lines 99-118).

### Complete Debugging Example

To debug a specific Copilot workflow execution:

```bash

# Download and parse the last 3 Copilot runs from the main branch

gh aw logs my-workflow --engine copilot --parse --ref main -c 3

# Inspect the parsed markdown output

cat run-1234567890/log.md

# Query specific metrics from the summary JSON

jq '.[] | {run_id: .run_id, duration: .duration, cost: .cost}' run-1234567890/summary.json

```

## Summary

- The `gh aw logs` command in the `github/gh-aw` repository provides a complete pipeline for debugging workflow execution, from artifact retrieval to intelligent log parsing.
- The command implementation in [`pkg/cli/logs_command.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_command.go) handles flag validation and delegates to `DownloadWorkflowLogs` for orchestration.
- The orchestrator downloads artifacts, generates [`summary.json`](https://github.com/github/gh-aw/blob/main/summary.json) with cost and token metrics, and optionally triggers parsing.
- Engine detection via `extractEngineFromAwInfo` and log discovery via `findAgentLogFile` ensure the correct files are processed for each workflow engine.
- JavaScript parsers execute in a sandboxed Node.js environment via `parseAgentLog`, producing readable [`log.md`](https://github.com/github/gh-aw/blob/main/log.md) reports.
- CLI flags like `--parse`, `--json`, `--tool-graph`, and `--firewall` enable targeted debugging workflows for specific engines, branches, or analysis formats.

## Frequently Asked Questions

### How does `gh aw logs` determine which log file to parse for different AI engines?

The command uses the `findAgentLogFile` function in [`pkg/cli/logs_parsing_core.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_parsing_core.go) to locate the correct file. It first queries the engine implementation via `engine.GetLogFileForParsing()` to get the engine-specific filename, then handles three directory layouts: pre-flattened artifacts in `agent_output/`, flattened paths translated from `/tmp/gh-aw/` temporary directories, and legacy recursive scans for `session*.log` or `process*.log` files. If no engine-specific file is defined, it falls back to `agent-stdio.log`.

### What information is included in the [`summary.json`](https://github.com/github/gh-aw/blob/main/summary.json) file generated by the logs command?

The [`summary.json`](https://github.com/github/gh-aw/blob/main/summary.json) file is generated by the orchestrator in [`pkg/cli/logs_orchestrator.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_orchestrator.go) and aggregates key execution metrics for each workflow run. It includes the total duration of the run, token usage statistics, estimated cost calculations, tool usage counts, and MCP (Model Context Protocol) failure counts. This structured data enables programmatic analysis when using the `--json` flag or allows for quick inspection of run health without parsing raw log files.

### Can I use `gh aw logs` to debug workflows that used the firewall tool?

Yes, the command provides specific support for firewall debugging through the `--firewall` flag, defined in [`pkg/cli/logs_command.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_command.go). When this flag is specified, the orchestrator filters the workflow runs to show only those where the firewall tool was enabled. Additionally, when using the `--parse` flag, the system invokes `parseFirewallLogs` alongside `parseAgentLog` to generate a [`firewall.md`](https://github.com/github/gh-aw/blob/main/firewall.md) report containing the firewall-specific execution details and security policy enforcement logs.

### How does the JavaScript log parser execute locally without GitHub Actions?

The `parseAgentLog` function in [`pkg/cli/logs_parsing_javascript.go`](https://github.com/github/gh-aw/blob/main/pkg/cli/logs_parsing_javascript.go) creates a sandboxed Node.js environment that mocks the GitHub Actions runtime. It retrieves the parser script ID via `engine.GetLogParserScriptId()`, fetches the actual script source from `workflow.GetLogParserScript`, and writes the raw logs to a temporary file. It then generates a bootstrap JavaScript file that creates a mock `@actions/core` API, allowing the same parser code used in production GitHub Actions to execute locally via `exec.Command("node", "parser.js")` without modification.