What is Qwen Code Observability? #
Qwen Code is Alibaba's open-source terminal coding agent. It ships its own OpenTelemetry exporter, so instrumenting it is a matter of configuration: there is no library to install and no collector to run. Once enabled it emits all three signals, with traces covering every agent turn, model call, and tool execution, metrics covering token usage and runtime health, and structured log events for each session.
With full Qwen Code observability in SigNoz, you can see how many tokens your team is spending and on which models, how much of each prompt is served from cache, how many round trips a single request actually takes, which tools the agent reaches for, and how much you are paying for background work nobody asked for.
Prerequisites #
-
SigNoz setup (choose one): SigNoz Cloud accountwith an active ingestion key- Self-hosted SigNoz instance
-
Qwen Code 0.21.15 or later, installed with
npm install -g @qwen-code/qwen-code -
Node.js 20 or later
-
A configured model provider, such as a DashScope API key or any OpenAI-compatible endpoint
Monitor Qwen Code with OpenTelemetry #
Telemetry is built into the CLI and turned off by default. Enabling it takes one settings block and one environment variable.
Step 1: Enable telemetry in .qwen/settings.json
Create the file in your project directory, or in ~/.qwen/settings.json
to apply it to every project.
{
"telemetry": {
"enabled": true,
"otlpProtocol": "http",
"otlpEndpoint": "https://ingest.<region>.signoz.cloud:443",
"metrics": { "includeSessionId": true }
}
}
Step 2: Set the ingestion key and service name
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export OTEL_SERVICE_NAME=qwen-code
Verify these values:
<region>
: YourSigNoz Cloud region.<your-ingestion-key>
: Your SigNozingestion key.
Step 3: Run the agent
qwen "list the files in this directory and summarise the project"
Spans are batched, so allow a few seconds after the turn finishes before looking in SigNoz.
View Qwen Code Traces in SigNoz #
Open the Traces explorer and filter on service.name = 'qwen-code'
. Each turn arrives as its own trace.
Open any qwen-code.interaction
span to see the full turn. The waterfall shows each model call with the raw HTTP request underneath it, and each tool alongside the hook that fired for it.
Note that the trace above reports three errors even though the root span status is Ok
. All three are hook spans, and the warning below explains why that is expected.
Qwen Code emits six span names, and the hierarchy matters when you write queries:
qwen-code.interaction root, one per user turn
├── qwen-code.llm_request the model call
│ └── POST (CLIENT) auto-instrumented HTTP to the provider
└── qwen-code.tool
├── qwen-code.tool.execution child of qwen-code.tool, not a sibling
└── qwen-code.hook fires per tool call
| Span | What it covers |
|---|---|
qwen-code.interaction |
One user turn, the root span |
qwen-code.llm_request |
One model call, with tokens, finish reason, and time to first chunk |
POST |
The raw HTTP request to the provider, with status code |
qwen-code.tool |
One tool call, with the tool name and outcome |
qwen-code.tool.execution |
The execution phase of that same tool call |
qwen-code.hook |
The hook runner, invoked per tool call |
Attributes Worth Knowing #
qwen-code.llm_request
carries the token counts and model metadata. A few names differ from what you may expect from other agents:
There is no Sumgen_ai.usage.total_tokens
.gen_ai.usage.input_tokens
andgen_ai.usage.output_tokens
instead.holds cached input tokens, and the segment is dotted rather than underscored.gen_ai.usage.cache_read.input_tokens
, so values render asgen_ai.response.finish_reasons
is a real array["stop"]
and["tool_calls"]
rather than as plain strings.Reasoning tokens are, not athoughts_token_count
gen_ai.*
name. On current builds it reports zero.filters correctly when you quote the value (success
onqwen-code.tool
success = 'false'
) but does not group reliably, so build success-versus-failure breakdowns from two filtered queries rather than agroupBy
.
session.id
, gen_ai.conversation.id
, and qwen-code.prompt_id
correlate spans across a whole session.
Background Subagents Spend Tokens You Did Not Ask For #
Qwen Code runs background subagents, such as the auto-memory extractor and the dreamer, that make their own model calls. On a measured sample of 80 model calls, 18 of them, or 22%, came from background subagents rather than from a user prompt. A single "What is 2+2?" prompt triggered a subagent that consumed 7,004 input tokens on its own.
None of this is visible in the terminal. llm_request.context
separates the two cleanly: interaction
for calls made for a user turn, standalone
for background work. subagent_name
names the subagent on the calls that have one. Group token sums by llm_request.context
to see what background work actually costs you.
Metrics and Logs #
Alongside traces, Qwen Code exports a full metric set including qwen-code.token.usage
, qwen-code.api.request.count
, qwen-code.api.request.latency
, qwen-code.tool.call.count
, qwen-code.session.count
, qwen-code.file.operation.count
, qwen-code.cpu.usage
, and qwen-code.memory.usage
. Histograms arrive split into .bucket
, .count
, .sum
, .min
, and .max
series.
It also emits structured log events per session, including session.start
, qwen-code.user_prompt
, qwen-code.api_request
, qwen-code.api_response
, qwen-code.subagent_execution
, and session.end
.
Qwen Code Observability Dashboard #
The Qwen Code dashboard gives you token spend and cache efficiency, turn and session volume, model latency and time to first chunk, tool activity, and the user-driven versus background split out of the box.
Troubleshooting Qwen Code Observability #
No data in SigNoz
An expired or wrong ingestion key fails silently. The CLI answers normally and exits successfully while the exporter receives a 401, so nothing in the terminal tells you anything is wrong. Test the key directly:
curl -i -X POST "https://ingest.<region>.signoz.cloud/v1/traces" \
-H "content-type: application/json" \
-H "signoz-ingestion-key: <your-ingestion-key>" \
-d '{"resourceSpans":[]}'
A working key returns 200
with {"partialSuccess":{}}
. An expired one returns 401
with Expired key
.
Telemetry is not being sent at all
Confirm the settings file the CLI actually loaded. A .qwen/settings.json
in a different project directory has no effect on the run you are debugging. Check that telemetry.enabled
is true
and that otlpEndpoint
carries no path: the exporter appends /v1/traces
itself.
Everything looks like it is failing
Check whether the errors are qwen-code.hook
spans. Those report an error by design with no hooks configured and are usually the large majority of error spans. Filter them out with name != 'qwen-code.hook'
.
Tool call counts look twice as high as expected
qwen-code.tool.execution
is a child of qwen-code.tool
and both carry gen_ai.tool.name
. Scope tool queries to name = 'qwen-code.tool'
.
Token totals come out empty
There is no gen_ai.usage.total_tokens
attribute on Qwen Code spans. Sum gen_ai.usage.input_tokens
and gen_ai.usage.output_tokens
instead.
Grouping by success
returns nothing
Filter it with a quoted value instead, and use two filtered queries for a success-versus-failure split.
Related integrations #
Instrument the other AI coding agents your team runs, using the same OpenTelemetry pipeline:
Monitor Claude Code with OpenTelemetry- token usage, cost, session activity, and tool decisionsGrok Build observability with OpenTelemetry- another agent with a native exporter and no library to installDeepSeek Harness observability with OpenTelemetry- an agent that instruments through a plugin insteadMonitor OpenAI Codex with OpenTelemetry- trace Codex runs from the IDE extension and the CLIMonitor GitHub Copilot with OpenTelemetry- trace Copilot chat requests, models, and token spend
Looking for the Qwen model API rather than the CLI agent? See Qwen observability.
Browse all LLM observability integrations to instrument the rest of your stack.