Claude Code Tracing with Langfuse
The best way to install this integration is to prompt Claude Code to do it for you. Use the prompt below in the repository you want to trace, then follow the manual steps in this guide if you want to inspect or customize the setup.
Install the Langfuse Claude Code tracing integration in this repository by following the latest manual guide from langfuse.com. Please:
1. install or update the Python langfuse package
2. download the latest Langfuse Claude Code hook scripts into ~/.claude/hooks
3. merge the Langfuse hook entries into ~/.claude/settings.json without removing unrelated hooks
4. create or update .claude/settings.local.json with TRACE_TO_LANGFUSE, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_BASE_URL, and LANGFUSE_PROJECT_ID
5. install a prepare-commit-msg wrapper that preserves any existing hook by chaining prepare-commit-msg.pre-langfuse
6. add .langfuse/ and .claude/settings.local.json to .gitignore
7. verify the setup with a short Claude Code session and one commitThis guide explains how to manually install the same hook-based Claude Code tracing workflow that was proposed in langfuse-cli PR #3, without requiring the CLI automation itself.
What is Claude Code? Claude Code is Anthropic’s agentic coding tool that lives in your terminal. It can inspect your codebase, edit files, run commands, and complete multi-step coding tasks.
What is Langfuse? Langfuse is the open-source LLM engineering platform. It helps you trace agent behavior, understand tool use, analyze latency and costs, and evaluate outputs.
Why this integration matters
The updated integration is designed to create a durable link between:
- the Claude Code session in Langfuse
- the trace(s) and tool calls emitted from that session
- the git commit created during that session
- the files changed by that commit in the local repository
That makes it easier to answer questions like:
- Is your Claude Code subscription worth it, or should you switch to a bring-your-own-key setup?
- How do latency and activity change by time of day, region, or developer?
- What prompts, tool calls, and intermediate steps led to a specific code change?
What changed from the original guide
The earlier version of this page documented a single Stop hook and one Python script. After reviewing langfuse-cli PR #3, the newer hook stack is more complete and materially better for code-to-trace linking:
- It adds a PreToolUse session-init hook that creates a deterministic Langfuse
trace_idfrom Claude Code’ssession_idbefore the first tool runs. - It keeps the Stop hook, but upgrades it to the latest incremental transcript reader that emits detailed generation and tool observations.
- It adds a SessionEnd hook as a final flush point for the same tracing script.
- It adds a PostToolUse Bash hook that detects
git commitexecutions, enriches tracing with git metadata, and writes repo-local manifest files under.langfuse/. - It adds a git
prepare-commit-msghook that appends aLangfuse-Session:trailer to commit messages and preserves any existing hook by chaining it. - It adds shared utilities so all hook scripts use the same state files, manifest writers, git helpers, and debug logging.
Earlier commits in the PR experimented with Langfuse-Trace: commit trailers. The latest merged scripts append Langfuse-Session: to commit messages and keep the trace URL in the repo-local .langfuse/traces/ manifests. This page follows the latest implementation.
How the updated hook stack works
| File or location | Installed as | Purpose |
|---|---|---|
~/.claude/hooks/langfuse_utils.py | Shared module | Common logging, state handling, git helpers, and manifest writing |
~/.claude/hooks/langfuse_session_init_hook.py | PreToolUse | Creates a deterministic trace_id from the Claude Code session_id before the first tool call |
~/.claude/hooks/langfuse_hook.py | Stop and SessionEnd | Reads Claude Code transcripts incrementally and emits turns, generations, and tool spans to Langfuse |
~/.claude/hooks/langfuse_git_commit_hook.py | PostToolUse with matcher Bash | Detects git commit, writes repo-local manifests, and links changed files to the active Langfuse trace |
~/.claude/hooks/langfuse_prepare_commit_msg.py | .git/hooks/prepare-commit-msg | Appends Langfuse-Session: to commit messages and chains any pre-existing git hook |
~/.claude/state/langfuse_last_trace.json | Runtime state | Stores the most recent session/trace mapping for commit linking |
.langfuse/current-session.json | Per repo runtime state | Stores the current repo’s active session/trace mapping |
.langfuse/traces/<session-id>.json | Per repo manifest | Stores session URL, trace URL, and git metadata for the active Claude Code session |
.langfuse/traces/agent-trace-<commit>.json | Per repo manifest | Maps a git commit’s changed files back to the Claude Code-generated trace |
Manual installation
Install prerequisites
You need:
- Claude Code
- Python 3
- a Langfuse project with API keys
- a git repository if you want commit linking
Install or update the Langfuse Python package and create the hook directories:
mkdir -p ~/.claude/hooks ~/.claude/state
python3 -m pip install --upgrade langfuseIf you use a custom Python interpreter or virtual environment, replace python3 with that interpreter consistently in every hook command below.
Download the latest hook scripts
Instead of copying a stale script from this page, download the latest versions directly from the langfuse-cli repository:
curl -fsSL https://raw.githubusercontent.com/langfuse/langfuse-cli/main/hooks/langfuse_utils.py -o ~/.claude/hooks/langfuse_utils.py
curl -fsSL https://raw.githubusercontent.com/langfuse/langfuse-cli/main/hooks/langfuse_session_init_hook.py -o ~/.claude/hooks/langfuse_session_init_hook.py
curl -fsSL https://raw.githubusercontent.com/langfuse/langfuse-cli/main/hooks/langfuse_hook.py -o ~/.claude/hooks/langfuse_hook.py
curl -fsSL https://raw.githubusercontent.com/langfuse/langfuse-cli/main/hooks/langfuse_git_commit_hook.py -o ~/.claude/hooks/langfuse_git_commit_hook.py
curl -fsSL https://raw.githubusercontent.com/langfuse/langfuse-cli/main/hooks/langfuse_prepare_commit_msg.py -o ~/.claude/hooks/langfuse_prepare_commit_msg.py
chmod +x ~/.claude/hooks/langfuse_*.pyIf you want a fully reproducible setup, replace main in the URLs above with a release tag or commit SHA.
Register the Claude Code hooks
Add the Langfuse hook commands to your global Claude Code settings at ~/.claude/settings.json.
If you already use Claude Code hooks, merge the entries below into your existing file instead of replacing unrelated hooks.
{
"hooks": {
"PreToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/langfuse_session_init_hook.py"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/langfuse_hook.py"
}
]
}
],
"SessionEnd": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/langfuse_hook.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/langfuse_git_commit_hook.py"
}
]
}
]
}
}The SessionEnd hook is intentionally registered to the same script as Stop. Stop captures most turns, while SessionEnd acts as a final flush point for the session transcript.
Enable tracing for a repository
Create .claude/settings.local.json in the root of each repository that you want to trace:
{
"env": {
"TRACE_TO_LANGFUSE": "true",
"LANGFUSE_PUBLIC_KEY": "pk-lf-...",
"LANGFUSE_SECRET_KEY": "sk-lf-...",
"LANGFUSE_BASE_URL": "https://cloud.langfuse.com",
"LANGFUSE_PROJECT_ID": "cloramnkj0002jz088vzn1ja4"
}
}You can copy the project ID from the Langfuse project settings page, or resolve it from the /api/public/projects response for your credentials.
Use these values:
| Variable | Description | Required for turn tracing | Required for commit/session linking |
|---|---|---|---|
TRACE_TO_LANGFUSE | Enables the hooks for this repository | Yes | Yes |
LANGFUSE_PUBLIC_KEY | Your Langfuse public key | Yes | Yes |
LANGFUSE_SECRET_KEY | Your Langfuse secret key | Yes | Yes |
LANGFUSE_BASE_URL | Langfuse host, for example https://cloud.langfuse.com or https://us.cloud.langfuse.com | Yes | Yes |
LANGFUSE_PROJECT_ID | Langfuse project ID used to build session_url values | No | Yes |
LANGFUSE_HOOK_DEBUG | Enables verbose debug logging in ~/.claude/state/langfuse_hook.log | No | No |
LANGFUSE_HOOK_MAX_CHARS | Max captured characters for long inputs and outputs | No | No |
LANGFUSE_PROJECT_ID is the key difference between the older guide and the newer hook stack. Without it, you still get traces, but you do not get the complete code-to-session link because the commit hook cannot build a Langfuse session URL.
Install the git commit hook wrapper
The Langfuse git integration uses a repo-local prepare-commit-msg hook. It should call the Langfuse Python hook first and then chain any previously existing hook.
If your repo already has .git/hooks/prepare-commit-msg, back it up to .git/hooks/prepare-commit-msg.pre-langfuse before you install the wrapper.
if [ -f .git/hooks/prepare-commit-msg ] && ! rg -q "langfuse-trace-trailer" .git/hooks/prepare-commit-msg; then
mv .git/hooks/prepare-commit-msg .git/hooks/prepare-commit-msg.pre-langfuse
fi
cat > .git/hooks/prepare-commit-msg <<'EOF'
#!/bin/sh
# langfuse-trace-trailer — installed manually
python3 ~/.claude/hooks/langfuse_prepare_commit_msg.py "$@" 2>/dev/null || true
if [ -x "$(dirname "$0")/prepare-commit-msg.pre-langfuse" ]; then
"$(dirname "$0")/prepare-commit-msg.pre-langfuse" "$@"
fi
EOF
chmod +x .git/hooks/prepare-commit-msgThis wrapper mirrors the implementation from langfuse-cli: it preserves existing git hook behavior instead of overwriting it.
Ignore local manifests and repo-specific secrets
Add these entries to your repository’s .gitignore:
.langfuse/
.claude/settings.local.jsonThe .langfuse/ directory contains local manifests that link traces, sessions, and commits. It is useful for local navigation and future tooling, but it should not be committed by default.
Verify the setup end-to-end
- Start Claude Code in the repository you just configured.
- Ask Claude Code to inspect or edit a file and to use at least one tool.
- Check that hook state files were created:
~/.claude/state/langfuse_last_trace.json.langfuse/current-session.json.langfuse/traces/<session-id>.json
- Ask Claude Code to create a git commit from within the session.
- Inspect the latest commit message:
git log -1 --pretty=%BYou should see a trailer like this:
Langfuse-Session: https://cloud.langfuse.com/project/<project-id>/sessions/<session-id>- Open the corresponding
.langfuse/traces/<session-id>.jsonfile and follow thetrace_urlto Langfuse. - In Langfuse, inspect the session to review prompts, tool usage, latencies, and cost data for the code change.
What gets linked together
The hook stack produces both Langfuse traces and repo-local metadata so you can move between code, commits, sessions, and traces.
A typical .langfuse/traces/<session-id>.json file looks like this:
{
"schema_version": 1,
"langfuse": {
"trace_id": "bcee2dea6c117f48ee1e51f01c6b67bc",
"trace_url": "https://cloud.langfuse.com/trace/bcee2dea6c117f48ee1e51f01c6b67bc",
"session_id": "b1bccb92-3cd3-4988-bbd9-9b46a7d01cba",
"session_url": "https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/sessions/b1bccb92-3cd3-4988-bbd9-9b46a7d01cba",
"host": "https://cloud.langfuse.com"
},
"git": {
"commit_sha": "6636a89b6bafd03ddf437503b2f60c84",
"commit_url": "https://github.com/org/repo/commit/6636a89b6bafd03ddf437503b2f60c84",
"branch": "main",
"commit_message": "test: update README comment for Langfuse tracing integration test"
}
}This is the key link model:
- Claude Code session -> Langfuse trace via deterministic
trace_idgeneration inPreToolUse - Langfuse trace -> repo metadata via
.langfuse/current-session.jsonand.langfuse/traces/<session-id>.json - Git commit -> Langfuse session via the
Langfuse-Session:commit trailer - Changed files -> trace URL via
.langfuse/traces/agent-trace-<commit>.json
If you want the strongest possible link between code and tracing data, ask Claude Code itself to run the final git commit command. That guarantees the Bash PostToolUse hook and the git prepare-commit-msg hook both execute in the same agent session.
How to use the data in Langfuse
Once this setup is installed, you can use Langfuse to answer the questions that matter for agent-assisted coding:
- Cost and ROI: Compare the number, duration, and cost of Claude Code sessions with the quality and speed of code changes.
- Latency patterns: Inspect turn and tool timings by session, user, or time window to see whether latency changes during busy hours.
- Change provenance: Start from a commit, jump to the Langfuse session, and inspect the prompts, responses, and tool calls that produced the code.
- Operational debugging: Filter traces by
session_id,commit_sha,github_commit_url, or theclaude-codetag.
The latest Stop hook also records detailed tool observations, including Bash commands, so Langfuse becomes a practical debugging surface for understanding how a code change happened, not just that it happened.
Troubleshooting
No traces appear in Langfuse
- Verify that
.claude/settings.local.jsoncontainsTRACE_TO_LANGFUSE: "true"and valid Langfuse credentials. - Confirm the Langfuse SDK is installed:
python3 -m pip show langfuse- Check the hook log:
tail -f ~/.claude/state/langfuse_hook.log- Make sure
~/.claude/settings.jsonincludesPreToolUse,Stop,SessionEnd, andPostToolUseentries for the Langfuse scripts.
Traces appear, but commits are not linked
- Confirm
LANGFUSE_PROJECT_IDis set in.claude/settings.local.json. - Verify
.git/hooks/prepare-commit-msgexists and is executable. - Let Claude Code run at least one tool before the first commit, so
langfuse_session_init_hook.pycan initialize the session trace. - Prefer having Claude Code run the
git commititself. The BashPostToolUsehook only fires when Claude uses the Bash tool.
A commit message is missing the Langfuse-Session trailer
The latest hook script skips trailer insertion when:
- the commit is a merge or squash commit
- there is no recent active Langfuse session state
LANGFUSE_PROJECT_IDis missing- the prepare-commit-msg hook is not installed or is not executable
I already have other Claude Code or git hooks
Do not delete them. Merge the Langfuse entries into ~/.claude/settings.json, and chain any existing prepare-commit-msg hook by renaming it to .git/hooks/prepare-commit-msg.pre-langfuse.
Hook sources
These are the latest hook files used by this guide:
langfuse_utils.pylangfuse_session_init_hook.pylangfuse_hook.pylangfuse_git_commit_hook.pylangfuse_prepare_commit_msg.py
Resources
- Claude Code documentation
- Claude Code hooks guide
- Langfuse Sessions
- Langfuse Observability Overview
- Langfuse Python SDK Reference