IntegrationsOtherClaude Code

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 commit

This 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_id from Claude Code’s session_id before 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 commit executions, enriches tracing with git metadata, and writes repo-local manifest files under .langfuse/.
  • It adds a git prepare-commit-msg hook that appends a Langfuse-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 locationInstalled asPurpose
~/.claude/hooks/langfuse_utils.pyShared moduleCommon logging, state handling, git helpers, and manifest writing
~/.claude/hooks/langfuse_session_init_hook.pyPreToolUseCreates a deterministic trace_id from the Claude Code session_id before the first tool call
~/.claude/hooks/langfuse_hook.pyStop and SessionEndReads Claude Code transcripts incrementally and emits turns, generations, and tool spans to Langfuse
~/.claude/hooks/langfuse_git_commit_hook.pyPostToolUse with matcher BashDetects 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-msgAppends Langfuse-Session: to commit messages and chains any pre-existing git hook
~/.claude/state/langfuse_last_trace.jsonRuntime stateStores the most recent session/trace mapping for commit linking
.langfuse/current-session.jsonPer repo runtime stateStores the current repo’s active session/trace mapping
.langfuse/traces/<session-id>.jsonPer repo manifestStores session URL, trace URL, and git metadata for the active Claude Code session
.langfuse/traces/agent-trace-<commit>.jsonPer repo manifestMaps 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 langfuse

If 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_*.py

If 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:

VariableDescriptionRequired for turn tracingRequired for commit/session linking
TRACE_TO_LANGFUSEEnables the hooks for this repositoryYesYes
LANGFUSE_PUBLIC_KEYYour Langfuse public keyYesYes
LANGFUSE_SECRET_KEYYour Langfuse secret keyYesYes
LANGFUSE_BASE_URLLangfuse host, for example https://cloud.langfuse.com or https://us.cloud.langfuse.comYesYes
LANGFUSE_PROJECT_IDLangfuse project ID used to build session_url valuesNoYes
LANGFUSE_HOOK_DEBUGEnables verbose debug logging in ~/.claude/state/langfuse_hook.logNoNo
LANGFUSE_HOOK_MAX_CHARSMax captured characters for long inputs and outputsNoNo

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-msg

This 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.json

The .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

  1. Start Claude Code in the repository you just configured.
  2. Ask Claude Code to inspect or edit a file and to use at least one tool.
  3. Check that hook state files were created:
    • ~/.claude/state/langfuse_last_trace.json
    • .langfuse/current-session.json
    • .langfuse/traces/<session-id>.json
  4. Ask Claude Code to create a git commit from within the session.
  5. Inspect the latest commit message:
git log -1 --pretty=%B

You should see a trailer like this:

Langfuse-Session: https://cloud.langfuse.com/project/<project-id>/sessions/<session-id>
  1. Open the corresponding .langfuse/traces/<session-id>.json file and follow the trace_url to Langfuse.
  2. 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_id generation in PreToolUse
  • Langfuse trace -> repo metadata via .langfuse/current-session.json and .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 the claude-code tag.

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

  1. Verify that .claude/settings.local.json contains TRACE_TO_LANGFUSE: "true" and valid Langfuse credentials.
  2. Confirm the Langfuse SDK is installed:
python3 -m pip show langfuse
  1. Check the hook log:
tail -f ~/.claude/state/langfuse_hook.log
  1. Make sure ~/.claude/settings.json includes PreToolUse, Stop, SessionEnd, and PostToolUse entries for the Langfuse scripts.

Traces appear, but commits are not linked

  1. Confirm LANGFUSE_PROJECT_ID is set in .claude/settings.local.json.
  2. Verify .git/hooks/prepare-commit-msg exists and is executable.
  3. Let Claude Code run at least one tool before the first commit, so langfuse_session_init_hook.py can initialize the session trace.
  4. Prefer having Claude Code run the git commit itself. The Bash PostToolUse hook 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_ID is 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:

Resources

Was this page helpful?