# Real User Monitoring

> Real User Monitoring SDKs for web, Android, iOS, React Native, and Flutter. Automatic instrumentation for sessions, views, network requests, errors, and resource metrics via OpenTelemetry.

Source: https://last9.io/docs/real-user-monitoring/

Last9 Real User Monitoring (RUM) captures real user experience data from web and mobile applications. Each SDK instruments sessions, views, network requests, unhandled errors, and resource metrics (plus ANRs on Android), then exports spans and logs to Last9 over OTLP/HTTP.

## Supported platforms

### Web

### Android / Kotlin

### iOS / Swift

### React Native

### Flutter

## What gets captured

All SDKs auto-instrument the same signals:

- **Sessions** — rolling session IDs with idle + max-duration expiry. Session ID is propagated on every span and log.
- **Views / page loads** — screen transitions tracked as spans. Auto-tracked via SPA route changes (web), activity lifecycle (Android), UIKit/SwiftUI hooks (iOS), React Navigation (React Native), or `NavigatorObserver` (Flutter). Manual `startView(name)` is available on every platform.
- **Network** — HTTP requests captured with URL, method, status, duration, request/response sizes. Traceparent header is injected so spans link to backend traces.
- **Errors** — unhandled JS exceptions, promise rejections, native crashes, and Flutter framework errors. ANR detection is Android-only. Manual `captureError(e, context)` is available on every platform.
- **Core Web Vitals** (web) — LCP, FCP, CLS, INP, TTFB.
- **Resources** (mobile) — periodic memory and CPU samples (configurable interval, default 30s).
- **Custom events + attributes** — `addEvent`, `spanAttributes`, and `identify` for user context.

## Distribution

SDKs are distributed from Last9's CDN:

| Platform     | Latest versioned | Artifact                            | Stable CDN root                                               | Versioned CDN root                                                       |
| ------------ | ---------------- | ----------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Web          | `2.8.0`          | UMD script                          | `https://cdn.last9.io/rum-sdk/builds/stable/v2/`              | `https://cdn.last9.io/rum-sdk/builds/2.8.0/`                             |
| Android      | `1.6.0`          | AAR + POM                           | `https://cdn.last9.io/rum-sdk/android/builds/stable/v1/`      | `https://cdn.last9.io/rum-sdk/android/maven/io/last9/rum-android/1.6.0/` |
| iOS          | `1.6.0`          | CocoaPods podspec + SPM (Git + CDN) | `https://cdn.last9.io/rum-sdk/ios/builds/stable/v1/`          | `https://cdn.last9.io/rum-sdk/ios/builds/1.6.0/`                         |
| React Native | `1.6.0`          | npm tarball                         | `https://cdn.last9.io/rum-sdk/react-native/builds/stable/v1/` | `https://cdn.last9.io/rum-sdk/react-native/builds/1.6.0/`                |
| Flutter      | `1.6.0`          | tar.gz package                      | `https://cdn.last9.io/rum-sdk/flutter/builds/stable/v1/`      | `https://cdn.last9.io/rum-sdk/flutter/builds/1.6.0/`                     |

Stable channels are major-version pinned: Web `stable/v2` currently serves
`2.8.0`; mobile `stable/v1` currently serves `1.6.0`, matching the latest
versioned mobile release. Pin versioned CDN URLs for `1.6.0` (iOS Swift
Package Manager Git URL, lockfiles, checksums). Mobile staging builds use the
`-alpha` suffix and explicit versioned URLs.

## Latest versioned changes

