docs/developers/development/telemetry.md
Learn how to enable and setup OpenTelemetry for Qwen Code.
Built on OpenTelemetry ā the vendor-neutral, industry-standard observability framework ā Qwen Code's observability system provides:
All telemetry behavior is controlled through your .qwen/settings.json file.
These settings can be overridden by environment variables or CLI flags.
| Setting | Environment Variable | CLI Flag | Description | Values | Default |
|---|---|---|---|---|---|
enabled | QWEN_TELEMETRY_ENABLED | --telemetry / --no-telemetry | Enable or disable telemetry | true/false | false |
target | QWEN_TELEMETRY_TARGET | --telemetry-target <local|gcp> (deprecated) | Informational destination label; does not control exporter routing ā set otlpEndpoint or outfile to configure where data is sent | "gcp"/"local" | "local" |
otlpEndpoint | QWEN_TELEMETRY_OTLP_ENDPOINT | --telemetry-otlp-endpoint <URL> | OTLP collector endpoint | URL string | http://localhost:4317 |
otlpProtocol | QWEN_TELEMETRY_OTLP_PROTOCOL | --telemetry-otlp-protocol <grpc|http> | OTLP transport protocol | "grpc"/"http" | "grpc" |
otlpTracesEndpoint | QWEN_TELEMETRY_OTLP_TRACES_ENDPOINT | - | Per-signal endpoint override for traces (HTTP only) | URL string | - |
otlpLogsEndpoint | QWEN_TELEMETRY_OTLP_LOGS_ENDPOINT | - | Per-signal endpoint override for logs (HTTP only) | URL string | - |
otlpMetricsEndpoint | QWEN_TELEMETRY_OTLP_METRICS_ENDPOINT | - | Per-signal endpoint override for metrics (HTTP only) | URL string | - |
outfile | QWEN_TELEMETRY_OUTFILE | --telemetry-outfile <path> | Save telemetry to file (overrides OTLP export) | file path | - |
logPrompts | QWEN_TELEMETRY_LOG_PROMPTS | --telemetry-log-prompts / --no-telemetry-log-prompts | Include prompts in telemetry logs | true/false | true |
includeSensitiveSpanAttributes | QWEN_TELEMETRY_INCLUDE_SENSITIVE_SPAN_ATTRIBUTES | - | Include user prompts, system prompts, tool I/O, and model output as native span attributes (in addition to log-to-span bridge spans) | true/false | false |
resourceAttributes | OTEL_RESOURCE_ATTRIBUTES (+ OTEL_SERVICE_NAME) | - | Static resource attributes attached to every exported span / log / metric. See Resource attributes below. | key=value,⦠| {} |
metrics.includeSessionId | QWEN_TELEMETRY_METRICS_INCLUDE_SESSION_ID | - | Include session.id on metric data points. Disabled by default to protect metric backends from time-series fan-out. | true/false | false |
Note on boolean environment variables: For the boolean settings (enabled,
logPrompts, includeSensitiveSpanAttributes), setting the
corresponding environment variable to true or 1 will enable the feature. Any
other value will disable it.
Sensitive span attributes: When includeSensitiveSpanAttributes is enabled,
two things happen:
Native span attributes (qwen-code.interaction, api.generateContent*,
tool.<name>) carry verbatim conversation content:
new_context)system_prompt ā full text once per session, deduped by
SHA-256 hash; subsequent spans only carry system_prompt_hash +
system_prompt_preview + system_prompt_length)tool_schema events, also hash-deduped)tool_input) and tool results (tool_result)response.model_output)Each value is truncated at 60 KB; *_truncated and *_original_length
flags surface when truncation occurs.
Log-to-span bridge spans (used when HTTP traces are exported without a
logs endpoint) keep their existing prompt, function_args, and
response_text fields, instead of being dropped.
ā ļø Security warning: enabling this flag streams full conversation history,
file contents read by read_file, shell commands and their output (including
secrets in env vars or arguments), and model responses to the configured OTLP
backend. Treat the backend as a privileged data sink. The flag defaults to
false.
Cost / payload size: A heavy turn (60 KB system prompt + 10 tool calls,
each up to 60 KB input + 60 KB result, plus 60 KB model output) can produce up
to ~1.5 MB of attribute payload before OTLP compression. When pointing tools
that read large files (read_file, etc.) at long-running sessions, monitor
exporter throughput.
This setting does not disable sensitive data in OTel logs or other telemetry
sinks; non-internal API response telemetry can populate response_text, so
OTel logs, UI telemetry, and chat recording may receive response text
independently of this setting. QwenLogger does not include response_text.
HTTP OTLP signal routing: When using HTTP protocol (otlpProtocol: "http"),
Qwen Code automatically appends signal-specific paths (/v1/traces, /v1/logs,
/v1/metrics) to the base otlpEndpoint. For example, http://collector:4318
becomes http://collector:4318/v1/traces for traces. If the URL already ends
with a signal path, it is used as-is. Per-signal endpoint overrides
(otlpTracesEndpoint, etc.) take precedence over the base endpoint and are used
verbatim. gRPC protocol uses service-based routing and does not append paths.
The per-signal endpoint environment variables also accept the standard
OpenTelemetry names: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.
The QWEN_TELEMETRY_OTLP_* variants take precedence over the OTEL_* variants.
For detailed information about all configuration options, see the Configuration Guide.
Resource attributes are static key-value pairs attached to every span, log, and metric exported via OTLP. Use them to slice telemetry by team, environment, deployment region, or any other dimension your backend cares about.
Two sources, merged in priority order (lowest ā highest):
OTEL_RESOURCE_ATTRIBUTES env vartelemetry.resourceAttributes in .qwen/settings.json (overrides env on
key conflict)OTEL_SERVICE_NAME is a separate escape hatch ā when set, it overrides
service.name from any other source (per the OpenTelemetry spec).
Slice all telemetry by team / environment:
export OTEL_RESOURCE_ATTRIBUTES="team=platform,env=prod,cost_center=eng-123"
Route to a per-tenant collector via service.name:
export OTEL_SERVICE_NAME=qwen-code-ci
Fleet baseline (~/.qwen/settings.json) + per-host override:
{
"telemetry": {
"resourceAttributes": {
"deployment.environment": "production",
"service.namespace": "engineering-tooling"
}
}
}
# Add a one-off tag without touching settings:
export OTEL_RESOURCE_ATTRIBUTES="debug_run=true"
Some keys are runtime-controlled and cannot be overridden:
service.version ā always set to the running CLI version. Setting it from
any source is silently dropped with a warning.session.id ā runtime-injected per session. User-provided values from
either env or settings are dropped with a warning. The reason is that
Resource attributes auto-attach to every metric data point; allowing user
override would bypass Cardinality controls below.
Spans and logs always carry session.id.service.name is not reserved; it follows the precedence chain above.
OTEL_RESOURCE_ATTRIBUTES follows the OpenTelemetry spec:
key1=value1,key2=value2 with values percent-encoded. Spaces in values must
be encoded as %20, commas as %2C (unencoded commas split the value at
the wrong boundary and the second half is dropped as malformed). Malformed
pairs are skipped with a warning rather than failing telemetry startup.
Reserved keys (service.version, session.id), malformed pairs, non-string
settings values, and invalid percent-encoding are all silently dropped with a
warning logged via the OpenTelemetry diagnostics channel. That channel routes
to the debug log file (~/.qwen/log/otel-*.log), not the console, so the
behavior can look like silent failure.
If a custom resource attribute isn't appearing on exported telemetry:
~/.qwen/log/otel-*.log for lines matching cannot override (reserved
key dropped), Skipping malformed (bad env var pair), or must be a string
(non-string settings value).telemetry.enabled is true ā telemetry init only runs if enabled.Metrics are aggregated by attribute set at the backend ā every distinct
combination of attribute values produces a new time series. Attaching a
high-cardinality field like session.id to a metric causes time-series fan-out
proportional to the number of sessions, which quickly exhausts metric backend
storage.
To prevent this, Qwen Code keeps high-cardinality attributes off metric data
points by default. Spans and logs are per-event and unaffected, so they
continue to carry session.id for trace and log correlation.
telemetry.metrics.includeSessionId (default: false)Setting this to true (via settings or
QWEN_TELEMETRY_METRICS_INCLUDE_SESSION_ID=true) re-attaches session.id to
every metric data point.
ā ļø Warning: each CLI session creates a new value. Leaving this on for a fleet will blow up metric storage. Recommended only for short-term debugging. For long-term session correlation, query trace or log backends instead.
Prior to this release, session.id was attached to metrics by default. If
your Prometheus queries / Grafana dashboards / alert rules reference
session_id on a metric, you have two options:
Option A ā restore the previous behavior for short-term debugging:
export QWEN_TELEMETRY_METRICS_INCLUDE_SESSION_ID=true
or:
{
"telemetry": {
"metrics": { "includeSessionId": true }
}
}
Option B (recommended) ā move session-level analysis off metrics. Spans
and logs still carry session.id, and trace / log backends (Jaeger, Tempo,
Loki, Aliyun SLS / ARMS Tracing) handle per-session slicing natively without
cardinality pressure.
To view Qwen Code telemetry in Alibaba Cloud Managed Service for OpenTelemetry, configure Qwen Code to export to the OTLP endpoint provided by ARMS.
Setting "target": "gcp" alone does not configure the export
destination. If otlpEndpoint is not set, Qwen Code still defaults to
http://localhost:4317. If outfile is set, it overrides
otlpEndpoint and telemetry is written to the file instead of being
sent to Alibaba Cloud.
Enable telemetry in your .qwen/settings.json and set the OTLP
endpoint:
Option A: gRPC protocol (standard OTLP endpoint):
{
"telemetry": {
"enabled": true,
"target": "gcp",
"otlpEndpoint": "https://<your-otlp-endpoint>",
"otlpProtocol": "grpc"
}
}
Option B: HTTP protocol with per-signal endpoints (for backends
that use non-standard paths, e.g., /api/otlp/traces instead of
/v1/traces):
{
"telemetry": {
"enabled": true,
"otlpProtocol": "http",
"otlpTracesEndpoint": "http://<host>/<token>/api/otlp/traces",
"otlpLogsEndpoint": "http://<host>/<token>/api/otlp/logs",
"otlpMetricsEndpoint": "http://<host>/<token>/api/otlp/metrics"
}
}
Note: When using HTTP protocol with only
otlpEndpoint(no per-signal overrides), Qwen Code appends standard OTLP paths (/v1/traces,/v1/logs,/v1/metrics) to the base URL. If your backend uses different paths, use per-signal endpoint overrides as shown in Option B.
If your Alibaba Cloud endpoint requires authentication, provide OTLP
headers through standard OpenTelemetry environment variables such as
OTEL_EXPORTER_OTLP_HEADERS (or the signal-specific variants). Qwen
Code does not currently expose OTLP auth headers directly in
.qwen/settings.json.
Run Qwen Code and send prompts.
View telemetry in Managed Service for OpenTelemetry:
Applications to inspect traces and service
topology.trace.console.aliyun.com or international):
navigate to Integration Center.tracing.console.aliyun.com): navigate to
Cluster Configurations ā Access point information.For local development and debugging, you can capture telemetry data locally:
Enable telemetry in your .qwen/settings.json:
{
"telemetry": {
"enabled": true,
"outfile": ".qwen/telemetry.log"
}
}
Note: When
outfileis set, OTLP export is automatically disabled. ThetargetandotlpEndpointsettings are not needed for file-only output and can be safely omitted from your config.
Run Qwen Code and send prompts.
View logs and metrics in the specified file (e.g., .qwen/telemetry.log).
npm run telemetry -- --target=local
~/.qwen/tmp/<projectHash>/otel/collector.logCtrl+C)The following section describes the structure of logs and metrics generated for Qwen Code.
sessionId is included as a common attribute on all logs and metrics.Logs are timestamped records of specific events. The following events are logged for Qwen Code:
qwen-code.config: This event occurs once at startup with the CLI's configuration.
model (string)sandbox_enabled (boolean)core_tools_enabled (string)approval_mode (string)file_filtering_respect_git_ignore (boolean)debug_mode (boolean)truncate_tool_output_threshold (number)truncate_tool_output_lines (number)hooks (string, comma-separated hook event types, omitted if hooks disabled)ide_enabled (boolean)interactive_shell_enabled (boolean)mcp_servers (string)output_format (string: "text" or "json")qwen-code.user_prompt: This event occurs when a user submits a prompt.
prompt_length (int)prompt_id (string)prompt (string, this attribute is excluded if log_prompts_enabled is
configured to be false)auth_type (string)qwen-code.tool_call: This event occurs for each function call.
function_namefunction_argsduration_mssuccess (boolean)decision (string: "accept", "reject", "auto_accept", or "modify", if
applicable)error (if applicable)error_type (if applicable)content_length (int, if applicable)metadata (if applicable, dictionary of string -> any)qwen-code.file_operation: This event occurs for each file operation.
tool_name (string)operation (string: "create", "read", "update")lines (int, if applicable)mimetype (string, if applicable)extension (string, if applicable)programming_language (string, if applicable)diff_stat (json string, if applicable): A JSON string with the following members:
ai_added_lines (int)ai_removed_lines (int)user_added_lines (int)user_removed_lines (int)qwen-code.api_request: This event occurs when making a request to Qwen API.
modelrequest_text (if applicable)qwen-code.api_error: This event occurs if the API request fails.
modelerrorerror_typestatus_codeduration_msauth_typeqwen-code.api_response: This event occurs upon receiving a response from Qwen API.
modelstatus_codeduration_mserror (optional)input_token_countoutput_token_countcached_content_token_countthoughts_token_countresponse_text (if applicable)auth_typeqwen-code.tool_output_truncated: This event occurs when the output of a tool call is too large and gets truncated.
tool_name (string)original_content_length (int)truncated_content_length (int)threshold (int)lines (int)prompt_id (string)qwen-code.malformed_json_response: This event occurs when a generateJson response from Qwen API cannot be parsed as a json.
modelqwen-code.flash_fallback: This event occurs when Qwen Code switches to flash as fallback.
auth_typeqwen-code.slash_command: This event occurs when a user executes a slash command.
command (string)subcommand (string, if applicable)qwen-code.extension_enable: This event occurs when an extension is enabled
qwen-code.extension_install: This event occurs when an extension is installed
extension_name (string)extension_version (string)extension_source (string)status (string)qwen-code.extension_uninstall: This event occurs when an extension is uninstalled
Metrics are numerical measurements of behavior over time. The following metrics are collected for Qwen Code (metric names remain qwen-code.* for compatibility):
qwen-code.session.count (Counter, Int): Incremented once per CLI startup.
qwen-code.tool.call.count (Counter, Int): Counts tool calls.
function_namesuccess (boolean)decision (string: "accept", "reject", or "modify", if applicable)tool_type (string: "mcp", or "native", if applicable)qwen-code.tool.call.latency (Histogram, ms): Measures tool call latency.
function_namedecision (string: "accept", "reject", or "modify", if applicable)qwen-code.api.request.count (Counter, Int): Counts all API requests.
modelstatus_codeerror_type (if applicable)qwen-code.api.request.latency (Histogram, ms): Measures API request latency.
modelqwen-code.token.usage (Counter, Int): Counts the number of tokens used.
modeltype (string: "input", "output", "thought", or "cache")qwen-code.file.operation.count (Counter, Int): Counts file operations.
operation (string: "create", "read", "update"): The type of file operation.lines (Int, if applicable): Number of lines in the file.mimetype (string, if applicable): Mimetype of the file.extension (string, if applicable): File extension of the file.model_added_lines (Int, if applicable): Number of lines added/changed by the model.model_removed_lines (Int, if applicable): Number of lines removed/changed by the model.user_added_lines (Int, if applicable): Number of lines added/changed by user in AI proposed changes.user_removed_lines (Int, if applicable): Number of lines removed/changed by user in AI proposed changes.programming_language (string, if applicable): The programming language of the file.qwen-code.chat_compression (Counter, Int): Counts chat compression operations
tokens_before: (Int): Number of tokens in context prior to compressiontokens_after: (Int): Number of tokens in context after compression