Your user saw 8 seconds.
Your dashboard saw 200ms.
Server metrics stop at the load balancer. Last9 RUM captures the rest — device, network, render — and links each frontend span to the backend trace behind it. One trace, from tap to database.
- 5 SDKs + alerting
- Web, Android, iOS, React Native, Flutter — alert on errors, p95, crashes, ANRs
- One trace
- traceparent injected on every request — frontend span links to the backend trace
- DNS → TTFB
- every network call broken into connection-phase child spans, no config
- 0–100% sampling
- per-session sampling rate, set in SDK config
The problem
Dashboards green.
Users gone.
The "app is slow" ticket lands, your p99 looks fine, and the investigation starts from zero. The slowest part of every request happens outside your infrastructure — on a device and a network you can't see.
The gap between p99 and a person
Backend latency excludes DNS, TLS, CDN, device CPU, and the user's network. A 200ms API response can still be an 8-second page for someone on a slow connection.
Two SDKs, two vendors, zero correlation
Frontend monitoring in one tool, backend traces in another. Connecting a JS error to the API call behind it means matching timestamps by hand.
Screen recording is a data problem
Session-replay tools capture rendered DOM — including fields your compliance team says must never leave the device. Masking is a patch on capture; not capturing is the fix.
Per-session pricing punishes traffic
When RUM is billed per session, a marketing campaign becomes an observability bill spike — so teams sample away the very users they wanted to watch.
Last9 RUM is spans and logs over OTLP — telemetry, not screen capture. It's priced like the rest of your telemetry and correlated with the rest of your telemetry.
How it works
One SDK per platform. One trace end to end.
Each SDK auto-instruments sessions, views, network requests, errors,
and vitals — then injects a traceparent header on every outgoing HTTP request, so your backend trace continues
the frontend span. The slow screen and the query behind it are the same
trace.
- Web · Android · iOS · React Native · Flutter sessions, views, network, errors, vitals — per real user
- Last9 — correlation: session · view · frontend span · backend trace
Every span and log carries a rolling
session ID, and every network call carriestraceparentinto your backend. A crash resolves to the session, the screen, and the API call that preceded it — not a timestamp hunt. - Discover → Applications performance · errors · sessions · alerts on RUM metrics
Platform coverage
What each SDK captures.
All SDKs instrument the same core signals — sessions, views, network, errors. Mobile SDKs also measure cold start, warm start, screen load, and time-to-full-display, and break every network call into DNS, TCP, TLS, and TTFB child spans automatically.
-
Web
JavaScriptCore Web Vitals — LCP, FCP, CLS, INP, TTFB. JS errors and promise rejections, fetch/XHR requests, SPA route changes, sessions. Works with React, Angular, Vue, Next.js.
Setup guide → -
Android
KotlinOkHttp interceptor, activity-lifecycle view tracking, crashes, ANR detection, periodic CPU and memory samples.
Setup guide → -
iOS
SwiftURLProtocol network capture, UIKit and SwiftUI view tracking, crash capture, WebView session correlation.
Setup guide → -
React Native
TypeScriptBridges the native Android and iOS SDKs — fetch/XHR, React Navigation view tracking, JS and native errors, ANR detection on Android.
Setup guide → -
Flutter
DartDart plugin over the native SDKs — HttpClient override, NavigatorObserver view tracking, Flutter framework errors.
Setup guide → -
Your platform
?The SDKs speak OTLP — spans and logs. If your platform isn't listed, tell us what you're building and we'll work out the integration with you.
Request a platform →
Setup
One init block per app.
A script tag on the web, an init call at app entry on mobile. SDKs ship from Last9's CDN — no proprietary agent, no build-system surgery. Create a client monitoring token, scope it to your app's origin, and data starts flowing.
Read the RUM docs<script src="https://cdn.last9.io/rum-sdk/builds/stable/v2/l9.umd.js"></script>
<script>
L9RUM.init({
baseUrl: "https://<your-last9-otlp-endpoint>",
headers: { clientToken: "<your-client-token>" },
resourceAttributes: {
serviceName: "your-app-name",
deploymentEnvironment: "production",
appVersion: "1.0.0",
},
});
// optional — tie sessions to a user on login
L9RUM.identify({ id, email, name });
</script>
Full Web guide →
// settings.gradle.kts:
// maven { url = uri("https://cdn.last9.io/rum-sdk/android/maven/") }
// build.gradle.kts:
// implementation("io.last9:rum-android:0.7.0")
L9Rum.initialize(
this,
L9RumConfig(
baseUrl = "https://<your-last9-otlp-endpoint>/v1/otlp/organizations/<org>",
origin = "android://com.example.myapp",
clientToken = "<your-client-token>",
serviceName = "my-android-app",
serviceVersion = "1.0.0",
deploymentEnvironment = "production",
)
)
Full Android guide →
import Last9RUM
let config = L9RumConfig(
baseUrl: "https://<your-last9-otlp-endpoint>/v1/otlp/organizations/<org>",
clientToken: "<your-client-token>",
serviceName: "my-ios-app",
serviceVersion: "1.0.0",
deploymentEnvironment: "production"
)
L9Rum.shared.initialize(config: config)
Full iOS guide →
// npm install https://cdn.last9.io/rum-sdk/react-native/builds/0.7.0/last9-rum-react-native-0.7.0.tgz
import { L9Rum } from "@last9/rum-react-native";
L9Rum.initialize({
baseUrl: "https://<your-last9-otlp-endpoint>/v1/otlp/organizations/<org>",
origin: "https://your-app-origin",
clientToken: "<your-client-token>",
serviceName: "my-rn-app",
serviceVersion: "1.0.0",
deploymentEnvironment: "production",
});
Full React Native guide →
import 'package:last9_rum_flutter/last9_rum_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await L9Rum.initialize(const L9RumConfig(
baseUrl: 'https://<your-last9-otlp-endpoint>/v1/otlp/organizations/<org>',
origin: 'https://your-app-origin',
clientToken: '<your-client-token>',
serviceName: 'my-flutter-app',
serviceVersion: '1.0.0',
deploymentEnvironment: 'production',
));
runApp(const MyApp());
}
Full Flutter guide →
After setup you get:
- Sessions, views, network, errors, and vitals captured automatically
- Frontend spans linked to backend traces — traceparent on every request
- Every network call broken into DNS, TCP, TLS, and TTFB
- Alerts on error rate, p95 latency, crashes, ANRs — same engine as your backend alerts
What you can finally answer
Questions the "app is slow" ticket will ask.
- Which release moved LCP — and on which pages?
- Is checkout slow because of our API or the user's network?
- Which app versions and devices throw this crash?
- What did the session do in the thirty seconds before the error?
- Which API endpoints fail most for real users, by geography?
- Did yesterday's backend deploy slow down the mobile app's screens?
The next "app is slow" ticket deserves an answer.
One init block per app, and every session lands in Discover → Applications — linked to the backend traces you already have.
Start observing for free. No lock-in.
OpenTelemetry · Prometheus
Just update your config. Start seeing data on Last9 in seconds.
Datadog · New Relic · Others
We've got you covered. Bring over your dashboards & alerts in one click.
Built on Open Standards
100+ integrations. OTel native, works with your existing stack.