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.
- Docker Desktop or Docker Engine with Docker Compose v2
- Claude Code installed and authenticated
- PowerShell on Windows, or a POSIX shell on macOS/Linux
From a fresh clone, run one command.
Windows PowerShell:
.\start.ps1macOS, Linux, or WSL:
sh ./start.shThe 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) |
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:
claudeNo 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.
Allow at least 10 seconds after starting a Claude session, then open Grafana:
- Open Dashboards → Claude Observability.
- Use Claude Code Telemetry for the overall metrics, events, tools, plugins, costs, and token views.
- Open Claude Code — Session Drill-down to inspect all activity belonging to one Claude session, including its Tempo tool spans.
- 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 lgtmFor ad hoc exploration:
- Select Prometheus and query
{__name__=~"claude_code_.*"}. OTel metric names containing dots are normalized to underscores by the Prometheus-compatible backend. - Select Loki and start with
{service_name=~"claude-code|claude-code-desktop"}. - Select Tempo and search for
claude-codeorclaude-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.
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.
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 the rendered Compose model and inspect service status:
docker compose config --quiet
docker compose ps
docker compose logs otel-collectorCheck the collector health endpoint:
Invoke-WebRequest http://localhost:13133On 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_PORTin.envif 3300 is occupied. - When Claude runs inside a container,
localhostpoints to that container. Use a reachable host address such ashost.docker.internalinstead.
Stop containers while retaining telemetry:
docker compose downDelete containers and all locally stored telemetry:
docker compose down --volumescompose.yamlkeeps ingestion separate from storage/visualization. This makes the collector the stable routing and processing boundary.otel-collector/config.yamlaccepts 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-lgtmsupplies preconfigured Grafana data sources and the local Mimir, Loki, and Tempo backends. Its/datadirectory 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: