Apex Debug Log Workflow with Claude Code
Overview
Apex debug logs are your primary tool for diagnosing runtime issues in Salesforce — governor limit violations, unexpected trigger behavior, silent failures, and more. This article covers a complete workflow for pulling debug logs from any Salesforce org using the SFDX CLI, then leveraging Claude Code to analyze them and surface root causes. Instead of manually scanning thousands of log lines, you hand the logs to Claude Code, which knows exactly which log events matter and in what order to prioritize them.
Prerequisites
- Salesforce CLI (
sf) installed and authenticated to your target org - jq installed for JSON parsing (
sudo apt install jqon Linux/WSL) - Claude Code running in your project directory
Project Setup
Create this directory structure in your Salesforce project:
<project-root>/
├── debug-logs/ # Pulled log files (gitignored)
├── scripts/
│ ├── debug-pull.sh # Log pull helper script
│ └── apex/ # Anonymous Apex test scripts
Add the log directory to .gitignore:
# Apex debug logs — local only, never commit
debug-logs/
The Log Pull Script
Save this as scripts/debug-pull.sh and make it executable with chmod +x scripts/debug-pull.sh:
#!/bin/bash
# Pull Apex debug logs from Salesforce for Claude Code review
# Usage: ./scripts/debug-pull.sh [org-alias] [number-of-logs]
ORG=${1:-""}
COUNT=${2:-5}
LOG_DIR="./debug-logs"
if [ -z "$ORG" ]; then
echo "ERROR: Org alias required."
echo "Usage: ./scripts/debug-pull.sh <org-alias> [log-count]"
exit 1
fi
if ! command -v jq &> /dev/null; then
echo "ERROR: jq is required. Install with: sudo apt install jq"
exit 1
fi
mkdir -p $LOG_DIR
echo ""
echo "=== Fetching last $COUNT logs from [$ORG] ==="
echo ""
LOGS=$(sf apex list log --target-org "$ORG" --json 2>/dev/null \
| jq -r ".result | sort_by(.StartTime) | reverse | .[:$COUNT] | .[].Id")
if [ -z "$LOGS" ]; then
echo "No logs found. Ensure tracing is active and transactions have run."
echo "Tip: Run 'sf apex tail log --target-org $ORG' to start a trace session."
exit 0
fi
PULLED=0
for LOG_ID in $LOGS; do
OUTPUT_FILE="$LOG_DIR/$LOG_ID.log"
if [ -f "$OUTPUT_FILE" ]; then
echo " [skip] $LOG_ID — already exists"
continue
fi
echo " [pull] $LOG_ID"
sf apex get log --log-id "$LOG_ID" --target-org "$ORG" > "$OUTPUT_FILE" 2>/dev/null
if [ ! -s "$OUTPUT_FILE" ]; then
echo " [warn] $LOG_ID — empty log, skipping"
rm "$OUTPUT_FILE"
continue
fi
PULLED=$((PULLED + 1))
done
echo ""
echo "=== Done. $PULLED new log(s) saved to $LOG_DIR/ ==="
echo ""
ls -lht "$LOG_DIR"/*.log 2>/dev/null | head -10
Pulling Logs
Quick pull (last 5 logs)
./scripts/debug-pull.sh my-sandbox
Pull a specific count
./scripts/debug-pull.sh my-sandbox 10
Tail logs live during manual testing
sf apex tail log --target-org my-sandbox --color
This streams log output in real time as you interact with the org. It also automatically creates a trace flag for your session.
Run anonymous Apex and capture the log inline
sf apex run \
--file ./scripts/apex/test-something.apex \
--target-org my-sandbox \
--log-level DEBUG \
--json | jq '.result.logs'
Other Useful Log Commands
List available logs without pulling them
sf apex list log --target-org my-sandbox --json
Pull a specific log by ID
sf apex get log --log-id <logId> --target-org my-sandbox
Trace Flag Management
Trace flags tell Salesforce to capture detailed logs for your user. They auto-expire after 30 minutes by default. Running sf apex tail log automatically creates one for your session.
To check active trace flags manually:
sf data query \
--query "SELECT Id, LogType, StartDate, ExpirationDate, DebugLevelId \
FROM TraceFlag \
WHERE TracedEntityId = '<your-user-id>'" \
--target-org my-sandbox \
--use-tooling-api
Debug Log Levels
From least to most verbose:
| Level | Use Case |
|---|---|
| ERROR | Production — errors only |
| WARN | Near-production monitoring |
| INFO | General flow tracking |
| DEBUG | Standard debugging |
| FINE | Detailed execution flow |
| FINER | Very detailed tracing |
| FINEST | Maximum detail — produces large logs |
Asking Claude Code to Analyze Logs
Once logs are in ./debug-logs/, ask Claude Code to review them directly:
"Review the latest log in ./debug-logs/ and tell me what's wrong"
For targeted analysis:
"Review ./debug-logs/07LxxxxID.log — I'm seeing a CPU time limit error. Find the root cause."
What Claude Code looks for (in priority order)
- FATAL_ERROR / unhandled exceptions — root cause first
- Governor limit warnings — CPU time, heap size, SOQL row counts, DML statement counts
- USER_DEBUG entries — intentional breadcrumbs left by the developer
- Slow SOQL — queries returning 100+ rows or taking 200ms+
- Unexpected null references
Log format reference
Debug logs follow a timestamp | event_type | detail structure. The key prefixes Claude Code focuses on:
| Prefix | Meaning |
|---|---|
FATAL_ERROR | Unhandled exception — always the starting point |
EXCEPTION_THROWN | Exception with stack trace |
LIMIT_USAGE_FOR_NS | Governor limit consumption snapshot |
USER_DEBUG | Developer-placed System.debug() output |
SOQL_EXECUTE_BEGIN/END | Query execution with row counts |
DML_BEGIN/END | DML operation markers |
Verbose noise that can typically be ignored: VARIABLE_SCOPE_BEGIN, STATEMENT_EXECUTE, METHOD_ENTRY/EXIT — unless you're tracing a specific execution path.
Wiring This Into Your CLAUDE.md
To make Claude Code automatically aware of this workflow in your Salesforce projects, add a block to your project's CLAUDE.md:
## Apex Debug Log Workflow
For Apex runtime issues, governor limit errors, or unexpected behavior in org:
1. Pull logs first: `./scripts/debug-pull.sh <org-alias>`
2. Logs land in `./debug-logs/` — read the most recent `.log` file(s)
3. For detailed workflow reference: see docs/APEX_DEBUG_WORKFLOW.md
You can also create a path-scoped rule at .claude/rules/apex-debug.md that auto-activates when Claude Code touches .cls, .trigger, .apex, or debug-logs/ files. This rule can instruct Claude Code to check for logs before suggesting fixes for runtime errors.