Skip to content

obs: NR browser agent → pro_plus_spa mode with route-level page views + custom attrs#49

Merged
mastermanas805 merged 1 commit into
mainfrom
feat/nr-browser-apm-fresh
May 13, 2026
Merged

obs: NR browser agent → pro_plus_spa mode with route-level page views + custom attrs#49
mastermanas805 merged 1 commit into
mainfrom
feat/nr-browser-apm-fresh

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Summary

Builds on PR #37 (JS error reports) so the dashboard's NR browser agent emits the full APM surface — page loads, SPA route changes, AJAX waterfalls, and Core Web Vitals — not just JS errors.

  • Mode locked to pro_plus_spa. Extracted the BrowserAgent init options into src/lib/newrelic-config.ts with a NR_BROWSER_MODE = 'pro_plus_spa' constant + explicit soft_navigations, page_view_event, page_view_timing, metrics, jserrors, ajax, and distributed_tracing enabled. Defaults already match in the agent, but stating them in source means a regression to lite shows up in code review, not in a grafana panel that goes flat.
  • Route-level page-view tracking. New <RouteTracker /> mounts inside <BrowserRouter>, watches useLocation, and on every change calls newrelic.setPageViewName(pathname) + setCustomAttribute(...) for tier (from ctx.me.team.tier), is_admin (ctx.me.is_platform_admin), and commit_id (VITE_COMMIT_ID). Falls back to anonymous/false when ctx.me hasn't resolved. Fail-open when window.newrelic is absent.
  • Web Vitals automatic. With page_view_timing on, the agent emits LCP/FID/CLS/FCP/TTFB as PageViewTiming events with no extra wiring — verified the feature is in the BrowserAgent loader's instrumentation set.

Bundle size

raw gzipped
Before (PR #37 main) 632.33 kB 174.21 kB
After (this PR) 634.40 kB 174.76 kB
Delta +2.07 kB +0.55 kB

The agent itself was already in the bundle from PR #37 (full BrowserAgent loader, not lite). The +2 kB is the new RouteTracker + config builder; no loader swap.

Test plan

  • npm test — 397 passed | 3 skipped (was 382 passed | 3 skipped before; +15 new tests)
  • 7 RouteTracker cases — initial fire, route-change fire, tier/admin/commit_id stamping, anonymous fallback, missing-agent fail-open, upgrade-webhook tier flip, null render
  • 8 newrelic-config cases — mode tag + every feature flag pinned
  • ErrorBoundary tests from PR obs: New Relic browser agent + VITE_COMMIT_ID + ErrorBoundary (track 8/8) #37 still pass — no regression
  • npm run build succeeds — main bundle + 116 prerendered HTML files
  • Smoke test in the live cluster after merge: load /app/resources, then click to /app/billing, and confirm NR Page Views UI shows two distinct page-view rows (route-level granularity, not just /)
  • Confirm Core Web Vitals appear in NR's Page Views → Web Vitals tab within ~5 min of first page load
  • Confirm custom attributes (tier, is_admin, commit_id) are facetable in NRQL: SELECT count(*) FROM PageView FACET tier, is_admin, commit_id

🤖 Generated with Claude Code

…custom attrs

Builds on PR #37 (JS error reports) so the dashboard's NR browser agent
emits the full APM surface, not just errors.

Changes:
1. src/lib/newrelic-config.ts — extract the BrowserAgent init options
   into a pure function buildBrowserAgentOptions() so a unit test can
   pin the pro_plus_spa feature set. Tagged with NR_BROWSER_MODE =
   'pro_plus_spa'. Explicit flags for soft_navigations,
   page_view_event, page_view_timing, metrics, jserrors, ajax,
   distributed_tracing — all defaults of the BrowserAgent loader, but
   stating them in source means a regression to lite shows up in
   review here, not in a grafana panel that suddenly goes flat.

2. src/main.tsx — now imports buildBrowserAgentOptions and passes the
   shaped options to `new BrowserAgent(...)`. Docstring lists what
   each enabled feature buys us (LCP/FID/CLS via page_view_timing,
   AJAX waterfalls via ajax, SPA pushState events via
   soft_navigations).

3. src/components/RouteTracker.tsx — null-rendering component that
   sits inside <BrowserRouter>. On every useLocation change it calls
   newrelic.setPageViewName(pathname) + setCustomAttribute(...) for
   tier (from ctx.me.team.tier), is_admin (ctx.me.is_platform_admin),
   and commit_id (VITE_COMMIT_ID). Falls back to "anonymous"/false
   when ctx.me hasn't resolved. Fail-open when window.newrelic is
   absent — telemetry must never crash the router.

4. src/App.tsx — mounts <RouteTracker /> just inside <BrowserRouter>
   and outside any Suspense boundary so a lazy-chunk fetch never
   unmounts it mid-navigation.

Tests:
  - 7 RouteTracker.test.tsx cases (initial fire, route-change fire,
    tier/admin/commit_id stamping, anonymous fallback, missing-agent
    fail-open, upgrade-webhook tier flip, null render)
  - 8 newrelic-config.test.ts cases (mode tag + every feature flag)
  - ErrorBoundary.test.tsx (PR #37) still passes — no regression

Run: vitest run → 397 passed | 3 skipped (was 382 | 3 before)

Bundle size delta (npm run build):
  - main index.js: 632.33 kB → 634.40 kB  (+2.07 kB raw)
  - gzipped:       174.21 kB → 174.76 kB  (+0.55 kB)
The agent itself was already in the bundle from PR #37 (full BrowserAgent
loader, not lite). The +2 kB is RouteTracker + config builder; no loader
swap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 merged commit 34de02e into main May 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant