📦 Part of the
langfuse-coding-agentsmonorepo. Other tools:claude-code·codex·oh-my-codex·opencode·gemini-cli
Automatic Langfuse tracing for Claude Code. Every conversation turn, tool call, and model response is captured as structured traces in your Langfuse dashboard -- zero code changes required.
- ✅ Hook pipeline verified on real Claude Code runs
- ✅ Turn traces, tool spans, and token usage confirmed in Langfuse
- ✅ Repository cleanup completed (no unnecessary tracked files found)
- ✅
v0.0.1release/tag refreshed with final docs sync - ✅ LCC (
langfuse-claude-code) aligned with companion repos:langfuse-oh-my-codexlangfuse-gemini-clilangfuse-opencode
- Progress docs: English | 한국어
- Full event coverage -- all 4 Claude Code hook events are captured (Stop, Notification, PreToolUse, PostToolUse)
- Per-turn tracing -- each user prompt + assistant response becomes a Langfuse trace
- Real-time tool events -- PreToolUse and PostToolUse hooks capture tool calls as they happen with precise timing
- System prompt capture -- system messages are recorded as dedicated spans
- Full assistant content -- all text blocks between tool calls are preserved in order (no content lost)
- Thinking blocks -- Claude's internal reasoning (
thinking) is captured as separate spans - Tool call tracking -- every tool use (Read, Write, Bash, etc.) is captured with inputs and outputs
- Token usage -- input/output/cache token counts are recorded on each generation
- Stop reason --
end_turn,tool_use, etc. tracked in metadata - Session grouping -- traces are grouped by Claude Code session ID
- Incremental processing -- only new transcript entries are sent (no duplicates)
- Incomplete turn capture -- if the session exits before a response, user messages are still recorded
- Fail-open design -- if anything goes wrong the hook exits silently; Claude Code is never blocked
- Cross-platform -- works on macOS, Linux, and Windows
- Dual SDK support -- works with both langfuse
>= 3.12(nested spans) and older versions (flat traces)
- Claude Code -- installed and working (install guide)
- Python 3.8+ -- with
pipavailable (python3 -m pip --versionorpython -m pip --versionto verify) - Langfuse account -- cloud.langfuse.com (free tier available) or a self-hosted instance
# Clone and run the installer
git clone https://github.com/BAEM1N/langfuse-coding-agents.git
cd langfuse-coding-agents/tools/claude-code
bash install.shOn Windows (PowerShell):
git clone https://github.com/BAEM1N/langfuse-coding-agents.git
cd langfuse-coding-agents/tools/claude-code
.\install.ps1The installer will:
- Check Python 3.8+ is available
- Install the
langfusePython package - Copy the hook script to
~/.claude/hooks/ - Prompt you for your Langfuse credentials:
- Public Key (
pk-lf-...) - Secret Key (
sk-lf-..., masked input) - Base URL (defaults to
https://cloud.langfuse.com) - User ID (defaults to
claude-user)
- Public Key (
- Merge the hook + env into
~/.claude/settings.json(preserves your existing settings) - Verify the installation
pip install langfusemkdir -p ~/.claude/hooks
cp langfuse_hook.py ~/.claude/hooks/
chmod +x ~/.claude/hooks/langfuse_hook.pyAdd (or merge) the following into your settings file:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/langfuse_hook.py"
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/langfuse_hook.py"
}
]
}
],
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/langfuse_hook.py"
}
]
}
],
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/langfuse_hook.py"
}
]
}
]
},
"env": {
"TRACE_TO_LANGFUSE": "true",
"LANGFUSE_PUBLIC_KEY": "pk-lf-...",
"LANGFUSE_SECRET_KEY": "sk-lf-...",
"LANGFUSE_BASE_URL": "https://cloud.langfuse.com",
"LANGFUSE_USER_ID": "your-username"
}
}| Variable | Required | Default | Description |
|---|---|---|---|
TRACE_TO_LANGFUSE |
Yes | - | Set to "true" to enable tracing |
LANGFUSE_PUBLIC_KEY |
Yes | - | Langfuse public key (or CC_LANGFUSE_PUBLIC_KEY) |
LANGFUSE_SECRET_KEY |
Yes | - | Langfuse secret key (or CC_LANGFUSE_SECRET_KEY) |
LANGFUSE_BASE_URL |
No | https://cloud.langfuse.com |
Langfuse host URL (or CC_LANGFUSE_BASE_URL) |
LANGFUSE_USER_ID |
No | claude-user |
User ID for trace attribution (or CC_LANGFUSE_USER_ID) |
CC_LANGFUSE_DEBUG |
No | false |
Set to "true" for verbose logging |
CC_LANGFUSE_MAX_CHARS |
No | 20000 |
Max characters per text field before truncation |
All LANGFUSE_* variables also accept a CC_LANGFUSE_* prefix (which takes priority).
Set LANGFUSE_BASE_URL to your instance URL:
"LANGFUSE_BASE_URL": "https://langfuse.your-company.com"┌─────────────────────────────────────────────────────────┐
│ Claude Code │
│ │
│ User prompt ──► Model response ──► Tool calls ──► ... │
│ │ │
│ ▼ │
│ Transcript file (.jsonl) │
│ │ │
│ │ ┌──── Stop Hook ────┐ │
│ └─►│ langfuse_hook.py │ │
│ │ │ │
│ │ 1. Read new JSONL │ │
│ │ 2. Build turns │ │
│ │ 3. Emit traces │ │
│ └───────┬───────────┘ │
│ │ │
└──────────────────┼───────────────────────────────────────┘
│
▼
┌─────────────────────┐
│ Langfuse │
│ │
│ Trace (Turn 1) │
│ ├─ System Prompt │
│ ├─ Generation │
│ │ ├─ model │
│ │ ├─ usage tokens │
│ │ └─ stop_reason │
│ ├─ Thinking [1] │
│ ├─ Text [1] │
│ ├─ Tool: Read │
│ ├─ Text [2] │
│ ├─ Tool: Write │
│ └─ Text [3] │
│ │
│ Session: abc123 │
└─────────────────────┘
Flow:
- Claude Code writes conversation data to a JSONL transcript file
- On every Stop event (after each model response) and Notification event, the hook reads the transcript
- On PreToolUse and PostToolUse events, the hook emits real-time tool spans independently
- The hook reads only new lines from the transcript (using a file offset saved in state)
- New messages are grouped into user-assistant turns
- Each turn is emitted as a Langfuse trace with:
- A system prompt span (if present)
- A generation observation (with model, token usage, stop reason)
- Ordered content spans preserving the full assistant flow:
- Thinking spans for internal reasoning blocks
- Text spans for assistant text between tool calls
- Tool spans for each tool call (with input/output)
- Real-time Before Tool / After Tool spans (from PreToolUse/PostToolUse)
- All traces share the same
session_idfor grouping
| Component | Version |
|---|---|
| Python | 3.8+ |
| langfuse SDK | 2.0+ (flat traces), 3.12+ (nested spans) |
| Claude Code | Any version with hooks support |
| OS | macOS, Linux, Windows |
- Verify
TRACE_TO_LANGFUSEis set to"true"in your settings - Check that your API keys are correct
- Enable debug logging: set
CC_LANGFUSE_DEBUGto"true" - Check the log file:
~/.claude/state/langfuse_hook.log
- Confirm the hooks are in
~/.claude/settings.jsonunderhooks.Stop,hooks.Notification,hooks.PreToolUse, andhooks.PostToolUse - Verify the Python path in the command is correct (
python3vspython) - Test manually:
echo '{}' | python3 ~/.claude/hooks/langfuse_hook.py(usepythoninstead ofpython3on Windows)
The hook tracks file offsets in ~/.claude/state/langfuse_state.json. If this file is deleted, previously-sent turns will be re-sent on the next invocation. Delete the state file only if you want a fresh start.
By default, text fields are truncated at 20,000 characters. Adjust with CC_LANGFUSE_MAX_CHARS:
"CC_LANGFUSE_MAX_CHARS": "50000"- Remove the hook entries from
~/.claude/settings.json(delete theStophook,Notificationhook, and theenvkeys) - Delete the hook script:
rm ~/.claude/hooks/langfuse_hook.py - Optionally remove state:
rm ~/.claude/state/langfuse_state.json