feat: add React + Sentry example#1
Open
Catalin4513 wants to merge 74 commits into
Open
Conversation
# Conflicts: # README.md
…rror demo to a multi-record sync
Author
|
@vmihailenco, PTAL |
…ctor Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Swap to reactRouterV6BrowserTracingIntegration with routes / and /todo/:id, enable the Logs API, and delete the old App.tsx that wrapped every action in startNewTrace. Traces now come only from pageload and navigation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
'Delivered to Uptrace ✓' read as if the user had just sent something on plain page load. Reframe to connection health — 'Connected to Uptrace ✓' / 'Connecting to Uptrace…' / 'Can't reach Uptrace …' — which reads correctly the instant the pageload trace is sent and after demo actions alike. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the sync_todos nested-span demo (button, syncTodos + wait helpers) and all its doc references. The lifecycle todo.open span and the Throw test error button already cover the span and error signals; Sync was extra surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove react-router-dom, the /todo/:id TodoDetail page, and the react-router tracing integration; the app is a single page again. Swap back to browserTracingIntegration (pageload trace only), make the todo text a plain span, read the trace badge once on load, and drop the now-dead detail/btn-quiet CSS. Docs updated to describe a single-page, pageload-only trace model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A trace id with a 'View in Uptrace' link is useless when the data never reached Uptrace — the link would open nothing. TraceBadge now watches the delivery status and, on a failed send, shows the trace id with 'not delivered — nothing to view' instead of a dead link. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…vered' Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The link was shown optimistically during the idle/sending window, then vanished when the first pageload envelope came back failed. Gate it on the last *settled* delivery result (ok/failed) instead of the live state: no link until delivery confirms ok, and no per-action flicker from the transient sending state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ls, dropping logs and spans
…es with a side nav
The suite only passed when a gitignored .env supplied VITE_SENTRY_DSN, so it failed on a fresh clone and in CI (8 of 14 tests): Sentry.init() with no DSN disables the SDK, which then records no spans, mints no trace ids and sends no envelopes -- exactly what the id and envelope assertions read. Give the Playwright dev server a dummy DSN instead of borrowing the developer's. It is a fixture, not a credential: nothing listens at that host, envelope-reading tests intercept the POSTs in the browser, and no test asserts delivery succeeded. Vite prioritizes prefixed process.env vars, so the suite is now deterministic whether .env is absent, an unfilled template, or a real DSN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
createTodo() ran inside the setTodos updater, and React double-invokes updaters in StrictMode to surface impurity -- which this updater has plenty of: it leaves a breadcrumb, starts and ends a span, pushes a signal and bumps an id counter. So every Add sent its telemetry twice in dev, the mode the README tells people to run, and the demo's own spans showed up doubled in Uptrace. Compute the todo (and its span) first, then hand the plain value to the updater, as done() already does with completeTodo. The existing test read only the last signal, so it passed while two spans were sent; the new one counts them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three defects, one cause: each component reasoned about delivery on its own. - DeliveryStatus rendered only on state === 'failed', but every send emits 'sending' first, so the "can't reach Uptrace" warning blinked out on each action while delivery was still broken. TraceBadge already guarded against this; the guard had not been carried over. - Inspector and the todo rows linked into Uptrace regardless of delivery, so a broken DSN showed "Can't reach Uptrace" next to a link offering to view the signal there -- one that opens nothing. - The failure message had dropped the ingest host in a604e15, leaving host as dead state in every snapshot. A wrong DSN host is the usual cause, so name it: "Can't reach Uptrace at localhost:14318". Extract the settled-ok/failed logic into useSettledDelivery() and read it from all four components, rather than writing it a fourth time: the blink bug came from exactly that copying. The link tests now accept envelopes (a reachable Uptrace) -- the condition under which a link is meant to appear at all. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every unmatched path renders the same catch-all route, so navigating from one to another keeps NotFound mounted and only changes its pathname -- the component is never remounted, contrary to what the comment claimed. The boolean `reported` ref therefore stayed true forever, and a second unknown path was silently never reported to Uptrace. Key the guard to the path instead of the mount. It still absorbs StrictMode's double-invoke (the effect re-runs with the pathname unchanged) while reporting each genuinely new unmatched path. The test navigates client-side between two unmatched paths; using page.goto() for the second would remount the component and hide the bug. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two ways the reported signal diverged from the sent one: - sendRequest's catch path captured the exception but pushed only an http record, so a request that never reached the server surfaced in the Inspector as a span while an error was quietly on its way to Uptrace. Push the error too, as the non-OK path above it does, keeping the exception's real name (a failed fetch is a TypeError). Only /api/fail was tested, and it takes the other path. - completeTodo paired the span id of the span it just sent with a trace id read from the scope. The deep link pairs the two, so they must come from the same span; every other signal already reads both from one spanContext(). No flow today makes them diverge -- the todo panel only exists on Home -- so this is a latent break, and signal-ids.spec.ts pins the invariant. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
instrument.ts appended every pageload/navigation event id to an unbounded window.recordedTransactions, and signals.ts mirrored every signal onto an unbounded window.__signals. Both are test scaffolding, both shipped in the production build, and neither is read by the app (the Inspector reads the store). Guard both with import.meta.env.DEV so Vite strips them from the build; the tests run against the dev server and still see them. instrument.ts is the file this example holds up as the integration to copy, so it should not carry test hooks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y sends README and AGENTS.md said the HTTP panel produces an `http.client` span on the current trace. It produces two spans: a hand-made `GET /api/…` request span, with the SDK's auto-instrumented `http.client` span nested inside it. The wrapper is load-bearing, so document it rather than remove it -- it carries parentSpan: pageRoot + forceTransaction, which keeps the request inside the page's single-root tree (the auto span alone would be a sibling root once the pageload span has ended, and Uptrace keeps one root per trace), and it supplies the span id the Inspector links and the duration it shows. The comment on sendRequest made the same wrong claim, crediting the auto span for the one the panel does not link. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the first example to the repo: a minimal React (Vite + TypeScript) todo app instrumented with
@sentry/react, sending data to Uptrace via Uptrace's Sentry-compatible ingest endpoint.What it shows
Error,TypeError,RangeError,TodoSyncError), raised a few frames deep so the stack trace has real app frames.Sentry.captureMessage()at a random level (info / warning / error).run_traced_taskwith two child spans; browser tracing also emits page-load/navigation spans.Wiring
The only Uptrace-specific code is a plain
Sentry.init({ dsn })insrc/instrument.ts; the DSN comes fromVITE_SENTRY_DSN. The README walks through copying the Sentry DSN from a project's Data Source Name page, configuring.env, running withnpm run dev, and where each signal appears in the Uptrace UI.Verified:
tsc -b+vite buildpass; empty and populated states render correctly in a headless Chrome check.