Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
6027591
feat: add React + Sentry example
Catalin4513 Jun 29, 2026
9e7e5b3
feat(react): add trace links and Uptrace activity feed, polish UI
Catalin4513 Jun 30, 2026
bb76ae1
docs(react): drop concrete dev/cloud URLs from .env.example
Catalin4513 Jul 1, 2026
fa5e3d3
Merge remote-tracking branch 'origin/main' into add-react-sentry-example
Catalin4513 Jul 1, 2026
0e6f947
refactor(examples): rename react example to sentry-react for naming c…
Catalin4513 Jul 1, 2026
9ec9089
feat(sentry-react): emit logs for add/delete todos and escalate the e…
Catalin4513 Jul 6, 2026
0ba13e8
feat(sentry-react): wrap every telemetry action in a span via reportI…
Catalin4513 Jul 6, 2026
ce200fb
docs(sentry-react): design spec for faithful Sentry signal-model refa…
Catalin4513 Jul 9, 2026
1a7edb6
docs(sentry-react): implementation plan for signal-model refactor
Catalin4513 Jul 9, 2026
15abd1d
feat(sentry-react): add telemetry module with faithful signal APIs
Catalin4513 Jul 9, 2026
06ce60b
feat(sentry-react): add todos context wiring actions to telemetry
Catalin4513 Jul 9, 2026
07ca1b5
feat(sentry-react): add trace badge, list and detail pages
Catalin4513 Jul 9, 2026
791b72f
feat(sentry-react): route the app and remove per-action traces
Catalin4513 Jul 9, 2026
e92d0c1
docs(sentry-react): document the faithful signal model and routes
Catalin4513 Jul 9, 2026
7ddd5d4
docs(sentry-react): complete breadcrumb list and name the banned trac…
Catalin4513 Jul 9, 2026
336e5fb
fix(sentry-react): end open spans on unmount; drop dead label CSS
Catalin4513 Jul 9, 2026
46d6bce
docs(sentry-react): design spec for telemetry delivery-status indicator
Catalin4513 Jul 9, 2026
5d6e528
docs(sentry-react): implementation plan for delivery-status indicator
Catalin4513 Jul 9, 2026
ec57772
feat(sentry-react): add delivery store and reporting transport wrapper
Catalin4513 Jul 9, 2026
051193d
feat(sentry-react): show telemetry delivery status in the UI
Catalin4513 Jul 9, 2026
393896e
docs(sentry-react): document the delivery-status indicator
Catalin4513 Jul 9, 2026
81bfd74
fix(sentry-react): distinguish unreachable vs rejected delivery in th…
Catalin4513 Jul 9, 2026
0af2d4a
fix(sentry-react): frame delivery status as connection health
Catalin4513 Jul 9, 2026
681cd9b
feat(sentry-react): remove the Sync todos demo button
Catalin4513 Jul 9, 2026
47ed51e
feat(sentry-react): drop routing and the per-todo detail page
Catalin4513 Jul 9, 2026
81c6b6a
fix(sentry-react): hide the trace link when delivery is failing
Catalin4513 Jul 9, 2026
9ce29e0
fix(sentry-react): shorten trace-badge undelivered label to 'not deli…
Catalin4513 Jul 9, 2026
c7dd392
fix(sentry-react): stop the trace link flashing on load
Catalin4513 Jul 9, 2026
7e3094e
feat(sentry-react): label the todo checkbox action on hover
Catalin4513 Jul 9, 2026
45df800
feat(sentry-react): show the checkbox hint as a tooltip above the box
Catalin4513 Jul 9, 2026
23674ec
style(sentry-react): make the checkbox tooltip match the UI
Catalin4513 Jul 9, 2026
7d4296c
docs(sentry-react): design spec for Signal Console redesign
Catalin4513 Jul 9, 2026
043853c
docs(sentry-react): implementation plan for Signal Console
Catalin4513 Jul 9, 2026
09396f3
test(sentry-react): add Playwright harness and window hooks
Catalin4513 Jul 9, 2026
34a39e2
feat(sentry-react): route the app with React Router v7 tracing
Catalin4513 Jul 9, 2026
3bbaaa1
feat(sentry-react): signals store, inspector, and Errors panel
Catalin4513 Jul 9, 2026
02da1e7
feat(sentry-react): Spans panel with a user-named custom span
Catalin4513 Jul 9, 2026
9e25abf
feat(sentry-react): Logs panel emitting structured logs
Catalin4513 Jul 9, 2026
b279f4d
feat(sentry-react): HTTP panel with ok/slow/fail dev endpoints
Catalin4513 Jul 9, 2026
7acfeaa
feat(sentry-react): item-route controls fire signals on the nav trace
Catalin4513 Jul 9, 2026
7e425bf
feat(sentry-react): style the Signal Console and rewrite the README
Catalin4513 Jul 9, 2026
40974e9
fix(sentry-react): end open span on unmount; require Node 20 in README
Catalin4513 Jul 9, 2026
8331a58
feat(sentry-react): deep-link inspector signals to Uptrace
Catalin4513 Jul 9, 2026
2a723ea
fix(sentry-react): deep-link spans via path segment, not ?span_id=
Catalin4513 Jul 9, 2026
cb21bea
docs(sentry-react): design spec for single-root Uptrace trace
Catalin4513 Jul 10, 2026
667053f
docs(sentry-react): implementation plan for single-root trace
Catalin4513 Jul 10, 2026
6f7b422
feat(sentry-react): nest custom spans under the page root
Catalin4513 Jul 10, 2026
46e85ec
feat(sentry-react): nest reported errors under the page root
Catalin4513 Jul 10, 2026
49b5f8c
feat(sentry-react): nest logs and HTTP requests under the page root
Catalin4513 Jul 10, 2026
b10eea4
docs(sentry-react): document single-tree trace nesting
Catalin4513 Jul 10, 2026
eba7c07
fix(sentry-react): seed page root from active span to catch initial p…
Catalin4513 Jul 10, 2026
bc9ebd7
feat(sentry-react): reshape the home console with todos and HTTP pane…
Catalin4513 Jul 13, 2026
33907fd
feat(sentry-react): route to product, category, news and settings pag…
Catalin4513 Jul 13, 2026
a604e15
refactor(sentry-react): condense the inspector and delivery-status re…
Catalin4513 Jul 13, 2026
62ff72d
style(sentry-react): sidebar layout and warm-light theme
Catalin4513 Jul 13, 2026
c24b79f
docs(sentry-react): refresh README and AGENTS.md for the reshaped demo
Catalin4513 Jul 13, 2026
20bbebd
chore: untrack local superpowers planning docs and ignore them
Catalin4513 Jul 13, 2026
c1857c1
test(sentry-react): give the test server its own DSN
Catalin4513 Jul 14, 2026
3d07370
fix(sentry-react): send the created-todo span once per add
Catalin4513 Jul 14, 2026
2905bb0
fix(sentry-react): make the delivery-driven UI stable and honest
Catalin4513 Jul 14, 2026
fa16464
fix(sentry-react): report every unmatched path, not just the first
Catalin4513 Jul 14, 2026
fa6e4b3
fix(sentry-react): report the signals telemetry actually sent
Catalin4513 Jul 14, 2026
15c109f
chore(sentry-react): keep test hooks out of the production bundle
Catalin4513 Jul 14, 2026
445b766
docs(sentry-react): fix stale nav wording and de-duplicate the routes…
Catalin4513 Jul 14, 2026
5de7c77
docs(sentry-react): describe the request spans the HTTP panel actuall…
Catalin4513 Jul 14, 2026
cbae743
test(sentry-react): keep only the specs that guard the Uptrace integr…
Catalin4513 Jul 14, 2026
ce36316
refactor(sentry-react): drive the UI from stores, not effects
Catalin4513 Jul 14, 2026
c9507b5
docs(sentry-react): cut the comments back to what the code cannot say
Catalin4513 Jul 14, 2026
6a58d6f
refactor(sentry-react): group the UI into four files by what it does
Catalin4513 Jul 14, 2026
cee542f
fix(sentry-react): use the non-deprecated React Router tracing APIs
Catalin4513 Jul 14, 2026
7b2f5ff
refactor(sentry-react): order every file top-down, entry points befor…
Catalin4513 Jul 14, 2026
3a69cf5
chore(sentry-react): trim playwright config to what it needs
Catalin4513 Jul 14, 2026
4fb83a6
docs(sentry-react): say what the delivery gate actually checks
Catalin4513 Jul 17, 2026
c7799f7
docs(sentry-react): record why the example has no logs panel
Catalin4513 Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ go-zero/go-zero
db.sqlite3
gorm.db

