# 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](/enterprise/compliance-audit-and-monitoring).

<CardGroup cols={2}>
  <Card title="Self-host via OTEL metrics export" icon="chart-line" href="#self-hosted-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.
  </Card>
  <Card title="Factory-hosted Analytics API" icon="cloud" href="/api-reference/analytics">
    Read aggregated usage, cost, and productivity data from Factory's cloud via the [Analytics API](/api-reference/analytics). Backed by internal tracing that feeds Factory's analytics pipeline.
  </Card>
</CardGroup>

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](#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.

<LabeledDivider label='Measurement surfaces' />

## 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

Set these environment variables before launching Droid:

```bash
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`.                                   |
| `OTEL_TELEMETRY_HEADERS`  | No       | Comma-separated `key=value` pairs sent as HTTP headers on every export. Values may contain `=` (e.g. base64 tokens). |
| `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. |

### How it works

- When `OTEL_TELEMETRY_ENDPOINT` is set, metrics are sent to your endpoint 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).

### Exported metrics

All metrics use the `droid.*` namespace.

<PropertyList>
  <Property name='droid.code.files_modified' type='Counter'>
    Files modified during a session.
  </Property>
  <Property name='droid.code.files_read' type='Counter'>
    Files read during a session.
  </Property>
  <Property name='droid.code.lines_modified' type='Counter'>
    Lines of code modified.
  </Property>
  <Property name='droid.git.commits' type='Counter'>
    Commits created.
  </Property>
  <Property name='droid.git.pull_requests' type='Counter'>
    Pull requests created.
  </Property>
  <Property name='droid.tool.invocations' type='Counter'>
    Tool invocations.
  </Property>
  <Property name='droid.tool.execution_time' type='Histogram'>
    Tool execution duration (ms).
  </Property>
  <Property name='droid.command.blocked' type='Counter'>
    Commands blocked by policy.
  </Property>
  <Property name='droid.mcp.tool_invocations' type='Counter'>
    MCP tool invocations.
  </Property>
  <Property name='droid.skill.invocations' type='Counter'>
    Skill invocations.
  </Property>
  <Property name='droid.skill.installed' type='Counter'>
    Skills installed.
  </Property>
  <Property name='droid.hook.invocations' type='Counter'>
    Hook invocations.
  </Property>
  <Property name='droid.slash_command.invocations' type='Counter'>
    Slash command invocations.
  </Property>
  <Property name='droid.auth.login_success' type='Counter'>
    Successful logins.
  </Property>
  <Property name='droid.repo.metadata' type='Gauge'>
    Repository metadata snapshot.
  </Property>
</PropertyList>

### Common attributes

Every data point includes these attributes automatically:

| Attribute         | Description              |
| :---------------- | :----------------------- |
| `user.id`         | Authenticated user ID    |
| `organization.id` | Organization ID          |
| `session.id`      | Current Droid session ID |

Tool-specific attributes (`tool.name`, `mcp.server`, `skill.name`, etc.) are included where applicable. Resource attributes include `service.name` (`cli`) and `service.version`.

### Message content logging

Message content export is disabled unless you set `OTEL_LOG_MESSAGE_CONTENT`. Metrics fan out to both Factory and your collector (see above); message content does not.

To enable message content export for the current process (for example, for session auditing), set:

```bash
export OTEL_LOG_MESSAGE_CONTENT=true   # or 1
```

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`)

<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 (`OTEL_TELEMETRY_ENDPOINT`) is **required**. If none is set, content is dropped rather than sent to Factory.
- 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.
</Note>

### 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

<Troubleshooting>
  <TroubleshootingItem title="Metrics are not reaching the collector">
    Verify your collector accepts OTLP HTTP on the `/v1/metrics` path.
  </TroubleshootingItem>

  <TroubleshootingItem title="Collector rejects telemetry">
    Confirm `OTEL_TELEMETRY_HEADERS` includes valid auth credentials.
  </TroubleshootingItem>

  <TroubleshootingItem title="Droid cannot reach the collector">
    Ensure network connectivity from the machine running Droid to your collector.
  </TroubleshootingItem>
</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](/api-reference/analytics) 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.

<LabeledDivider label='Using the data' />

## 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](/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](/api-reference/analytics) 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](/api-reference/analytics) 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.

<RelatedLinks>
  <RelatedLink href="/enterprise/compliance-audit-and-monitoring" title="Compliance & Audit">
    Map telemetry signals to audit trails and regulatory workflows.
  </RelatedLink>
  <RelatedLink href="/enterprise/hierarchical-settings-and-org-control" title="Enterprise Controls & Managed Settings">
    Set model policies and autonomy ceilings that drive cost and usage.
  </RelatedLink>
</RelatedLinks>
