3.9 KiB
Exercise 02: Build an Audit Trail
Concept: Hooks (CC-024) Level: Intermediate Time: ~15 minutes
Objective
Add a post-tool-use hook that logs every tool call Claude makes to a local file. You will give Claude a multi-step task, then read the audit log to see everything it did.
This is the foundation for compliance logging, debugging, and understanding what Claude actually does during a session.
Before You Start
Confirm you have completed Exercise 01, or at minimum:
hooks/post-tool-use.shexists in this repo.claude/settings.jsonexists with the PreToolUse hook from Exercise 01
Instructions
Step 1: Read the post-tool-use hook.
Open hooks/post-tool-use.sh and read through it. Notice:
- It always exits 0. Post-tool-use hooks cannot block.
- It extracts different keys depending on the tool (
command,file_path,pattern) - It appends to
hooks/audit.login the same directory as the script itself - The timestamp format is UTC ISO 8601
Step 2: Make the script executable.
chmod +x hooks/post-tool-use.sh
Step 3: Add the PostToolUse hook to .claude/settings.json.
Update your .claude/settings.json to include both hooks:
{
"permissions": {
"allow": [
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(echo:*)",
"Bash(pwd)",
"Bash(date)",
"Read",
"Write",
"Edit",
"Glob",
"Grep"
],
"deny": [
"Bash(rm -rf *)",
"Bash(sudo *)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash hooks/pre-tool-use.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash hooks/post-tool-use.sh"
}
]
}
]
}
}
The matcher: ".*" pattern matches every tool, not just Bash. This
means every Read, Write, Grep, and WebSearch call will be logged.
Step 4: Start or restart Claude Code.
cd claude-code-hooks
claude
If Claude Code is already running, restart it so it picks up the new settings file.
Step 5: Give Claude a multi-step task.
Paste this prompt into Claude Code:
List the files in this directory, then read README.md, then tell me
how many lines it has.
This will trigger multiple tool calls: a Bash call for ls, a Read
call for README.md, and a Bash call for wc -l.
Step 6: Read the audit log.
After Claude finishes, paste this prompt:
Show me the contents of hooks/audit.log
Or read it directly from your terminal:
cat hooks/audit.log
Expected Output
The audit log should contain entries like:
2026-03-30T04:12:33Z | Bash | ls -la
2026-03-30T04:12:34Z | Read | README.md
2026-03-30T04:12:35Z | Bash | wc -l README.md
Each line shows the UTC timestamp, the tool name, and the primary argument (command, file path, or search pattern).
If audit.log does not exist after the task, check that post-tool-use.sh
is executable and that the PostToolUse hook is in settings.json.
What You Learned
- PostToolUse hooks run after every tool call. They receive the same JSON input as PreToolUse hooks but cannot influence the outcome. Their job is observation, not control.
matcher: ".*"captures everything. Narrowing the matcher to"Bash"would log only shell commands. Using".*"logs all tool types.- Audit logs survive sessions. The log file persists between Claude Code sessions. This makes it useful for compliance and debugging: you can always reconstruct what happened.
- Logs are per-project. The log path is relative to the script, so each project has its own audit trail.
Next
Move to Exercise 03: Prompt-Based Approval.