**/docs/superpowers/

# Misc
.DS_Store
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ project.
- [sentry-go](sentry-go) — errors and tracing with sentry-go.
- [sentry-python](sentry-python) — nested exception chains with sentry-python.
- [sentry-browser](sentry-browser) — events with @sentry/browser.
- [sentry-react](sentry-react) — errors, breadcrumbs, and tracing with @sentry/react in a todo app.

### Logs, Collector, and demos

Expand Down
12 changes: 12 additions & 0 deletions sentry-react/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copy this file to `.env` and paste your Uptrace Sentry DSN.
#
# Get it from Uptrace: open your project, go to "DSN", switch to the
# "Sentry" tab, and copy the DSN. It looks like:
#
# http://<project_token>@<uptrace-host>/<project_id>
VITE_SENTRY_DSN=

# Optional: base URL of your Uptrace UI (its site URL), used to turn the current
# trace badge into a clickable link to that trace. Without it the badge still
# shows the trace id.
VITE_UPTRACE_URL=
8 changes: 8 additions & 0 deletions sentry-react/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
dist
.env
*.local
*.tsbuildinfo
test-results
playwright-report
blob-report
82 changes: 82 additions & 0 deletions sentry-react/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# React + Sentry example

Rules here apply to `examples/sentry-react`, one of the `uptrace/examples` projects:
small, runnable apps that show how to send data to Uptrace. This one shows how
to integrate the Sentry SDK (`@sentry/react`) with a React frontend reporting to
Uptrace, and that is the one thing it teaches. Keep it minimal: an example is
read more than it is run, so every extra feature, file, or dependency is noise
that hides the integration. Add something only when it makes the integration
clearer, not the app richer. The `README.md` is the primary deliverable; when
behavior changes, update it in the same change.

