Error tracking + Real User Monitoring.
Two drop-in integrations that reuse the SDKs you already run. Keep your
existing @sentry/* client and just repoint its DSN at us for
Sentry-style error grouping. Keep Google's web-vitals library
and beacon its readings to us for Core Web Vitals. No re-instrumentation,
no second agent, no per-seat bill — errors and vitals land in the same
Logs and metrics pipeline you already use.
TL;DR
Two drop-in integrations that reuse the SDKs you already run. Keep your
existing @sentry/* client and just repoint its DSN at us for
Sentry-style error grouping. Keep Google's web-vitals library
...
Error tracking + Real User Monitoring.
Two drop-in integrations that reuse the SDKs you already run. Keep your
existing @sentry/* client and just repoint its DSN at us for
Sentry-style error grouping. Keep Google's web-vitals library
and beacon its readings to us for Core Web Vitals. No re-instrumentation,
no second agent, no per-seat bill — errors and vitals land in the same
Logs and metrics pipeline you already use.
Sentry-SDK error ingest
Keep the free, open-source @sentry/* SDK exactly as it is.
Change one value — the DSN — to point at us. The unmodified SDK then
ships its error events to us instead of Sentry, and they flow straight
into the Error-tracking view you already have.
DSN format
https://<obs_PAT>@api.24observe.com/<project>
<obs_PAT>is a 24Observe Personal Access Token with thelogs:writescope. Like a normal Sentry DSN key, it's a public, write-only credential — safe to ship in your application bundle. It can write errors; it can't read your data.<project>can be any identifier you like — it labels the source of the events.
Under the hood, the unmodified SDK POSTs its envelope to
/api/<project>/envelope/?sentry_key=<obs_PAT> —
the standard Sentry envelope endpoint. You don't call this directly; the
SDK does, the moment you set the DSN.
JavaScript
import * as Sentry from '@sentry/node'; // or @sentry/browser, etc.
Sentry.init({
dsn: 'https://obs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@api.24observe.com/my-app',
});
// Then capture as usual — no other changes.
Sentry.captureException(new Error('something broke')); This works with any Sentry SDK — Node, browser, Python, and the rest — because they all speak the same envelope protocol. Initialize with the 24Observe DSN and your existing capture calls just work.
What we do with it
We translate each Sentry error event into one of our error-level log
events. The message becomes "ExceptionType: value" — the
same string Sentry groups on — so it fingerprints and groups exactly the
way you're used to. From there, everything downstream works unchanged:
- Error fingerprint grouping — repeated occurrences collapse into a single row with first-seen, last-seen, and a running count in the Error-tracking view.
- Search — errors are queryable alongside the rest of your logs (KQL-lite, facets, the lot).
- Redaction + quota — the same privacy and volume controls that govern your logs apply automatically; no separate config.
The result: Sentry-style error grouping inside the same Logs pipeline you already pay for, with no separate per-seat bill. Grouped errors surface in the dashboard under Logs → Error tracking.
Real User Monitoring (Core Web Vitals)
Keep Google's free, open-source web-vitals library on your
site. Report its readings to us and they become first-class metrics —
queryable and alertable like anything else. No new SDK, no bundle bloat.
Endpoint
POST https://api.24observe.com/api/v1/rum/vitals?key=<obs_PAT>
<obs_PAT> is a Personal Access Token with the
metrics:write scope — again a public, write-only key, safe to
ship in the page. The standard pattern is
navigator.sendBeacon(url, JSON.stringify(payload)), which
fires reliably even as the page is unloading.
Payload shapes
The endpoint accepts three shapes, so you can wire it up however the library hands you data:
- Batched object:
{ service, url, metrics: [ { name, value, rating?, id?, navigationType? } ] } - Bare array of metric objects.
- Single metric object — the exact shape the web-vitals
onLCP/onCLS/ … callbacks hand you, so you can forward each reading as it arrives.
Metric names
LCP,FCP,INP,FID,TTFB— recorded in milliseconds.CLS— the unitless cumulative layout-shift score.
Each reading becomes a metric named web_vitals.lcp,
web_vitals.cls, and so on — queryable and alertable like any
other metric in 24Observe. service defaults to the URL host
when omitted, so a single line of config tags every reading by site.
Response + CORS
- Returns
202. sendBeaconis a simple request and needs no CORS preflight — it just works from any page.- A
fetch(url, { keepalive: true })reporter from any origin works too, if you prefer fetch over beacon.
JavaScript
import { onLCP, onCLS, onINP, onFCP, onTTFB } from 'web-vitals';
const URL = 'https://api.24observe.com/api/v1/rum/vitals?key=obs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
function report(metric) {
// metric = { name, value, rating, id, navigationType } from web-vitals
navigator.sendBeacon(URL, JSON.stringify(metric));
}
// Wire each Core Web Vital to the reporter.
onLCP(report);
onCLS(report);
onINP(report);
onFCP(report);
onTTFB(report);
Prefer to batch? Collect the callbacks into an array and send a single
{ service, url, metrics: [...] } beacon on
visibilitychange — the endpoint accepts that shape directly.