Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.18] - 2026-09-18

### Added

- **`app_startup.duration_ms`** on every `app_startup` span (native cold and
warm, web cold and bfcache warm), next to the seconds-valued
`app_startup.duration`. The unsuffixed key gave no unit and produced the same
1000x display bug `anr.duration` did (fixed in 0.1.17 by renaming). Unlike
ANR, the seconds key is *kept*: RUM plugins before 0.1.34 read only
`app_startup.duration`, so dropping it would blank their startup panels.
Backends coalesce `app_startup.duration_ms` over `app_startup.duration * 1000`.

### Fixed

- `docs/configuration.md` claimed `enableStartupTracking` measured a "hot" start
and the 0.1.8 changelog entry named attribute keys that were never emitted.

## [0.1.17] - 2026-09-07

Eight data-quality defects found validating browser RUM against live data. Four
Expand Down Expand Up @@ -634,8 +651,10 @@ slows trace/log export from 5s to 30s. All of it is opt-in-able — see below.
the cold-start duration is measured from the OS process start, not from
`Scout.initialize`. On background-to-active transitions, a `warm`
`app_startup` span is emitted with duration measured to the next animation
frame. Both carry `app.startup.type` (`cold` | `warm`) and
`app.startup.duration_seconds`.
frame. Both carry `app_startup.type` (`cold` | `warm`) and
`app_startup.duration` (seconds). *(Corrected 2026-09-18: this entry
originally named the keys `app.startup.type` / `app.startup.duration_seconds`,
which were never what the SDK emitted.)*