## Core Rules

- The only Uptrace-specific code is `Sentry.init()` in `src/instrument.ts`;
`src/telemetry.ts` holds the rest of the Sentry SDK usage. `instrument.ts` is
imported first in `src/main.tsx` (before React) so instrumentation is in place
before the app renders. `instrument.ts` also installs a reporting transport
wrapper (`src/delivery.ts`) that observes send outcomes for the UI — it wraps
the standard fetch transport and does not change delivery, so it is not an
Uptrace-specific adapter.
- The DSN comes from `VITE_SENTRY_DSN`; never hardcode a DSN. `.env` is
gitignored — keep `.env.example` as the template and document it in the README.
- Uptrace ingests the standard Sentry protocol, so there is no Uptrace exporter
or adapter. If you reach for one, you are doing it wrong.
- Never reach for `Sentry.logger.*` / `enableLogs`. They emit `log` envelope items,
and Uptrace's Sentry ingest handles only `transaction`, `event` and
`client_report` — anything else is skipped, so the data is silently dropped (the
SDK still reports a successful send). A `logger.*` Logs panel was added and
removed once already; do not re-add it. `Sentry.captureMessage()` is the path
that works: Uptrace stores a Sentry event carrying a `message` as a log
(`event_name=log`, the level normalized into `log_severity`).
- The app is a small routed single-page app: Vite + React + TypeScript with
React Router v7, state held in memory (`useState`). There is no separate
backend — the `/api/ok|slow|fail` endpoints are Vite dev-server middleware
(`vite.config.ts`), so they exist under `npm run dev` but not in a static
`vite preview` build. No UI framework.
- Navigation is traced by `Sentry.wrapReactRouterRouting` (`src/main.tsx`),
which names each navigation trace by its route pattern (`/products/:id`,
`/categories/*`), not the concrete URL. Keep that: it is a deliberate
low-cardinality demonstration, and the concrete URL still rides on the root span
as `url.full`.
- Each control fires an explicit Sentry signal and leaves a breadcrumb; keep both
going for new controls. The signals are: Todos (a `created todo` span on add, a
back-dated `completed todo` span on Done), HTTP (`/api/ok|slow|fail` → a
`GET /api/…` span nested under the page root, wrapping the SDK's auto-instrumented
`http.client` span; Fail also captures an error), and Errors (one exception per
button — the home panel uses fixed types, each sub-route its own named errors).
The not-found route auto-reports a `PageNotFound` error when shown. Document any
new control in the README.
- Hard rule: never call `Sentry.startNewTrace()` — the trace comes only from the
browser-tracing integration (a pageload trace on load, a navigation trace on each
route change). Every signal attaches to the current trace **nested under the
page's root span** (via `startSpan`/`startInactiveSpan` with `parentSpan` +
`forceTransaction`, wired in `telemetry.ts`), because Uptrace renders only one
root span per trace — sibling roots would be dropped from the trace tree.
- TypeScript with `strict` on. JS/TS comments use `//` line comments, including
comments for exported types and functions. Comment only what the code cannot say.
- Order code top-down, as the Go backend does: module doc, imports, package-level
consts, then the entry points, then their callees below them — a helper goes under
the function that calls it, never above. A const or type used by exactly one
function sits directly above that function. Read a file top to bottom and the call
stack unfolds in order.
- The UI is four files grouped by what they do — `shell.tsx` (frame), `inspector.tsx`
(bottom readout), `panels.tsx` (the demo controls), `pages.tsx` (the routes) — not
one file per component. An example is read top to bottom; keep it that way.
- Plain CSS only. Design tokens (OKLCH colors, radius, easing) live in `:root` in
`src/index.css`; component styles in `src/App.css`. No Tailwind, no component
library, no CSS-in-JS. Keep the dependency list short.

