# App performance monitoring
Source: https://sovaria.in/docs/app-performance

Measure what your users wait for (web vitals, app start, screens and every API call) and see each slow call next to the server's time for the same request.

Turn on `sov.perf` and the SDK measures in the app: **web vitals** in browsers, **app start** and **screens** on iOS, Android and Flutter, every **network call** the SDK makes, and your own **traces**. Each network measurement carries the W3C `traceparent` the SDK sent with that request, so the console puts the app's time next to the gateway's time for the same route, and a slow call opens the server-side trace of that very request. That join is something Firebase Performance Monitoring doesn't give you.

JavaScript:
```js
sov.perf.install({ release: '2.4.1', sampleRate: 0.5 })   // half of page loads
// LCP, INP, CLS, FCP and TTFB per page, and every Sovaria request, automatically.
await sov.perf.trace('checkout', () => pay(cart))           // your own step
```

Swift:
```swift
sov.perf.install(release: "2.4.1 (88)")        // cold start from process launch to the app becoming active
let t = sov.perf.screen("Checkout")  /* … */  t.end()
// SwiftUI: CheckoutView().sovariaScreen("Checkout", perf: sov.perf)
try await sov.perf.trace("sync") { try await sync() }
```

Kotlin:
```kotlin
sov.perf.installAndroid(application, release = "2.4.1 (88)")   // cold start at the first activity resume
val t = sov.perf.screen("Checkout"); /* … */ t.end()
sov.perf.trace("sync") { sync() }
```

Flutter:
```dart
sovaria.perf.install(release: '2.4.1+88');
WidgetsBinding.instance.addPostFrameCallback((_) => sovaria.perf.markAppReady());
final t = sovaria.perf.screen('Checkout'); /* … */ t.end();
```

CLI:
```bash
sov perf summary --kind vital --by route    # p50 / p75 / p95 and ratings per page
sov perf network                            # app time vs server time per route
sov perf slow --name "GET /rest/v1/orders"  # slowest calls with trace ids
sov perf trace <trace_id>                   # app spans + server hops for one request
```

(Diagram: One request, seen from both sides)

## What is measured

| Kind | Name | Where |
|---|---|---|
| Web vital | LCP, INP, CLS, FCP, TTFB (rated good / needs improvement / poor at p75, web.dev thresholds) | Browsers, once per page load, per page route (ids collapsed: `/orders/:id`) |
| Network | `GET /rest/v1/orders` (method + route with ids collapsed) | Every SDK, every request the SDK makes; status and trace id kept |
| App start | `cold` | iOS, Android, Flutter |
| Screen | The name you give | iOS (incl. a SwiftUI modifier), Android, Flutter |
| Trace | The name you give | Every SDK: `trace(name, fn)` or `start(name)` → `end()` |

## In the console

- Observability › **App performance**: web vitals tiles (p75 and the share of good / poor page loads), a p75 line over time and a table per page.
- **Network vs server**: each route's p50 / p95 measured in the app beside the gateway's p50 / p95 for the same route. A big gap is network or device; slow on both sides is the server (then see Query Performance or the function).
- **App start and screens** for mobile apps, and **Slow requests**: the slowest calls, each opening a drawer with the app's spans and the server's hops (servers keep hop-by-hop traces for refused and failed requests).
- Percentiles come from hourly histograms (log-scale buckets, within about 7 %), so they stay fast for millions of measurements; raw measurements are kept for drill-down for the plan's days.

## Plans and limits

|  | Free | Pro | Ultra | Team | Enterprise |
|---|---|---|---|---|---|
| App performance measurements per month (per project) | 50,000 | 1,000,000 | 5,000,000 | 20,000,000 | 100,000,000 |
| Days app performance measurements are kept | 7 days | 30 days | 90 days | 90 days | 365 days |

Measurements are batched (every 10 s, up to 100 per request) and count against the month; past the limit they are dropped and counted. Use `sampleRate` to send a share of sessions.