## [0.1.6] - 2026-05-22

Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ Every auto-instrumentation can be turned off independently. All default to `true
| `interactionEvents` | `['click','change','submit','input']` | Web only. Which DOM events auto-tap tracking listens to; the value lands on the span as `user_interaction.type`. See below. |
| `enableErrorTracking` | `true` | `window.onerror`, `unhandledrejection`, native crashes via KSCrash + NDK signal handler + MetricKit + ApplicationExitInfo. Emits `error`, `app_crash`, `native_crash` spans. |
| `enableLifecycleTracking` | `true` | App `foreground`/`background`/`paused`/`resumed`. Emits `app_paused` / `app_resumed` spans + `view.in_foreground_periods_json` on screen_view. |
| `enableStartupTracking` | `true` | Cold/warm/hot start timing. Emits `app_startup` span. |
| `enableStartupTracking` | `true` | Cold/warm start timing. Emits `app_startup` spans with `app_startup.type` (`cold` \| `warm`), `app_startup.duration` (seconds) and `app_startup.duration_ms` (milliseconds). Native cold start is measured from the OS process start; web cold start from navigation start to `loadEventEnd`. |
| `enableConnectivityTracking` | `true` | Network type changes (`wifi` → `cellular`), connection quality. |
| `enablePerformanceMetrics` | `true` | Memory and CPU samples. |
| `enableLongTaskDetection` | `true` | JS long tasks > `longTaskThresholdMs`. Emits `long_task` span. |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@base-14/scout-react",
"version": "0.1.17",
"version": "0.1.18",
"description": "Zero-config OpenTelemetry RUM for React and React Native. Auto-captures clicks, navigation, errors, lifecycle, network, performance, and web vitals.",
"license": "MIT",
"author": "base-14",
Expand Down
1 change: 1 addition & 0 deletions src/core/attributes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('attribute / span / metric name contract', () => {
expect(ATTR.USER_INTERACTION_TARGET).toBe('user_interaction.target');
expect(ATTR.APP_STARTUP_TYPE).toBe('app_startup.type');
expect(ATTR.APP_STARTUP_DURATION).toBe('app_startup.duration');
expect(ATTR.APP_STARTUP_DURATION_MS).toBe('app_startup.duration_ms');
expect(ATTR.LONG_TASK_DURATION).toBe('long_task.duration');
expect(ATTR.ANR_DURATION_MS).toBe('anr.duration_ms');
expect(ATTR.ANR_THRESHOLD_MS).toBe('anr.threshold_ms');
Expand Down
4 changes: 4 additions & 0 deletions src/core/attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ export const ATTR = {
DISPLAY_SCROLL_MAX_SCROLL_HEIGHT_TIME_MS: 'display.scroll.max_scroll_height_time_ms',
APP_STARTUP_TYPE: 'app_startup.type',
APP_STARTUP_DURATION: 'app_startup.duration',
/** Milliseconds. Pairs with the seconds-valued `app_startup.duration`
* the way `anr.duration_ms` replaced `anr.duration`; readers coalesce
* this over the legacy key while both exist. */
APP_STARTUP_DURATION_MS: 'app_startup.duration_ms',
APP_STARTUP_METRIC: 'app_startup.metric',
APP_STARTUP_IS_PREWARMED: 'app_startup.is_prewarmed',
APP_STARTUP_HAS_SAVED_INSTANCE_STATE_BUNDLE:
Expand Down
2 changes: 1 addition & 1 deletion src/core/scope.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const SCOPE_NAME = 'base14.scout.react';
export const SCOPE_VERSION = '0.1.17';
export const SCOPE_VERSION = '0.1.18';
3 changes: 2 additions & 1 deletion src/native/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,13 @@ export const Scout = {
nativeStartMs !== null
? (Date.now() - nativeStartMs) / 1000
: core.timeSinceAppStartMs() / 1000;
const fbcMs = Math.round(coldDurationSec * 1000);
core.emitSpan(SPAN.APP_STARTUP, {
[ATTR.APP_STARTUP_TYPE]: 'cold',
[ATTR.APP_STARTUP_DURATION]: coldDurationSec,
[ATTR.APP_STARTUP_DURATION_MS]: fbcMs,
...core.commonAttributes(),
});
const fbcMs = Math.round(coldDurationSec * 1000);
core.emitSpan(SPAN.APP_VITAL, {
[ATTR.VITAL_NAME]: 'fbc',
[ATTR.VITAL_TYPE]: 'startup',
Expand Down
1 change: 1 addition & 0 deletions src/native/instrumentations/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export function installNativeLifecycleTracker(
scout.emitSpan(SPAN.APP_STARTUP, {
[ATTR.APP_STARTUP_TYPE]: 'warm',
[ATTR.APP_STARTUP_DURATION]: durationMs / 1000,
[ATTR.APP_STARTUP_DURATION_MS]: Math.round(durationMs),
...scout.commonAttributes(),
});
} catch {}
Expand Down
2 changes: 2 additions & 0 deletions src/web/instrumentations/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function installStartupTracker(scout: Scout): () => void {
scout.emitSpan(SPAN.APP_STARTUP, {
[ATTR.APP_STARTUP_TYPE]: 'cold',
[ATTR.APP_STARTUP_DURATION]: duration,
[ATTR.APP_STARTUP_DURATION_MS]: Math.round(duration * 1000),
[ATTR.BROWSER_NAV_DOM_COMPLETE_MS]: nav.domComplete,
[ATTR.BROWSER_NAV_DOM_CONTENT_LOADED_MS]: nav.domContentLoadedEventEnd,
[ATTR.BROWSER_NAV_DOM_INTERACTIVE_MS]: nav.domInteractive,
Expand All @@ -52,6 +53,7 @@ export function installStartupTracker(scout: Scout): () => void {
scout.emitSpan(SPAN.APP_STARTUP, {
[ATTR.APP_STARTUP_TYPE]: 'warm',
[ATTR.APP_STARTUP_DURATION]: 0,
[ATTR.APP_STARTUP_DURATION_MS]: 0,
...scout.commonAttributes(),
});
scout.addBreadcrumb(BREADCRUMB_TYPE.STARTUP, 'warm start (bfcache)');
Expand Down
Loading