## Commands

- `npm install`
- `npm run dev` — local dev server.
- `npm run build` — type-check (`tsc -b`) and production build.
- `npm test` — two Playwright specs, guarding only what is Uptrace-specific: single-
root trace nesting, and route-pattern trace naming. They read the intercepted Sentry
envelopes, so no credentials and no running Uptrace are needed: `playwright.config.ts`
gives the test dev server a dummy DSN and ignores your `.env`, keeping them green on
a fresh clone. Keep it that way — the SDK is inert without a DSN (no spans, no trace
ids, no envelopes), so a suite that reads them must supply one itself. The app must
not carry test hooks: assert on what it sends, never on globals it exposes for tests.
Run `npm run build` and `npm test` when changing behavior, and click through the app.
126 changes: 126 additions & 0 deletions sentry-react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# React + Sentry → Uptrace

A minimal React (Vite + TypeScript) app instrumented with the
[`@sentry/react`](https://docs.sentry.io/platforms/javascript/guides/react/)
SDK. Uptrace speaks the Sentry ingest protocol, so the SDK sends data to Uptrace
**without any extra exporter** — you point the SDK's DSN at your Uptrace project.

Every control fires one explicit Sentry signal and shows what it sent in an
in-page inspector, so you can watch each signal type and then find it in Uptrace.

- **Todos** — add a todo (a `created todo` span) and mark it **Done** (a
`completed todo` span whose duration is how long the todo was open).
- **HTTP** — **OK / Slow (~5s) / Fail (500)** fetch a dev endpoint. Each request is
a `GET /api/…` span on the current trace, wrapping the `http.client` span the SDK
instruments fetch with (Fail also captures an error).
- **Errors** — one button per type (`Error`, `TypeError`, `RangeError`,
`SyncError`); each is a distinct, findable issue.
- **Routes** — a side nav lists the app's routes (Products, Categories, News,
Settings, Redirect, Not found). Each navigation mints a new trace named by its
route **pattern** (`/products/:id`, `/categories/*`), so Uptrace groups
navigations by route rather than by concrete URL. An unmatched path renders
NotFound, which reports itself as a `PageNotFound` error.
- A **trace badge** shows the current trace id (and a link to it if you set
`VITE_UPTRACE_URL`); a bottom **inspector** shows what the last control sent,
whether it reached Uptrace (its **delivery status**), and (when
`VITE_UPTRACE_URL` is set) links straight to it in Uptrace — to the exact span
for a custom span (`/traces/<traceId>/<spanId>`), or to the trace for other
signals.

> Session Replay is intentionally not included: Uptrace's Sentry ingest does not
> confirm replay support, and this example only demonstrates signals you can find
> in Uptrace (errors, spans).

## How it works

