What is Cursor IDE Observability?
Cursor is an AI-native code editor whose agent plans and executes multi-step work: it reads files, runs shell commands, calls MCP servers, and edits code on your behalf. Cursor's own usage console shows what your team spent. What it does not show is what the agent actually did to spend it: which tools ran, how long each took, and which ones failed.
This guide closes that gap using OpenTelemetry. Cursor's agent hooks fire on every step of the agent loop, and a hook runner turns those events into OTLP traces. The result is one trace per agent turn carrying both token counts and the full tool sequence, on any Cursor plan.
With Cursor IDE observability in SigNoz, you can see how many tokens your team is spending and on which models, how long a turn actually takes end to end, which tools the agent reaches for most, which ones fail and why, and how all of that breaks down per repository and branch.
Prerequisites
- SigNoz setup (choose one):
- SigNoz Cloud account with an active ingestion key
- Self-hosted SigNoz instance
- Cursor IDE on macOS or Linux
- Python 3.11 or later
Monitor Cursor IDE with OpenTelemetry
Cursor has no built-in OTLP exporter outside its Enterprise plan, and that path emits metrics and logs only, with no traces. Instead, this setup uses Cursor's agent hooks with opentelemetry-hooks, an MIT-licensed runner that converts hook events into OTLP spans.
Step 1: Install the hook runner in its own environment so it does not touch your system Python.
python3 -m venv ~/.local/opt/otel-hook
~/.local/opt/otel-hook/bin/pip install opentelemetry-hooksStep 2: Register it with Cursor. This writes ~/.cursor/hooks.json.
~/.local/opt/otel-hook/bin/otel-hook setup --agent cursorStep 3: Add the event that carries token counts.
python3 - <<'EOF'
import json, os
p = os.path.expanduser("~/.cursor/hooks.json")
d = json.load(open(p))
h = d.get("hooks", d)
h["afterAgentResponse"] = json.loads(json.dumps(h["stop"]))
json.dump(d, open(p, "w"), indent=2)
print("events registered:", len(h))
EOFYou should see events registered: 16. If otel-hook is not on your PATH, rewrite the commands in ~/.cursor/hooks.json to the absolute path of the binary, otherwise Cursor silently fails to run the hook.
Step 4: Point the exporter at SigNoz. Edit ~/.local/share/opentelemetry-hooks/otel_config.json, keeping the other keys already in the file.
{
"OTEL_EXPORTER_OTLP_ENDPOINT": "https://ingest.<region>.signoz.cloud:443",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_EXPORTER_OTLP_HEADERS": "signoz-ingestion-key=<your-ingestion-key>",
"OTEL_EXPORTER_OTLP_INSECURE": "false",
"IDE_OTEL_BATCH_ON_STOP": "true"
}Verify these values:
<region>: Your SigNoz Cloud region.<your-ingestion-key>: Your SigNoz ingestion key.
The file holds your ingestion key, so restrict it with chmod 600 ~/.local/share/opentelemetry-hooks/otel_config.json.
Step 5: Quit Cursor completely and reopen it. Cursor reads ~/.cursor/hooks.json only at startup, so a running instance will not pick up the change. Confirm the hooks are active under Customize > Hooks, then send a prompt that uses a tool.
View Cursor IDE Traces in SigNoz
Allow a few minutes after your first prompt, then open the Traces explorer and filter on service.name = 'ide-agent'.

Opening a turn shows the full waterfall, with the agent's tool sequence nested under the turn and the gen_ai.* attributes on the right.

Attributes Worth Knowing
A turn produces a gen_ai.client.generation span with one gen_ai.client.hook.* child per hook event. Four details decide whether your queries are correct.
- Token counts arrive twice.
afterAgentResponseandStopboth carry identicalgen_ai.usage.input_tokensandgen_ai.usage.output_tokens. Filter to one of them or every token figure doubles. - Turn duration is on
gen_ai.client.generation. TheStopspan measures 1 ms on every turn and is a marker, not a measurement. - Tool failures are
PostToolUseFailure. A single failed tool call also sets error status on itsPreToolUseandPostToolUsespans, so counting error status roughly doubles the failure rate. service.nameis alwayside-agent. The runner hardcodes it for every agent it supports, so group bygen_ai.client.name(cursor) when you run more than one agent.
Every span also carries gen_ai.request.model, gen_ai.client.session_id, gen_ai.client.generation_id, and, when the runner resolves it, vcs.repository.name and vcs.ref.head.name.
View Cursor IDE Logs in SigNoz
The runner emits logs alongside the traces, one record per tool call, shell execution, and MCP call. Open the Logs Explorer and filter with service.name = 'ide-agent' to see the event stream.

Logs carry detail the spans do not. Open a record to see it in the attributes.

Useful attributes include gen_ai.client.command and gen_ai.client.shell.stdout for shell executions, gen_ai.client.tool_name and gen_ai.client.duration_ms for tool calls, and gen_ai.client.hook.event to separate the event types. Every record also carries trace_id and span_id, so a log line links back to the exact span in the turn's waterfall.
Cursor IDE Observability Dashboard
The Cursor IDE dashboard turns these spans into token spend by model, turn and tool latency, tool mix, and failure tracking.

Troubleshooting Cursor IDE Observability
No spans arrive at all
Check delivery locally before assuming the export is broken:
cat ~/.local/share/opentelemetry-hooks/.state/delivery_health.jsonA last_success_at_ns newer than last_failure_at_ns means export is working and you are looking at ingestion delay. Allow 2 to 4 minutes before re-checking SigNoz.
If there is no success timestamp, confirm Cursor was fully restarted after Step 2, and that Customize > Hooks lists the registered events.
Spans arrive but every token panel is empty
afterAgentResponse is not registered. Re-run Step 3 and confirm it prints events registered: 16, then restart Cursor. A restart is required whenever the registered event set changes.
Traces show a "missing span" placeholder
This is expected. The runner parents each turn to a session span that it never emits, so SigNoz shows the turn under a missing parent. The turn and its children are complete and every query works normally.
Repository shows as N/A on some spans
The runner resolves repository context once per session and does not always succeed, so vcs.repository.name can be absent even when the workspace is a git repository. Those spans are missing attribution rather than running outside a repository, which matters if you group token spend by repository.
Nothing is emitted from the Cursor CLI
This cannot be fixed by configuration. The Cursor CLI omits the beforeSubmitPrompt, afterAgentResponse, and stop hooks in non-interactive mode, and the runner builds a turn's spans only when Stop arrives. Use the Cursor IDE, or query Cursor's Admin API for team-level token and cost totals without per-tool detail.
Related integrations
Instrument the other AI coding agents your team runs, using the same OpenTelemetry pipeline:
- Monitor Claude Code with OpenTelemetry
- Monitor OpenAI Codex with OpenTelemetry
- OpenCode observability with OpenTelemetry
- Monitor GitHub Copilot with OpenTelemetry
Browse all LLM observability integrations to instrument the rest of your stack.