Errors, sessions, traces, and Web Vitals for the browser, sent as standard OpenTelemetry over OTLP/HTTP. It works with Superlog and with any OTLP backend.
@superlog/browser: error capture, breadcrumbs, user and attributes, filtering, sessions, fetch and XMLHttpRequest tracing, navigation, and Web Vitals.@superlog/react: an error boundary, React 19 root error handlers, and route patterns from React Router.@superlog/sourcemaps: a Vite plugin and a CLI that tag built files with debug IDs and upload their source maps, so errors show and group by the original code.
It is built on the OpenTelemetry Browser SIG packages
(@opentelemetry/browser-sdk and @opentelemetry/browser-instrumentation),
which provide the providers, exporters, sessions, and instrumentations.
npm install @superlog/browser @superlog/reactCall init before the application renders.
import { init, setUser } from "@superlog/browser";
init({
service: "storefront",
ingestKey: import.meta.env.VITE_SUPERLOG_INGEST_KEY,
release: import.meta.env.VITE_RELEASE,
environment: import.meta.env.MODE,
// Cross-origin APIs that should continue the trace. Same-origin requests
// always do.
tracePropagationTargets: ["https://api.example.com"],
});
setUser({ id: user.id, email: user.email });The SDK sends to https://superlog.sh/api/ingest by default. For a workspace in
the US region, set endpoint to https://us.superlog.sh/api/ingest. To send to
another OTLP backend, set endpoint and headers instead of
ingestKey. propagators replaces the default W3C trace context and baggage
headers, for backends that read another header. ignoreUrls keeps requests,
such as analytics, out of traces and breadcrumbs.
import { ErrorBoundary, reactErrorHandler } from "@superlog/react";
import { instrumentRouter } from "@superlog/react/router";
import { createBrowserRouter, RouterProvider } from "react-router";
const router = createBrowserRouter(routes);
instrumentRouter(router);
createRoot(document.getElementById("root")!, {
onCaughtError: reactErrorHandler(),
onRecoverableError: reactErrorHandler(),
}).render(
<ErrorBoundary fallback={({ reset }) => <button onClick={reset}>Try again</button>}>
<RouterProvider router={router} />
</ErrorBoundary>,
);With declarative routes, use SuperlogRoutes in place of <Routes>:
import { SuperlogRoutes } from "@superlog/react/router";
<SuperlogRoutes>
<Route path="/orders/:orderId" element={<Order />} />
</SuperlogRoutes>// vite.config.ts
import { superlogSourceMaps } from "@superlog/sourcemaps/vite";
export default defineConfig({
plugins: [superlogSourceMaps({ release: process.env.RELEASE })],
});The plugin builds hidden source maps, writes a debug ID into each file and
its map, uploads the maps with SUPERLOG_API_KEY (a workspace API key, not
the ingest key), and deletes them from the output. For other bundlers, run
the CLI on the build output:
SUPERLOG_API_KEY=... npx superlog-sourcemaps upload dist --release "$RELEASE" --deleteThe SDK sends the debug IDs of the files in each error's stack as
app.debug_ids, and Superlog maps those frames back to the original file,
line, and function before grouping.
import { addBreadcrumb, captureException, captureMessage, setAttributes } from "@superlog/browser";
try {
await pay(cart);
} catch (error) {
captureException(error, { attributes: { "payment.provider": "stripe" } });
}
addBreadcrumb({ category: "checkout", message: "payment started" });
setAttributes({ "app.plan": "pro" });
captureMessage("checkout took longer than 5s", "warning");init and OpenTelemetry can live in a chunk of their own. The capture
functions in @superlog/browser/api, which @superlog/react uses, are under
1 KB and share the client that init starts:
import { captureException } from "@superlog/browser/api";
const { init } = await import("@superlog/browser");
init({ service: "storefront", ingestKey });Each error is an OpenTelemetry log record with eventName: "exception" and
severity ERROR, which is how OpenTelemetry records exceptions as logs.
| Attribute | Value |
|---|---|
exception.type, exception.message, exception.stacktrace |
The error. Causes follow the stack as Caused by: |
exception.mechanism |
onerror, onunhandledrejection, react, or manual |
exception.handled |
Whether the application caught it |
app.breadcrumbs |
JSON array of the last clicks, navigations, console messages, and requests |
app.debug_ids |
JSON object of each file's debug ID by URL, for files tagged by @superlog/sourcemaps |
url.full |
The page |
url.template |
The route pattern, with @superlog/react's router integration |
react.component_stack |
The component stack, from React |
session.id |
The session, on every log and span |
user.id, user.email, user.name |
From setUser, on every log and span |
The resource has service.name, service.version (the release),
deployment.environment.name, and the browser's user_agent.original,
browser.platform, browser.brands, browser.mobile, and
browser.language.
Other signals:
- Spans for fetch and XMLHttpRequest calls, with
traceparentsent to the application's own origin and totracePropagationTargets. - Logs for page loads and navigations (
browser.navigation), page load timing, and Web Vitals (browser.web_vital).
These are dropped before sending:
Script error.without details.- The ResizeObserver loop notice.
- Errors whose frames all come from browser extensions or from
denyUrls. - Errors matching
ignoreErrors. - The same error repeated within a second.
- Errors past
maxErrorsPerMinute(30).
beforeSend can change or drop any error, and beforeBreadcrumb any
breadcrumb.
Bump version in each package, then push a tag such as v0.1.1. The Publish
workflow builds, tests, and publishes all three packages with npm trusted
publishing, which also records provenance.
npm only allows trusted publishing for packages that exist, so a new package
is published once by hand (pnpm build && pnpm -r publish --access public),
then its trusted publisher is set on npmjs.com: repository
superloglabs/browser, workflow publish.yml.
pnpm install
pnpm test # Chromium, Firefox, and WebKit through Playwright
pnpm typecheck
pnpm size
node examples/cross-browser/run.mjs # the same bug in three browsers, as sent