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.

Self-host via OTEL metrics export

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.

Self-host via OTEL metrics export
Factory-hosted Analytics API

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.

Factory-hosted Analytics API

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:

  1. 1
    Org-managed settings. A telemetry block in org-managed settings pins the sink for every member of the organization. See Org-managed telemetry settings.
  2. 2
    Environment 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:

JSON
{
  "telemetry": {
    "enabled": true,
    "endpoint": "https://collector.example.com:4318",
    "headers": { "Authorization": "Bearer ${OTEL_COLLECTOR_TOKEN}" },
    "metricExportIntervalMs": 60000,
    "logMessageContent": false
  }
}
enabledboolean

Master switch for the customer telemetry pipeline. true keeps it on even if the machine sets OTEL_CUSTOMER_ENABLED=false; false keeps it off even if the machine sets OTEL_CUSTOMER_ENABLED=true. Omit it to fall back to OTEL_CUSTOMER_ENABLED. Airgapped deployments disable telemetry regardless.

endpointstring

Base URL of your OTLP HTTP collector, taking precedence over OTEL_TELEMETRY_ENDPOINT. Must be an http/https URL 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 overrides OTEL_TELEMETRY_ENDPOINT on every machine, use a ${VAR_NAME} reference if machines legitimately need to point at different collectors.

headersobject

Static 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.

metricExportIntervalMsinteger

Metric flush interval in milliseconds, taking precedence over OTEL_METRIC_EXPORT_INTERVAL. Capped at 2147483647.

logMessageContentboolean

Whether 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 telemetry block in project, folder, or user settings is ignored with a warning, so a checked-in repository config or a developer's own settings.json cannot 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.
Warning

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.

Note

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.

Bash
export OTEL_TELEMETRY_ENDPOINT="https://your-collector.example.com:4318"
export OTEL_TELEMETRY_HEADERS="Authorization=Bearer <your-token>"
VariableRequiredDescription
OTEL_TELEMETRY_ENDPOINTYesBase 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_HEADERSNoComma-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_CONTENTNoSet 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_INTERVALNoMetric flush interval in milliseconds. Defaults to 60000.
OTEL_CUSTOMER_ENABLEDNoSet 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_ATTRIBUTESNoStandard OTEL key=value,key=value attributes. Applied to metrics only, not trace spans (see Resource attributes).
DROID_PARENT_SESSION_IDNoAdds 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_modifiedCounter

Files modified during a session.

droid.code.files_readCounter

Files read during a session.

droid.code.lines_modifiedCounter

Lines of code modified.

droid.git.commitsCounter

Commits created.

droid.git.pull_requestsCounter

Pull requests created.

droid.tool.invocationsCounter

Tool invocations.

droid.tool.execution_timeHistogram

Tool execution duration (ms).

droid.command.blockedCounter

Commands blocked by policy.

droid.mcp.tool_invocationsCounter

MCP tool invocations.

droid.skill.invocationsCounter

Skill invocations.

droid.skill.installedCounter

Skills installed.

droid.hook.invocationsCounter

Hook invocations.

droid.slash_command.invocationsCounter

Slash command invocations.

droid.auth.login_successCounter

Successful logins.

droid.repo.metadataGauge

Repository metadata snapshot.

Common attributes

Every data point includes these attributes automatically:

AttributeDescription
user.idAuthenticated user ID
user.emailAuthenticated user email
organization.idOrganization ID
session.idCurrent Droid session ID
model.idActive model ID (when available)
parent_session.idParent 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:

Bash
export OTEL_RESOURCE_ATTRIBUTES="team.id=platform,environment=ci"
Warning

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:

ControlEffect
telemetry.logMessageContent: true in org-managed settingsContent is exported on every member's machine, whether or not they set OTEL_LOG_MESSAGE_CONTENT.
telemetry.logMessageContent: false in org-managed settingsContent is never exported, even on a machine that sets OTEL_LOG_MESSAGE_CONTENT.
Property omitted from org-managed settingsEach 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:

Bash
export OTEL_LOG_MESSAGE_CONTENT=true   # or 1

The 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)
Warning

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.

Note

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.endpoint or OTEL_TELEMETRY_ENDPOINT) is required. If none is set, content is dropped rather than sent to Factory, including when the organization has set logMessageContent: 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

Bash
export OTEL_TELEMETRY_ENDPOINT="https://collector.example.com:4318"

Datadog via OTLP ingestion

Bash
export OTEL_TELEMETRY_ENDPOINT="http://localhost:4318"

Datadog direct OTLP intake

Bash
export OTEL_TELEMETRY_ENDPOINT="https://otlp.datadoghq.com"
export OTEL_TELEMETRY_HEADERS="dd-api-key=<your-datadog-api-key>"

New Relic

Bash
export OTEL_TELEMETRY_ENDPOINT="https://otlp.nr-data.net:4318"
export OTEL_TELEMETRY_HEADERS="api-key=<your-new-relic-license-key>"

Honeycomb

Bash
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.