Architecture
ouo11y is a small set of standard parts. Nothing in the path is proprietary: every arrow below is OTLP or plain HTTP, and every box can be replaced.
Your apps
Web app@ouo11y/browser or plain OTel
Mobile appReact Native · iOS
Your backendany OpenTelemetry SDK
Edge
OpenTelemetry Collector:4318 OTLP · :4319 replay
project credential · rate limit
project credential · rate limit
Storage
Lokievents · logs
Tempotraces
Mimirmetrics
Sessions
rum-apisession records · replay store
View
Grafana + ouo11y apphealth · sessions · timeline · replay
The parts
Section titled “The parts”- The collector is the edge. Everything enters through an OpenTelemetry Collector: OTLP on
:4318, replay chunks on:4319, each request with a project credential. The ouo11y distribution adds a replay receiver, a rate limiter and a Sentry SDK bridge. You can build your own with the OpenTelemetry Collector Builder. - Storage is the LGTM stack. Loki holds RUM events and logs, Tempo traces, Mimir metrics, all in open formats.
- rum-api folds every signal with the same
session.idinto one session record (status, counts, traces, replay) and stores replay chunks on disk or in S3. It is internal: nothing outside the cluster reaches it. - Grafana with the ouo11y app shows session health, the sessions list and a session’s timeline, traces and replay, next to every other dashboard you have.
One checkout, end to end
Section titled “One checkout, end to end”What happens when a user clicks Checkout and the API fails. Press Play to step through it.
Browser
API
Collector
Loki · Tempo
rum-api
- 1 click Checkout · click span starts
- 2 POST /api/checkout · traceparent + baggage(session.id)
- 3 charge card fails · server span, status error
- 4 500
- 5 spans + http.request.failed · OTLP /v1/traces, /v1/logs
- 6 server spans · OTLP /v1/traces
- 7 replay chunk · :4319
- 8 store traces and events
- 9 replay chunk
- 10 fold the session · status: errored
What a session looks like
Section titled “What a session looks like”Every signal of the visit carries the same session.id, so rum-api can put them on one line. One row per signal.
0s45s90s135s180s
0s
session.start2s
navigation/products3s
GET /products9s
web_vitalLCP 1.8 s, good40s
cart.addedcustom event61s
POST /checkout64s
http.request.failed500 POST /checkout120s
POST /checkout150s
exceptionfatal TypeErrorIts status is the worst thing that happened: a fatal exception makes it crashed, a failed request or a handled exception errored, nothing at all healthy. See Sessions and health.