Skip to content

About

Local Claude Code observability with OpenTelemetry and Grafana LGTM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Local Claude Code observability

This project runs a local LGTM stack (Loki, Grafana, Tempo, and Mimir) behind a standalone OpenTelemetry Collector. Claude Code sends OTLP metrics, events, and traces to the collector on the host; the collector batches and forwards all three OTel signals to the collector embedded in the LGTM development image.

Claude Code
    │ OTLP/gRPC :4317 (or OTLP/HTTP :4318)
    ▼
OpenTelemetry Collector
    │ OTLP/gRPC on the Docker network
    ▼
Grafana OTEL-LGTM ──► Mimir (metrics)
                   ├► Loki (events/logs)
                   └► Tempo (traces)

The stack is for local development and testing, not production. All published ports bind to 127.0.0.1.

Prerequisites

  • Docker Desktop or Docker Engine with Docker Compose v2
  • Claude Code installed and authenticated
  • PowerShell on Windows, or a POSIX shell on macOS/Linux

Start the stack

From a fresh clone, run one command.

Windows PowerShell:

.\start.ps1

macOS, Linux, or WSL:

sh ./start.sh

The bootstrap script checks Docker, creates an ignored .env with a random local Grafana password when needed, starts the collector and LGTM services, waits for health, and verifies all three dashboards. It prints the Grafana URL and credentials when the stack is ready. Re-running the script is safe and preserves the existing configuration and telemetry volume.

Grafana normally becomes available at http://localhost:3300.

The host endpoints are:

Endpoint Purpose
localhost:4317 OTLP/gRPC receiver used by the Claude wrappers
localhost:4318 OTLP/HTTP receiver for optional local clients
localhost:13133 collector health endpoint
localhost:3300 Grafana (change with GRAFANA_PORT in .env)

Run Claude Code with local telemetry

The bootstrap installs the telemetry environment into the current user's ~/.claude/settings.json, preserving other settings already in that file. It applies to Claude Code CLI and Claude Code editor sessions launched by this local user, regardless of the project directory.

Restart any running Claude Code CLI or VS Code extension session after the first bootstrap. Then use Claude normally from any project:

claude

No repository wrapper is required. Claude Code exports metrics, logs/events, beta traces, prompts, assistant responses, tool content, and raw API bodies to the collector at localhost:4317. The 10-second metric interval is intentionally short for local feedback.

claude-settings.example.json is the telemetry template merged by the bootstrap scripts. Claude Desktop is a separate application and does not use Claude Code's user settings file.

Dashboards

Allow at least 10 seconds after starting a Claude session, then open Grafana:

  1. Open Dashboards → Claude Observability.
  2. Use Claude Code Telemetry for the overall metrics, events, tools, plugins, costs, and token views.
  3. Open Claude Code — Session Drill-down to inspect all activity belonging to one Claude session, including its Tempo tool spans.
  4. Open Claude Code — Prompt Drill-down to follow an individual prompt, its logs, tool or plugin activity, and linked Tempo trace.

The dashboards are provisioned directly from grafana/dashboards/*.json; live Grafana edits are disabled so those JSON files remain authoritative. After editing a dashboard file, restart Grafana:

docker compose up -d --force-recreate lgtm

For ad hoc exploration:

  1. Select Prometheus and query {__name__=~"claude_code_.*"}. OTel metric names containing dots are normalized to underscores by the Prometheus-compatible backend.
  2. Select Loki and start with {service_name=~"claude-code|claude-code-desktop"}.
  3. Select Tempo and search for claude-code or claude-code-desktop.

Useful Prometheus-normalized metric names include claude_code_session_count_total, claude_code_token_usage_tokens_total, claude_code_cost_usage_USD_total, and claude_code_active_time_seconds_total.

Trace export

Claude Code trace export is a beta capability. The supplied wrappers and settings example enable it with:

OTEL_TRACES_EXPORTER=otlp
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1

The collector routes those spans to Tempo. Detailed hook tracing has additional gates and remains disabled; consult the current Claude Code monitoring documentation before enabling it. If the installed Claude Code build or account does not support beta traces, metrics and events continue to export normally.

Sensitive content

This validation setup deliberately enables OTEL_LOG_USER_PROMPTS, OTEL_LOG_ASSISTANT_RESPONSES, OTEL_LOG_TOOL_DETAILS, OTEL_LOG_TOOL_CONTENT, and OTEL_LOG_RAW_API_BODIES. It can store prompts, responses, source files, command output, tool inputs, API conversation history, and OAuth account identity attributes. The local stack stores this data in the Docker volume claude-observability_lgtm-data.

Use synthetic test data, keep all ports on loopback, protect the local Grafana password, and delete the volume after sensitive test campaigns. Inline bodies are truncated at Claude's content limit; extended thinking remains redacted.

Validate and troubleshoot

Validate the rendered Compose model and inspect service status:

docker compose config --quiet
docker compose ps
docker compose logs otel-collector

Check the collector health endpoint:

Invoke-WebRequest http://localhost:13133

On macOS/Linux:

curl -fsS http://localhost:13133/

If Grafana has no data:

  • Confirm Claude was launched through the wrapper or equivalent settings.
  • Wait for the configured export intervals and keep the Claude process running.
  • Check collector logs for failed exports to lgtm:4317.
  • Ensure ports 3300, 4317, 4318, and 13133 are unused, or change GRAFANA_PORT in .env if 3300 is occupied.
  • When Claude runs inside a container, localhost points to that container. Use a reachable host address such as host.docker.internal instead.

Stop or reset

Stop containers while retaining telemetry:

docker compose down

Delete containers and all locally stored telemetry:

docker compose down --volumes

Implementation notes

  • compose.yaml keeps ingestion separate from storage/visualization. This makes the collector the stable routing and processing boundary.
  • otel-collector/config.yaml accepts gRPC and HTTP OTLP for metrics, logs, and traces; applies a memory limiter, tags records with the local environment and route, batches them, and forwards them with retry and queueing.
  • grafana/otel-lgtm supplies preconfigured Grafana data sources and the local Mimir, Loki, and Tempo backends. Its /data directory is persisted in a named volume.
  • The LGTM image's own OTLP ports remain internal to Docker; host ports 4317 and 4318 belong only to the standalone collector, preventing an accidental bypass.

References:

About

Local Claude Code observability with OpenTelemetry and Grafana LGTM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages