Quickstart 2: Tiny Shop without the ouo11y SDK
You build the same Tiny Shop as in quickstart 1: an Express API and a React page with three products, a checkout, a broken API call and a button that throws. The difference is the browser. There is no @ouo11y/browser in it. You instrument it yourself with three upstream open-source libraries:
- the OpenTelemetry JS SDK, for spans and log records;
- rrweb, for session replay;
- Google’s web-vitals, for LCP, INP, CLS, FCP and TTFB.
At the end, ouo11y shows the same session the SDK would have produced: crashed, with its activity list, its traces from the click to the API, and a replay. The code that made it is about 400 lines in one folder of your app, and you own every one of them.
This is for teams that want ouo11y’s sessions, health, traces and replays, but keep their browser instrumentation to standard libraries. It is also the clearest way to see what any RUM SDK does: you write it.
What you build
Section titled “What you build”You build it one step at a time, with no scripts. Each page is one step, and each ends with what you should see.
- The plain app: an Express API and a React page, with no telemetry at all.
- The stack: the collector, Tempo, Loki, Mimir, rum-api and Grafana, one folder per service.
- Backend traces: the API traced with the OpenTelemetry Node SDK.
- The browser, by hand, in four parts: spans and the session, RUM events, replay and a custom event.
- The full picture: one crashed session in Grafana, with everything joined.
- What the SDK adds: what
@ouo11y/browserdoes on top, and how hard each part is to add yourself. - Clean up and troubleshooting.
Plan on about 45 minutes. You end with this folder:
Directorytiny-shop-otel/
Directorystack/ the ouo11y stack, one folder per service
- compose.yaml
Directory10-collector/
- …
Directory20-tempo/
- …
Directory30-loki/
- …
Directory40-mimir/
- …
Directory50-rum-api/
- …
Directory60-grafana/
- …
Directorybackend/ Express API, OpenTelemetry traces
- instrumentation.js
- server.js
Directoryfrontend/ React app, OpenTelemetry + rrweb + web-vitals, no ouo11y SDK
Directorysrc/
Directorytelemetry/
- config.js where the data goes
- session.js the session id
- otel.js spans, logs, propagation
- events.js the RUM events
- replay.js rrweb in chunks
- index.js starts it all
- App.jsx
- main.jsx
What ouo11y needs from a browser
Section titled “What ouo11y needs from a browser”This is the whole contract. The ouo11y SDK does exactly this. Here you do it yourself, and each row links to the step that builds it.
| What | How | Step |
|---|---|---|
| Where | OTLP/HTTP to the collector: :4318/v1/traces and :4318/v1/logs. Every request carries Authorization: Basic base64("<project id>:<project token>"). |
Step 6a |
| Who | Resource attributes service.name, service.version, deployment.environment.name, and ouo.platform = "web". |
Step 6a |
| Which session | session.id and user.id on every span and log record. As attributes, not on the resource: the session can change while the page lives. Also in the baggage header to your API. |
Step 6a |
| What happened | OTLP log records whose event.name is session.start, session.end, exception, http.request.failed, navigation or web_vital, with the attributes of each. Any other name is a custom event. |
Step 6b |
| Replay | rrweb events in chunks { session_id, seq, started_at, ended_at, events }, sent with POST to the collector’s replay port (:4319/v1/replay/chunks) with the same credential and the x-ouo-session-id and x-ouo-seq headers. |
Step 6c |
From these, ouo11y builds the session, its timeline, its traces and its replay, and gives it a status. A session whose events carry ouo.exception.fatal = true is crashed. One with a non-fatal exception or an http.request.failed is errored. Any other session is healthy. Sessions and health explains the rules.
The ouo. attributes in this table wait for an OpenTelemetry semantic convention. Everything else is an OpenTelemetry name already. See Semantic conventions.
What you need
Section titled “What you need”- Node 24 or newer, Docker Desktop, and VS Code with its
codecommand. The first page checks all three. - Pull access to the ouo11y images. The collector, rum-api and Grafana images on
ghcr.io/ouo11yare private for now.
Or run the finished files
Section titled “Or run the finished files”Every file of the finished quickstart is on these pages. To skip ahead, download the finished folder:
⬇ Download the finished files (tiny-shop-otel.zip)Unzip it (it makes a tiny-shop-otel/ folder), log in to ghcr.io (step 4b), then run each command from the folder named above it.
From stack/:
docker compose up -dFrom backend/:
npm installnpm startFrom frontend/, in a second terminal:
npm installnpm run devOpen http://localhost:5173 and follow the full picture to see the session.
Ready to build it yourself? Start with the plain app.