Telemetry & Analytics
Measure Droid adoption, activity, and cost by exporting OpenTelemetry metrics to your own collector or reading Factory's hosted Analytics API.
Enterprise adoption requires more than a good developer experience. You need to understand who is using Droid, on what, and at what cost. There are two complementary ways to measure that, and you can use either or both.
Use this page for measurement setup and usage analytics. For audit posture, Trust Center materials, and compliance workflows, see Compliance & Audit.
Export Droid's OpenTelemetry metrics to your own OTLP-compatible collector and build dashboards in your existing observability stack. Metrics by default; message content can optionally be exported as trace spans to your endpoint only.
Read aggregated usage, cost, and productivity data from Factory's cloud via the Analytics API. Backed by internal tracing that feeds Factory's analytics pipeline.
Customer-side export is metrics only by default; message content can optionally be exported as trace spans to your own endpoint (see Message content logging). Content spans are not included in the separate export to Factory's collector. Factory's internal tracing is what powers the hosted Analytics API; it is never sent to your collectors.
Self-hosted OTEL metrics export
Droid can export OpenTelemetry (OTEL) metrics to your own OTLP-compatible collector, giving you visibility into Droid activity within your existing observability stack. Metrics are sent to both Factory's collector and yours in the same export cycle. If your collector is unreachable, Factory's own export is not affected.
Configuration
There are two places the sink can be configured, and they take precedence in this order:
- 1Org-managed settings. A
telemetryblock in org-managed settings pins the sink for every member of the organization. See Org-managed telemetry settings. - 2Environment variables on the developer's machine, used for any value the organization has not pinned. See Machine environment variables.
Org-managed telemetry settings
Environment variables live on the developer's machine, which makes them advisory: anyone can export OTEL_TELEMETRY_ENDPOINT=... to redirect the stream or switch it off. Organizations that need the sink enforced can pin it centrally with a telemetry block in org-managed settings, edited under Settings → Enterprise Controls → Raw Configuration:
{
"telemetry": {
"enabled": true,
"endpoint": "https://collector.example.com:4318",
"headers": { "Authorization": "Bearer ${OTEL_COLLECTOR_TOKEN}" },
"metricExportIntervalMs": 60000,
"logMessageContent": false
}
}enabledbooleanMaster switch for the customer telemetry pipeline.
truekeeps it on even if the machine setsOTEL_CUSTOMER_ENABLED=false;falsekeeps it off even if the machine setsOTEL_CUSTOMER_ENABLED=true. Omit it to fall back toOTEL_CUSTOMER_ENABLED. Airgapped deployments disable telemetry regardless.endpointstringBase URL of your OTLP HTTP collector, taking precedence over
OTEL_TELEMETRY_ENDPOINT. Must be anhttp/httpsURL with no query string or fragment, or a${VAR_NAME}reference standing in for the whole URL. Invalid values are rejected when you save, not on member machines. Because it overridesOTEL_TELEMETRY_ENDPOINTon every machine, use a${VAR_NAME}reference if machines legitimately need to point at different collectors.headersobjectStatic OTLP headers sent on every export, as a map of header name to value. Values may contain
${VAR_NAME}references. Headers are transport-only — used to authenticate and route the HTTP export — and are never attached to exported spans or metrics as attributes.metricExportIntervalMsintegerMetric flush interval in milliseconds, taking precedence over
OTEL_METRIC_EXPORT_INTERVAL. Capped at2147483647.logMessageContentbooleanWhether message content is exported. See Message content logging.
Three properties of the block are worth calling out:
- It is read from the organization level only. A
telemetryblock in project, folder, or user settings is ignored with a warning, so a checked-in repository config or a developer's ownsettings.jsoncannot redirect the stream. - It is distributed to every member, not just Managers and Owners, because the CLI reads it on each member's machine. Treat every value in it as readable by the whole organization.
- It configures your sink only. Factory's own collector is not affected by it.
Prefer a ${VAR_NAME} reference over a literal collector credential in headers, so the secret is distributed to machines out of band (MDM, a secret manager) and never stored in Factory's control plane.
If a referenced variable is not set on a machine, Droid disables the customer sink for that session and logs an error naming the missing variable, rather than shipping the literal ${VAR_NAME} to your collector.
When endpoint is set in the block, the OTEL_* header environment variables never apply to it, because a credential sitting in a developer's shell for unrelated tooling must not be shipped to the organization's collector. Omitting headers therefore means the org sink is called with none; put machine-local values in ${VAR_NAME} references inside headers instead.
The block is also read once, at startup. A session that begins while logged out has no org settings to read and runs on the OTEL_* environment for the life of that process.
Machine environment variables
Set these before launching Droid. Each one configures the sink only where the organization has left that value unpinned; a telemetry block in org-managed settings overrides the matching variable.
export OTEL_TELEMETRY_ENDPOINT="https://your-collector.example.com:4318"
export OTEL_TELEMETRY_HEADERS="Authorization=Bearer <your-token>"| Variable | Required | Description |
|---|---|---|
OTEL_TELEMETRY_ENDPOINT | Yes | Base URL of your OTLP HTTP collector. Metrics are sent to {endpoint}/v1/metrics. Falls back to OTEL_EXPORTER_OTLP_ENDPOINT. Not required when the organization pins telemetry.endpoint. |
OTEL_TELEMETRY_HEADERS | No | Comma-separated key=value pairs sent as HTTP headers on every export. Values may contain = (e.g. base64 tokens). Falls back to OTEL_EXPORTER_OTLP_HEADERS. Never applied to an org-pinned endpoint. |
OTEL_LOG_MESSAGE_CONTENT | No | Set to true or 1 to export message content (user/assistant messages, tool input/results) as trace spans to OTEL_TELEMETRY_ENDPOINT. Content is dropped if no customer endpoint is set. |
OTEL_METRIC_EXPORT_INTERVAL | No | Metric flush interval in milliseconds. Defaults to 60000. |
OTEL_CUSTOMER_ENABLED | No | Set to false to opt this machine out of customer telemetry entirely (useful in tests and local development). This is the same pipeline pinned by telemetry.enabled, not a separate stream. |
OTEL_RESOURCE_ATTRIBUTES | No | Standard OTEL key=value,key=value attributes. Applied to metrics only, not trace spans (see Resource attributes). |
DROID_PARENT_SESSION_ID | No | Adds parent_session.id to metrics and content spans, for linking headless/CI sub-sessions to a parent. |
How it works
- Once an endpoint is resolved, metrics are sent to it in the same export cycle via a fan-out exporter: no extra timers, no duplication of metric readers.
- Failures to your collector do not affect Factory's own export. Each endpoint is isolated.
- Metrics use delta temporality: each export contains only new values since the last flush (60-second intervals by default).
Exported metrics
All metrics use the droid.* namespace.
droid.code.files_modifiedCounterFiles modified during a session.
droid.code.files_readCounterFiles read during a session.
droid.code.lines_modifiedCounterLines of code modified.
droid.git.commitsCounterCommits created.
droid.git.pull_requestsCounterPull requests created.
droid.tool.invocationsCounterTool invocations.
droid.tool.execution_timeHistogramTool execution duration (ms).
droid.command.blockedCounterCommands blocked by policy.
droid.mcp.tool_invocationsCounterMCP tool invocations.
droid.skill.invocationsCounterSkill invocations.
droid.skill.installedCounterSkills installed.
droid.hook.invocationsCounterHook invocations.
droid.slash_command.invocationsCounterSlash command invocations.
droid.auth.login_successCounterSuccessful logins.
droid.repo.metadataGaugeRepository metadata snapshot.
Common attributes
Every data point includes these attributes automatically:
| Attribute | Description |
|---|---|
user.id | Authenticated user ID |
user.email | Authenticated user email |
organization.id | Organization ID |
session.id | Current Droid session ID |
model.id | Active model ID (when available) |
parent_session.id | Parent session, when DROID_PARENT_SESSION_ID is set |
Tool-specific attributes (tool.name, mcp.server, skill.name, etc.) are included where applicable. Resource attributes include service.name (cli), service.version, os.type, os.version, host.arch, and terminal.type.
Resource attributes
To attach your own identity or lineage attributes (for example team.id, repo, environment), set the standard OTEL variable per machine:
export OTEL_RESOURCE_ATTRIBUTES="team.id=platform,environment=ci"OTEL_RESOURCE_ATTRIBUTES is currently applied to metrics only, not to trace spans. Your custom attributes appear on droid.* metrics but not on the droid.message.* / droid.tool.* content spans, which carry a fixed resource (service.*, os.*, host.arch, terminal.type) plus the per-span common attributes above.
There is no org-managed (Raw Configuration) field for resource attributes, and headers cannot carry them. To attribute headless or CI runs, set OTEL_RESOURCE_ATTRIBUTES — and DROID_PARENT_SESSION_ID for lineage — in each runner's environment.
Message content logging
Message content export is off by default. Metrics fan out to both Factory and your collector (see above); message content does not.
Two controls decide whether it is exported, and the organization's is authoritative:
| Control | Effect |
|---|---|
telemetry.logMessageContent: true in org-managed settings | Content is exported on every member's machine, whether or not they set OTEL_LOG_MESSAGE_CONTENT. |
telemetry.logMessageContent: false in org-managed settings | Content is never exported, even on a machine that sets OTEL_LOG_MESSAGE_CONTENT. |
| Property omitted from org-managed settings | Each machine decides for itself with OTEL_LOG_MESSAGE_CONTENT. |
To enable message content export for the current process (for example, for session auditing) where the organization has left the decision open, set:
export OTEL_LOG_MESSAGE_CONTENT=true # or 1The organization-level control covers content only. Token-usage telemetry (droid.turn.complete and the droid.token.* metrics) is not message content and is unaffected, so an organization can suppress content and keep aggregate usage accounting.
When enabled, Droid emits message content as OTEL trace spans (on the /v1/traces path of your endpoint), covering:
- User messages (
droid.message.user) - Assistant responses (
droid.message.assistant) - Tool calls and their inputs (
droid.tool.call) - Tool results (
droid.tool.result)
Message content is exported raw, with no redaction. There is no PII scrubbing and no secret detection; the only transformation is truncation of any single attribute to 32,000 characters. These spans contain verbatim prompts, responses, tool inputs (including file contents and command arguments), and tool results. Make your data-classification decision accordingly before enabling.
Message content is sent only to the collector you configure. Unlike metrics, Droid does not fan out content spans to Factory's collector.
- A customer endpoint (
telemetry.endpointorOTEL_TELEMETRY_ENDPOINT) is required. If none is set, content is dropped rather than sent to Factory, including when the organization has setlogMessageContent: true. - If your endpoint resolves to the same URL as Factory's collector, content logging is disabled.
- Metrics export is unaffected and continues to fan out to both Factory and your collector.
Example configurations
Generic OTEL collector / Grafana Alloy
export OTEL_TELEMETRY_ENDPOINT="https://collector.example.com:4318"Datadog via OTLP ingestion
export OTEL_TELEMETRY_ENDPOINT="http://localhost:4318"Datadog direct OTLP intake
export OTEL_TELEMETRY_ENDPOINT="https://otlp.datadoghq.com"
export OTEL_TELEMETRY_HEADERS="dd-api-key=<your-datadog-api-key>"New Relic
export OTEL_TELEMETRY_ENDPOINT="https://otlp.nr-data.net:4318"
export OTEL_TELEMETRY_HEADERS="api-key=<your-new-relic-license-key>"Honeycomb
export OTEL_TELEMETRY_ENDPOINT="https://api.honeycomb.io"
export OTEL_TELEMETRY_HEADERS="x-honeycomb-team=<your-api-key>,x-honeycomb-dataset=droid-metrics"Troubleshooting
Hybrid deployments can add customer-owned OTEL pipelines. Fully airgapped deployments do not use Factory-hosted analytics and should use deployment-local observability patterns agreed during rollout.
Factory-hosted Analytics API
In cloud-managed deployments, Factory provides a hosted analytics view for platform and leadership teams. It is backed by Factory's internal tracing, which feeds an aggregation pipeline; this internal tracing is separate from the metrics you export above and is never sent to your collectors.
The Analytics API exposes aggregated, org-level data, including:
- Adoption metrics by org, team, and user.
- Model usage and performance trends.
- Token consumption and cost estimates for LLM usage.
- Tool usage and productivity signals.
Use the Analytics API when you want a hosted, aggregated view, particularly for token and cost data, which is not part of the customer OTEL metric set above.
Cost management strategies
LLM cost control is a combination of model policy, usage patterns, and observability.
Constrain the model catalog
Use org-level policies to limit which models are available.
- Prefer smaller models for everyday tasks; reserve large models for complicated refactors or design work.
- Disable experimental or high-cost models by default.
- Enforce model choices per environment, such as cheaper models in CI.
See Models for the current model catalog.
Tune autonomy and context usage
Higher autonomy and larger context windows consume more tokens.
- Set reasonable defaults for autonomy level and reasoning effort.
- Use hooks to cap context size or block unnecessary large prompts.
- Encourage teams to iterate with tighter scopes, such as specific directories instead of entire monorepos.
Monitor activity and cost
Combine both measurement surfaces:
- Feed exported activity metrics, including tool invocations, code activity, and git activity, into your observability stack to build per-team and per-tool dashboards.
- Use the Analytics API for token consumption and cost estimates, which are not exported as customer OTEL metrics.
- Alert on unusual spikes and compare trends before and after policy changes.
Measuring productivity impact
Cost only matters in the context of outcomes. You can correlate Droid usage with software delivery and quality metrics you already track.
Common approaches:
- Build dashboards from exported activity metrics (files and lines modified, commits, pull requests, tool invocations) per team and repository.
- Pull aggregated adoption and productivity signals from the Analytics API for leadership reporting.
- Measure how often Droid is involved in changes that reduce incidents, resolve alerts, or improve test coverage.
These analyses run entirely in your existing observability and analytics stack; Factory's role is to provide clean, structured signals from Droid.