- Mobile `1.6.0` auto-names WebView-hosted views from the current URL: `app.screen.name` becomes the folded pathname (e.g. `/health-record`), and `view.url` carries the full URL. An explicit `startView` / `setViewName` still wins. Flutter does not pick this up yet — it uses `getWebViewInjectedJavaScript()` rather than native `instrument(webView)`.
- Mobile `1.5.2` attaches an `addEvent` fired during screen teardown to **that** screen (1000 ms grace), not the next one, and keeps the OTLP log correlated to the last view when none is current.
- iOS `1.5.1` adds Swift Package Manager from a git URL (`https://github.com/last9/last9-rum-ios.git`) — pin an exact version tag or track `stable/v1`. Android, React Native, and Flutter `1.5.1` are version-parity releases with no functional changes.
- Mobile `1.5.0` adds `autoViewTrackingEnabled` (default `true`) and stops `startView` from leaving a duplicate native view alongside Activity/ViewController auto-tracking. `setViewName` renames the active view in place. Android also stops WebView re-injection from mis-tagging later screens as `view.type=webview`.
- Mobile `1.4.1` keeps the RUM session alive across Home / background (and system file pickers). Returning within 30 minutes resumes the same `session.id`. A force-quit or crash backfills `Session End` on the next cold start (`session.end_reason=process_death`).
- Mobile `1.4.0` adds `scheduleDelayMs` so batch export can flush in ~1s when `debugLogs` is on (default 5s in production).
- Web `2.8.0` and mobile `1.3.0` fold fully-numeric and UUID path segments in network span names (`GET /workspaces/1` → `GET /workspaces/?`) so per-endpoint metrics aggregate. GraphQL span names are unchanged; the raw URL is kept on `http.url` / `url.full`.
- Web `2.7.1` stops emitting source map files from production ESM and UMD bundles.
- Mobile `1.2.1` fixes an iOS main-thread hang when calling `instrument(webView:)` on a `WKWebView`, and hardens React Native `instrumentWebView` so it accepts a WebView load event (`event.nativeEvent.target`), a numeric tag, a ref, or a component instance without throwing — recommended: `onLoadStart={(e) => L9Rum.instrumentWebView(e.nativeEvent.target)}`. Android and Flutter pick up the iOS fix via native pin bumps; no Dart or Android SDK changes.
- React Native `1.2.0` adds an Expo config plugin for Continuous Native Generation — add `"@last9/rum-react-native"` to `app.json` `plugins` and the CDN Maven repo and `Last9RUM` pod source are re-applied on every `expo prebuild`. Requires a development build (not Expo Go).
- React Native `1.1.10` fixes a New Architecture Android clean-build failure at `:app:configureCMakeDebug` when the codegen JNI directory does not exist yet under parallel Gradle execution.
- Mobile `1.1.9` makes `getSessionId()` reliable: it returns a real session id (never an empty string) and, on iOS, is available synchronously as soon as `initialize()` returns. On React Native and Flutter, `initialize()` now resolves with a real session id on iOS instead of `null` or empty. `1.1.8` is a version-parity release with no functional changes.
- Mobile `1.1.7` removes the unused OpenTelemetry Android agent so the SDK no longer forces `androidx.core` 1.17.0 and AGP 8.9.1+ onto consumers — fixes `:app:checkReleaseAarMetadata` failures on older Android toolchains (Android, React Native, Flutter).
- React Native `1.1.6` migrates the bridge to a real TurboModule so `await L9Rum.initialize()` resolves on the New Architecture (bridgeless, default from RN 0.76+). Old Architecture (Paper) remains supported. Upgrade to `1.1.6` or later if init hangs on bridgeless.
- Web `2.7.0` adds automatic GraphQL network observability — descriptive span names, `graphql.operation.*` attributes, and detection of `errors[]` returned on HTTP `200` responses.
- Mobile `1.1.0` adds the same GraphQL observability across Android, iOS, React Native, and Flutter, including opt-in Layer 2 client integrations (Apollo Link on React Native; Dio and gql interceptors on Flutter).
- Mobile `0.9.0` adds an embedded per-flow lifecycle — `shutdown()` and `isActive()` — so RUM can be scoped to a single flow and safely re-initialized.
- Android `1.1.2` requires Kotlin `1.9.0`+ / AGP `8.3.0`+; Android `1.1.1` isolates telemetry attribute collection from the request path so a missing `READ_PHONE_STATE` permission can no longer fail a network request.
- Mobile `0.8.0` changes the ingestion `Client-ID` header to an SDK-generated per-install UUID instead of `serviceName`, preventing all devices for an app from sharing the same ingestion rate-limit bucket.

## Next steps

Pick your platform:

- [Web](/docs/real-user-monitoring/web/)
- [Android / Kotlin](/docs/real-user-monitoring/android/)
- [iOS / Swift](/docs/real-user-monitoring/ios/)
- [React Native](/docs/real-user-monitoring/react-native/)
- [Flutter](/docs/real-user-monitoring/flutter/)

Once data is flowing, explore it in [Discover > Applications](/docs/discover-applications/) — performance, errors, sessions, and session correlation with backend traces. To turn RUM signals into alerts (HTTP error rates, p95 latency, crashes, ANRs), see [Alerting on RUM Metrics](/docs/alerting/rum-alerting/).

---

## Troubleshooting

Please get in touch with us on [Discord](https://discord.com/invite/Q3p2EEucx9) or [Email](mailto:support@last9.io) if you have any questions.