The Sentry SDK builds its request URLs from the DSN
(`http://<token>@<host>/<project_id>` → `POST <host>/api/<project_id>/envelope/`),
which is exactly what Uptrace exposes. So instrumentation is a standard
`Sentry.init({ dsn })` — see [`src/instrument.ts`](src/instrument.ts). The app
never starts a trace itself: the React Router tracing integration opens a
pageload trace on load and a navigation trace on each route change, and every
span, request, and error attaches to the current trace.

The **HTTP** panel calls `/api/ok`, `/api/slow`, and `/api/fail`, served by a
small Vite dev-server middleware (see [`vite.config.ts`](vite.config.ts)) so
there is **no separate backend**. These endpoints exist under `npm run dev`; a
static `vite preview` build does not include them.

### One trace, one tree

Every signal on a page (custom spans, errors, HTTP) is nested under that
page's pageload/navigation root span, so opening the trace in Uptrace shows one
tree with everything in it. Uptrace stores one root span per trace, so signals are
attached as children rather than as separate roots. Each interaction is therefore
sent as its own Sentry transaction — you will see multiple transaction envelopes
for one page in the Network tab; in Uptrace they appear as one nested tree.

## Prerequisites

- [Node.js](https://nodejs.org) 20 or newer.
- A running Uptrace and a project to send data to:
- **Self-hosted:** [Uptrace get-started guide](https://uptrace.dev/get-started).
The Sentry ingest host is usually `localhost:14318`.
- **Uptrace Cloud:** create a project at <https://app.uptrace.dev>.

## 1. Get your Uptrace Sentry DSN

Open your project in Uptrace → **Project → Data Source Name** → **Sentry** tab →
copy the DSN. It looks like `http://project2_secret_token@localhost:14318/2`.

## 2. Configure and run

```bash
# from this directory: examples/sentry-react
cp .env.example .env
# edit .env: paste your DSN into VITE_SENTRY_DSN
# optionally set VITE_UPTRACE_URL to your Uptrace UI for clickable trace links

npm install
npm run dev
```

Open the URL Vite prints (default <http://localhost:5173>).

## 3. Generate and find data

Use the panels, watching the inspector and delivery-status line. Then in Uptrace:

- **Errors** — the four error buttons (each a distinct type).
- **Traces / spans** — your todo spans (`created todo` / `completed todo`), the
request spans (`GET /api/slow` is visibly long, with the SDK's `http.client` span
nested inside it), and the pageload/navigation traces. Navigate between routes and
watch the trace id change.

If nothing shows up, check that `VITE_SENTRY_DSN` is set (the app warns in the
console if not) and that the DSN host matches your Uptrace ingest address.
Restart `npm run dev` after editing `.env`. The delivery-status line distinguishes
"can't reach Uptrace" (host unreachable) from "Uptrace rejected the data" (bad
DSN key/project).

## Tests

```bash
npm test
```

Two specs, covering only what is Uptrace-specific: every signal nests under one
root span per trace, and each navigation mints a trace named by its route pattern.
They intercept the Sentry envelopes in the browser and read them, so no Sentry
credentials and no running Uptrace are needed — `playwright.config.ts` gives the
test dev server a dummy DSN (the SDK is inert without one) and ignores your `.env`.

## Project layout

| File | Purpose |
| --- | --- |
| `src/instrument.ts` | `Sentry.init()` — the only Uptrace-specific wiring — plus React Router tracing. |
| `src/main.tsx` | Imports instrumentation first; sets up the router and `Sentry.ErrorBoundary`. |
| `src/telemetry.ts` | Every Sentry SDK call: breadcrumbs, custom spans, errors, requests, trace helpers. |
| `src/signals.ts` | Framework-free "last signal sent" store the inspector renders. |
| `src/pages.tsx` | The routes: home console, `/products/:id`, `/categories/*`, not-found, and the shared route body. |
| `src/panels.tsx` | The three demo controls: todos, HTTP, errors. |
| `src/shell.tsx` | Persistent frame: side nav, trace badge, routed content, inspector. |
| `src/inspector.tsx` | Bottom bar: what the last control sent, and whether it reached Uptrace. |
| `src/delivery.ts` | Observe and show whether envelopes reach Uptrace. |
| `vite.config.ts` | Dev-server middleware for `/api/ok|slow|fail`. |
11 changes: 11 additions & 0 deletions sentry-react/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading