# ADR 062: Direct OTLP Export to PostHog Without a Collector

- HTML version: https://robbiepalmer.me/projects/recipe-site/adrs/062-direct-otlp-export-to-posthog
- Project: Recipe Site (https://robbiepalmer.me/projects/recipe-site.md)
- Status: Accepted
- Date: 2026-07-25
- Supersedes: recipe-site:048-cloudflare-observability-destinations

## Summary

Export application traces and their correlated logs directly from the recipe site's instrumented
runtimes to PostHog over OTLP/HTTP protobuf. Do not operate an OpenTelemetry Collector yet. Continue
retaining Cloudflare's built-in logs and native PostHog log destination as an independent fallback.

This is an intentionally small-scale serverless decision, not a claim that collectors are
unnecessary in general. Direct export keeps traces, logs, product events, people, and session
replays in the PostHog project already chosen by
[ADR 028](/projects/recipe-site/adrs/028-posthog-analytics) and
[ADR 047](/projects/recipe-site/adrs/047-posthog-logs). It accepts weaker delivery guarantees and
PostHog tracing's alpha status to avoid introducing and operating another service or observability
vendor before the traffic or incident load justifies one.

## Context

Recipe operations cross independently executed compute boundaries. Logs in separate service views
cannot reliably identify which stage made one user's operation slow or fail. The system needs W3C
trace-context propagation, a single trace waterfall, logs carrying the active trace and span IDs,
and PostHog person/session identifiers for movement from an operational failure to the affected
user's replay and product journey.

An OpenTelemetry Collector is the usual production recommendation because it can batch, retry,
filter, redact, sample, transform, and fan out telemetry independently of application processes.
However, the OTel guidance also says
[direct backend export is reasonable for getting started and at small scale](https://opentelemetry.io/docs/collector/).
A conventional sidecar assumes a co-scheduled container or host process. Cloudflare Workers instead
run as short-lived, geographically distributed
[V8 isolates rather than per-application containers](https://developers.cloudflare.com/workers/reference/how-workers-works/).
There is therefore no local sidecar deployment unit. Adding a collector means provisioning a
separate, reachable, highly available
[collector gateway](https://opentelemetry.io/docs/collector/deploy/gateway/), with its own hosting,
TLS, authentication, scaling, monitoring, upgrades, and failure modes.

There is also a protocol gap in the otherwise attractive Cloudflare-managed path. PostHog requires
OTLP/HTTP protobuf for traces, while Cloudflare's destination export does not support OTLP binary
and [lists PostHog trace export as unsupported](https://developers.cloudflare.com/workers/observability/exporting-opentelemetry-data/).
Cloudflare can still export logs to PostHog, so that path remains useful as a fallback.

## Decision

Use the shared `packages/observability` OpenTelemetry library in instrumented server runtimes:

* export traces to the regional PostHog `/i/v1/traces` endpoint and correlated application logs to
  `/i/v1/logs`, authenticated with the PostHog project token;
* propagate W3C `traceparent` and `tracestate` over HTTP and serialize them into Workflow parameters
  across the durable, non-HTTP boundary;
* add `service.name`, deployment environment, route, recipe-import job ID, `posthogDistinctId`, and
  `sessionId` where applicable; do not attach request bodies, recipe contents, credentials, or other
  sensitive payloads;
* flush request telemetry through `waitUntil()` so export is outside response latency, and perform
  Workflow exports inside `step.do()` so replay does not duplicate side effects;
* treat telemetry as best effort: exporter errors must never fail an application request or
  ingestion job;
* sample all instrumented spans initially because traffic is low, then introduce explicit sampling
  before volume becomes material;
* retain Cloudflare Workers Logs and the existing `posthog-logs` destination for platform and
  `console` logs when direct application export is unavailable.

[PostHog's tracing documentation](https://posthog.com/docs/distributed-tracing/start-here) explicitly
supports standard OpenTelemetry clients and colocates traces with logs, product analytics, session
replay, and error tracking. It also explicitly labels distributed tracing **alpha** and warns that
the endpoint and setup may change. That maturity risk is accepted because loss of telemetry does not
affect application correctness, Cloudflare retains a fallback, and OTLP keeps the instrumentation
portable.

## Alternatives Considered

| Option                                                            | Advantages                                                                                                                                                                                              | Costs and gaps                                                                                                                                                                                                                                                                                                     | Decision                                                                   |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| **Direct OTLP to PostHog**                                        | No new platform or always-on service; application-controlled propagation across execution boundaries; one investigation surface connected to people, events, replays, and product impact; standard OTLP | PostHog tracing is alpha; each runtime carries exporter code; only best-effort buffering/retry; PostHog outage or isolate termination can lose telemetry; no collector-side redaction, tail sampling, or fan-out                                                                                                   | **Adopt now**                                                              |
| **OTel Collector gateway → PostHog**                              | Central batching, retries, filtering, redaction, sampling, routing, and backend changes; application exports to one stable endpoint                                                                     | No true Worker sidecar; requires a separately hosted and monitored gateway; adds network hop, credentials, deployment, cost, and another component that can fail; does not remove PostHog's alpha risk                                                                                                             | Defer until its operational benefits exceed its operational cost           |
| **Cloudflare logs and traces only**                               | Lowest application complexity; platform-managed capture and retention; no application exporter; Cloudflare-native request details                                                                       | Cloudflare tracing is itself [open beta](https://developers.cloudflare.com/workers/observability/traces/known-limitations/); external trace propagation is currently incomplete; data is separated from PostHog users, feature flags, funnels, and session replay; Cloudflare cannot export traces to PostHog      | Keep as fallback, not the primary investigation surface                    |
| **Grafana Cloud (Tempo/Loki), with direct export or a collector** | Established, full-featured logs/traces platform; mature querying, dashboards, alerting, service graphs, sampling, and multi-signal operations; native Cloudflare OTLP endpoints                         | Adds another account, token set, dashboards, alerts, on-call workflow, billing surface, and vendor to maintain; operational data is not natively connected to the existing PostHog person, replay, experiment, and funnel context; correlation would require duplicated identity attributes and manual cross-links | Reconsider when observability depth or reliability outweighs consolidation |
| **Self-hosted collector plus Grafana/Tempo/Loki**                 | Maximum control and portability; no SaaS dependency for storage                                                                                                                                         | Highest operational burden: hosting, upgrades, storage, retention, backups, security, alerting, and availability become part of this personal site's production surface                                                                                                                                            | Reject at current scale                                                    |

Grafana Cloud currently has a useful free allowance, so adopting it would not necessarily create an
immediate bill. It nevertheless creates a second metered platform and future payment path:
[logs, traces, and profiles are usage-priced beyond the allowance](https://grafana.com/pricing/).
PostHog is usage-priced too; cost alone does not decide this. The differentiator is that PostHog
[links logs to users, replays, analytics, and retention impact](https://posthog.com/docs/logs/start-here),
whereas Grafana provides the stronger dedicated observability product but would require rebuilding
that user/product connection.

## Consequences

### Positive

* One trace follows a user action through every instrumented compute boundary.
* Trace-local logs can lead directly to the affected PostHog person and session replay.
* The project avoids operating an always-on gateway or adopting another vendor at low volume.
* W3C Trace Context and OTLP preserve a migration path to a collector or another backend.
* Cloudflare-native logs remain available during PostHog or direct-export failures.

### Negative

* PostHog may change tracing behavior or ingestion details before general availability.
* Delivery is not durable: background execution limits, exporter timeouts, or a PostHog outage may
  drop spans or logs.
* Exporter dependencies increase Worker bundle size and application-owned observability code.
* Full sampling can create avoidable volume and cost if traffic grows unnoticed.
* Two log paths may contain overlapping entries and have different attributes or arrival times.
* PostHog is weaker than a mature dedicated observability platform for advanced sampling, service
  maps, SLOs, alert routing, long retention, and multi-signal infrastructure analysis.

## Guardrails and Verification

* Instrumentation failure must be swallowed, while application errors are recorded and rethrown.
* CI type-checks and dry-run bundles every instrumented runtime.
* A deployment smoke test must verify a complete end-to-end trace and its correlated logs in
  PostHog.
* Periodically verify that Cloudflare fallback logs are still arriving independently.
* Review telemetry volume and configure billing limits before removing full sampling.
* Treat identifiers as correlation metadata, not permission checks; never emit secrets or payloads.

## When To Revisit

Introduce a collector gateway or a mature dedicated backend when any of these becomes true:

* missing telemetry during incidents is frequent enough to impair diagnosis;
* volume requires central batching, tail sampling, filtering, or cost controls;
* compliance requires central PII redaction, egress policy, or auditable delivery;
* telemetry must fan out to multiple backends or switch vendors without coordinated application
  deployments;
* Grafana Cloud, Honeycomb, or another platform's SLOs, alerting, service graphs, retention, or
  reliability materially improve incident response;
* a managed collector/gateway becomes part of the existing platform, eliminating most operational
  overhead;
* PostHog tracing changes incompatibly during alpha, remains unreliable, or fails to mature;
* PostHog tracing reaches general availability, prompting a deliberate review of this temporary
  risk acceptance.

---

Markdown index of this site: https://robbiepalmer.me/llms.txt
