|
| 1 | +# Performance tracing (PerfTrace) and OTEL export |
| 2 | + |
| 3 | +Corbits Code measures session performance with an always-on local tracer |
| 4 | +(`src/perf/`). Optional OpenTelemetry export sends the same span tree to **your** |
| 5 | +collector. This is separate from product analytics — see `docs/TELEMETRY.md` for |
| 6 | +PostHog usage events. |
| 7 | + |
| 8 | +## Local sink (always on) |
| 9 | + |
| 10 | +- In-process ring buffer of phase spans (turn, inference, tools, …) |
| 11 | +- Privacy-strict tags: enums, ids, and numbers only — no prompts, paths, tool |
| 12 | + args, free-text errors, or credentials |
| 13 | +- Future session dumps (CL-5169) use the same allowlist and must never include |
| 14 | + OTEL auth headers |
| 15 | + |
| 16 | +Local measurement does not require any settings or env vars. |
| 17 | + |
| 18 | +## OTEL export (opt-in) |
| 19 | + |
| 20 | +Export is **off** until an OTLP endpoint is configured. When enabled, traces go |
| 21 | +to the operator-owned backend you point at — not Corbits product analytics. |
| 22 | + |
| 23 | +The settings/env surface is implemented now (`src/perf/otel-config.ts`). The |
| 24 | +actual OTLP transport lands in a follow-up (CL-5173). Invalid config fails |
| 25 | +closed with a stable error code `OTEL_CONFIG_INVALID` and does not half-enable |
| 26 | +export. |
| 27 | + |
| 28 | +### Configuration |
| 29 | + |
| 30 | +**Env vars (preferred for secrets; match OTEL conventions):** |
| 31 | + |
| 32 | +| Variable | Meaning | |
| 33 | +|---|---| |
| 34 | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP base URL (`http` or `https` only) | |
| 35 | +| `OTEL_EXPORTER_OTLP_HEADERS` | Comma-separated `key=value` headers (values may be percent-encoded) | |
| 36 | +| `OTEL_SERVICE_NAME` | Resource `service.name` (default: `corbits-code`) | |
| 37 | +| `OTEL_RESOURCE_ATTRIBUTES` | Comma-separated `key=value` resource attributes | |
| 38 | + |
| 39 | +**Global settings** (`~/.corbits/settings.json`), optional `otel` block: |
| 40 | + |
| 41 | +```json |
| 42 | +{ |
| 43 | + "otel": { |
| 44 | + "enabled": true, |
| 45 | + "endpoint": "https://collector.example/v1", |
| 46 | + "headers": { "Authorization": "Bearer …" }, |
| 47 | + "serviceName": "corbits-code", |
| 48 | + "resourceAttributes": { |
| 49 | + "deployment.environment": "dev" |
| 50 | + } |
| 51 | + } |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +Precedence: |
| 56 | + |
| 57 | +- **endpoint:** env overrides settings |
| 58 | +- **headers:** when `OTEL_EXPORTER_OTLP_HEADERS` is set, it fully replaces |
| 59 | + settings headers (prefer env so secrets stay out of the settings file) |
| 60 | +- **serviceName:** env > settings > `corbits-code` |
| 61 | +- **resourceAttributes:** settings merged with env; env wins on key conflict |
| 62 | +- **`otel.enabled: false`:** disables export when only settings provide an |
| 63 | + endpoint; an explicit env endpoint still enables export |
| 64 | + |
| 65 | +Do not put credentials in the endpoint URL (`https://user:pass@…` is rejected). |
| 66 | +Use headers instead. |
| 67 | + |
| 68 | +### Fail closed |
| 69 | + |
| 70 | +Any of the following yields `OTEL_CONFIG_INVALID` and must not start export: |
| 71 | + |
| 72 | +- Endpoint that is not a valid `http`/`https` URL |
| 73 | +- Credentials embedded in the endpoint URL |
| 74 | +- Headers (settings or env) without an endpoint |
| 75 | +- `otel.enabled: true` without an endpoint |
| 76 | +- Malformed `key=value` lists for headers or resource attributes |
| 77 | + |
| 78 | +No endpoint and no half-config → export stays disabled (not an error). |
| 79 | + |
| 80 | +### Secrets and dumps |
| 81 | + |
| 82 | +- Header **values** are secrets. Prefer env for them. |
| 83 | +- `otelConfigForDump()` exposes only: enabled flag, endpoint, service name, |
| 84 | + resource attributes, and header **names** — never values. |
| 85 | +- Local privacy-strict dump writers must call `otelConfigForDump` (or omit OTEL |
| 86 | + config entirely). Never serialize `OtelExportConfig.headers` into session |
| 87 | + artifacts, logs, or crash dumps. |
| 88 | + |
| 89 | +### Targeting common collectors |
| 90 | + |
| 91 | +Examples assume the OTLP HTTP base URL your collector documents. Paths such as |
| 92 | +`/v1/traces` are appended by the exporter (CL-5173), not by this settings layer. |
| 93 | + |
| 94 | +#### Arize Phoenix |
| 95 | + |
| 96 | +Local Phoenix typically listens for OTLP HTTP on port 6006: |
| 97 | + |
| 98 | +```bash |
| 99 | +export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:6006" |
| 100 | +export OTEL_SERVICE_NAME="corbits-code" |
| 101 | +``` |
| 102 | + |
| 103 | +Cloud / authenticated Phoenix: set the project endpoint and pass the API key as |
| 104 | +a header (exact header name follows Phoenix’s current docs): |
| 105 | + |
| 106 | +```bash |
| 107 | +export OTEL_EXPORTER_OTLP_ENDPOINT="https://app.phoenix.arize.com/v1/traces" |
| 108 | +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20<phoenix-api-key>" |
| 109 | +export OTEL_SERVICE_NAME="corbits-code" |
| 110 | +``` |
| 111 | + |
| 112 | +#### PostHog OTEL |
| 113 | + |
| 114 | +PostHog can ingest OTLP independently of Corbits product telemetry. Use your |
| 115 | +project’s OTEL endpoint and project API key as documented by PostHog: |
| 116 | + |
| 117 | +```bash |
| 118 | +export OTEL_EXPORTER_OTLP_ENDPOINT="https://us.i.posthog.com/i/v0/otlp" |
| 119 | +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20<phc_…>" |
| 120 | +export OTEL_SERVICE_NAME="corbits-code" |
| 121 | +``` |
| 122 | + |
| 123 | +This does **not** expand the three PostHog product events in `docs/TELEMETRY.md`. |
| 124 | +Product analytics opt-out (`CORBITS_TELEMETRY`, `DO_NOT_TRACK`, settings) does |
| 125 | +not control OTEL export, and vice versa. |
| 126 | + |
| 127 | +#### Generic OTLP collector (Jaeger, Grafana Alloy, otel-collector, …) |
| 128 | + |
| 129 | +Point at any OTLP-compatible base URL: |
| 130 | + |
| 131 | +```bash |
| 132 | +export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" |
| 133 | +export OTEL_SERVICE_NAME="corbits-code" |
| 134 | +export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=local,service.namespace=dev" |
| 135 | +``` |
| 136 | + |
| 137 | +Or in settings without secrets: |
| 138 | + |
| 139 | +```json |
| 140 | +{ |
| 141 | + "otel": { |
| 142 | + "endpoint": "http://localhost:4318", |
| 143 | + "serviceName": "corbits-code", |
| 144 | + "resourceAttributes": { |
| 145 | + "deployment.environment": "local" |
| 146 | + } |
| 147 | + } |
| 148 | +} |
| 149 | +``` |
| 150 | + |
| 151 | +Then supply auth only via env when needed. |
| 152 | + |
| 153 | +## Relationship to product telemetry |
| 154 | + |
| 155 | +| Pipe | Purpose | Default | Content | |
| 156 | +|---|---|---|---| |
| 157 | +| PostHog (`docs/TELEMETRY.md`) | Aggregate product usage | Opt-out | Three allowlisted events | |
| 158 | +| Local PerfTrace | Operator/dev attribution | Always on | Privacy-strict phase spans | |
| 159 | +| OTEL export | Your APM / Phoenix / collector | Opt-in | Full span tree when enabled | |
| 160 | + |
| 161 | +Do not enlarge the PostHog event schema for performance diagnostics. |
0 commit comments