Skip to content

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
Storage
Lokievents · logs
Tempotraces
Mimirmetrics
Sessions
rum-apisession records · replay store
View
Grafana + ouo11y apphealth · sessions · timeline · replay
Everything enters through the collector. Storage is the LGTM stack you may already run.
  • 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.id into 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.

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. 1 click Checkout · click span starts
  2. 2 POST /api/checkout · traceparent + baggage(session.id)
  3. 3 charge card fails · server span, status error
  4. 4 500
  5. 5 spans + http.request.failed · OTLP /v1/traces, /v1/logs
  6. 6 server spans · OTLP /v1/traces
  7. 7 replay chunk · :4319
  8. 8 store traces and events
  9. 9 replay chunk
  10. 10 fold the session · status: errored
The browser's fetch span is the root of the backend trace; session.id rides along in baggage.

Every signal of the visit carries the same session.id, so rum-api can put them on one line. One row per signal.

0s45s90s135s180s
0ssession.start
2snavigation/products
3sGET /products
9sweb_vitalLCP 1.8 s, good
40scart.addedcustom event
61sPOST /checkout
64shttp.request.failed500 POST /checkout
120sPOST /checkout
150sexceptionfatal TypeError
One session: started, browsed, failed a checkout, crashed. Its status is crashed.

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