From 1077a9751721a73bcdfdcc7ed767c139613f7c62 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 23 Jul 2026 13:52:39 -0400 Subject: [PATCH 1/3] feat(dashboards): add BFCache Metrics prebuilt dashboard config Adds the frontend config for the BFCACHE_METRICS prebuilt dashboard (8 trace-metric widgets over browser.bfcache.*) plus a custom onboarding panel that prompts enabling the SDK's bfcacheMetricsIntegration when the selected project has no frontend data yet. --- .../components/bfcacheMetricsOnboarding.tsx | 53 ++++++ .../prebuiltDashboardOnboardingGate.tsx | 5 + .../dashboards/utils/prebuiltConfigs.tsx | 9 +- .../utils/prebuiltConfigs/bfcache.ts | 178 ++++++++++++++++++ 4 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx create mode 100644 static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts diff --git a/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx b/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx new file mode 100644 index 000000000000..4bd4034cef7b --- /dev/null +++ b/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx @@ -0,0 +1,53 @@ +import emptyStateImg from 'sentry-images/spot/performance-waiting-for-span.svg'; + +import {LinkButton} from '@sentry/scraps/button'; +import {CodeBlock} from '@sentry/scraps/code'; +import {Image} from '@sentry/scraps/image'; +import {Flex, Stack} from '@sentry/scraps/layout'; +import {Heading, Text} from '@sentry/scraps/text'; + +import {Panel} from 'sentry/components/panels/panel'; +import {t} from 'sentry/locale'; + +// TODO(bfcache): point at the real docs page once it is published. +const DOCS_URL = + 'https://docs.sentry.io/platforms/javascript/configuration/integrations/bfcachemetrics/'; + +const CODE_SNIPPET = `import * as Sentry from '@sentry/browser'; + +Sentry.init({ + dsn: '__YOUR_DSN__', + integrations: [Sentry.bfcacheMetricsIntegration()], +});`; + +export function BfcacheMetricsOnboarding() { + return ( + + + + + + {t('Monitor Back/Forward Cache')} + + + + {t( + 'Track how often the browser back/forward cache (bfcache) restores your pages, why restores are blocked, and how long miss reloads take. Enable the bfcache metrics integration to start collecting data.' + )} + + + {CODE_SNIPPET} + + + {t('Read the Docs')} + + + + + + + + + + ); +} diff --git a/static/app/views/dashboards/components/prebuiltDashboardOnboardingGate.tsx b/static/app/views/dashboards/components/prebuiltDashboardOnboardingGate.tsx index 8b880e73252f..bf71783b235c 100644 --- a/static/app/views/dashboards/components/prebuiltDashboardOnboardingGate.tsx +++ b/static/app/views/dashboards/components/prebuiltDashboardOnboardingGate.tsx @@ -14,6 +14,7 @@ import {NodeRuntimeMetricsOnboarding} from 'sentry/views/insights/pages/nodeRunt import {ModuleName} from 'sentry/views/insights/types'; import {LegacyOnboarding} from 'sentry/views/performance/onboarding'; +import {BfcacheMetricsOnboarding} from './bfcacheMetricsOnboarding'; import {GenericOnboarding} from './genericOnboarding'; interface PrebuiltDashboardOnboardingGateProps { @@ -89,6 +90,10 @@ export function PrebuiltDashboardOnboardingGate({ return ; } + if (onboarding.componentId === 'bfcache-metrics') { + return ; + } + return ; } diff --git a/static/app/views/dashboards/utils/prebuiltConfigs.tsx b/static/app/views/dashboards/utils/prebuiltConfigs.tsx index 29ef57c65061..770dcc11ad7e 100644 --- a/static/app/views/dashboards/utils/prebuiltConfigs.tsx +++ b/static/app/views/dashboards/utils/prebuiltConfigs.tsx @@ -12,6 +12,7 @@ import {MCP_PROMPTS_PREBUILT_CONFIG} from 'sentry/views/dashboards/utils/prebuil import {MCP_RESOURCES_PREBUILT_CONFIG} from 'sentry/views/dashboards/utils/prebuiltConfigs/ai/mcpResources'; import {MCP_TOOLS_PREBUILT_CONFIG} from 'sentry/views/dashboards/utils/prebuiltConfigs/ai/mcpTools'; import {BACKEND_OVERVIEW_PREBUILT_CONFIG} from 'sentry/views/dashboards/utils/prebuiltConfigs/backendOverview/backendOverview'; +import {BFCACHE_METRICS_PREBUILT_CONFIG} from 'sentry/views/dashboards/utils/prebuiltConfigs/bfcache'; import {CACHES_PREBUILT_CONFIG} from 'sentry/views/dashboards/utils/prebuiltConfigs/caches/caches'; import {FRONTEND_ASSETS_PREBUILT_CONFIG} from 'sentry/views/dashboards/utils/prebuiltConfigs/frontendAssets/frontendAssets'; import {FRONTEND_ASSETS_DETAILS_PREBUILT_CONFIG} from 'sentry/views/dashboards/utils/prebuiltConfigs/frontendAssets/frontendAssetsDetails'; @@ -65,6 +66,7 @@ export enum PrebuiltDashboardId { BACKEND_QUEUE_SUMMARY = 27, BACKEND_CACHES = 28, NODE_RUNTIME_METRICS = 29, + BFCACHE_METRICS = 30, } /** Boolean flags on Project that indicate whether telemetry data has been received. */ @@ -81,7 +83,11 @@ type OnboardingConfig = requiredProjectFlags?: ProjectTelemetryFlag[]; } | { - componentId: 'agent-monitoring' | 'mcp' | 'node-runtime-metrics'; + componentId: + | 'agent-monitoring' + | 'mcp' + | 'node-runtime-metrics' + | 'bfcache-metrics'; requiredProjectFlags: ProjectTelemetryFlag[]; // Custom onboarding component (AI Agents, MCP, Node.js Runtime Metrics) type: 'custom'; @@ -148,4 +154,5 @@ export const PREBUILT_DASHBOARDS: Record [PrebuiltDashboardId.BACKEND_QUEUE_SUMMARY]: QUEUE_DETAILS_PREBUILT_CONFIG, [PrebuiltDashboardId.BACKEND_CACHES]: CACHES_PREBUILT_CONFIG, [PrebuiltDashboardId.NODE_RUNTIME_METRICS]: NODE_RUNTIME_METRICS_PREBUILT_CONFIG, + [PrebuiltDashboardId.BFCACHE_METRICS]: BFCACHE_METRICS_PREBUILT_CONFIG, }; diff --git a/static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts b/static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts new file mode 100644 index 000000000000..80f5ea18ffc7 --- /dev/null +++ b/static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts @@ -0,0 +1,178 @@ +import {t} from 'sentry/locale'; +import {DisplayType, WidgetType} from 'sentry/views/dashboards/types'; +import type {PrebuiltDashboard} from 'sentry/views/dashboards/utils/prebuiltConfigs'; +import {traceMetricField} from 'sentry/views/dashboards/utils/prebuiltConfigs/utils/traceMetricField'; + +const INTERVAL = '5m'; + +// Emitted by the browser SDK's `bfcacheMetricsIntegration`. +const NAVIGATION = traceMetricField('sum', 'browser.bfcache.navigation', 'counter', null); +const NOT_RESTORED = traceMetricField( + 'sum', + 'browser.bfcache.not_restored', + 'counter', + null +); +const RELOAD_P75 = 'p75(value,browser.bfcache.reload.duration,distribution,millisecond)'; +const RELOAD_P95 = 'p95(value,browser.bfcache.reload.duration,distribution,millisecond)'; + +export const BFCACHE_METRICS_PREBUILT_CONFIG: PrebuiltDashboard = { + dateCreated: '', + filters: {}, + projects: [], + title: 'BFCache Metrics', + widgets: [ + { + id: 'bfcache-hits-vs-misses', + title: t('BFCache Hits vs Misses'), + displayType: DisplayType.AREA, + widgetType: WidgetType.TRACEMETRICS, + interval: INTERVAL, + queries: [ + { + name: '', + conditions: '', + fields: ['browser.bfcache.outcome', NAVIGATION], + aggregates: [NAVIGATION], + columns: ['browser.bfcache.outcome'], + orderby: `-${NAVIGATION}`, + }, + ], + layout: {x: 0, y: 0, w: 3, h: 2, minH: 2}, + }, + { + id: 'bfcache-outcome-trend', + title: t('BFCache Outcome Trend'), + displayType: DisplayType.LINE, + widgetType: WidgetType.TRACEMETRICS, + interval: INTERVAL, + queries: [ + { + name: '', + conditions: '', + fields: ['browser.bfcache.outcome', NAVIGATION], + aggregates: [NAVIGATION], + columns: ['browser.bfcache.outcome'], + orderby: `-${NAVIGATION}`, + }, + ], + layout: {x: 3, y: 0, w: 3, h: 2, minH: 2}, + }, + { + id: 'bfcache-miss-reload-duration', + title: t('BFCache Miss Reload Duration'), + displayType: DisplayType.LINE, + widgetType: WidgetType.TRACEMETRICS, + interval: INTERVAL, + queries: [ + { + name: '', + conditions: '', + fields: ['sentry.transaction', RELOAD_P75, RELOAD_P95], + aggregates: [RELOAD_P75, RELOAD_P95], + columns: ['sentry.transaction'], + orderby: `-${RELOAD_P75}`, + }, + ], + layout: {x: 0, y: 2, w: 6, h: 2, minH: 2}, + }, + { + id: 'bfcache-misses-by-route', + title: t('BFCache Misses by Route'), + displayType: DisplayType.CATEGORICAL_BAR, + widgetType: WidgetType.TRACEMETRICS, + interval: INTERVAL, + queries: [ + { + name: '', + conditions: 'browser.bfcache.outcome:miss', + fields: ['sentry.transaction', NAVIGATION], + aggregates: [NAVIGATION], + columns: ['sentry.transaction'], + orderby: `-${NAVIGATION}`, + }, + ], + layout: {x: 0, y: 4, w: 3, h: 3, minH: 2}, + }, + { + id: 'bfcache-not-restored-reasons', + title: t('Top BFCache Not-Restored Reasons'), + displayType: DisplayType.CATEGORICAL_BAR, + widgetType: WidgetType.TRACEMETRICS, + interval: INTERVAL, + queries: [ + { + name: '', + conditions: '', + fields: ['browser.bfcache.reason', NOT_RESTORED], + aggregates: [NOT_RESTORED], + columns: ['browser.bfcache.reason'], + orderby: `-${NOT_RESTORED}`, + }, + ], + layout: {x: 3, y: 4, w: 3, h: 3, minH: 2}, + }, + { + id: 'bfcache-reasons-by-route', + title: t('BFCache Reasons by Route'), + displayType: DisplayType.CATEGORICAL_BAR, + widgetType: WidgetType.TRACEMETRICS, + interval: INTERVAL, + queries: [ + { + name: '', + conditions: '', + fields: ['sentry.transaction', 'browser.bfcache.reason', NOT_RESTORED], + aggregates: [NOT_RESTORED], + columns: ['sentry.transaction', 'browser.bfcache.reason'], + orderby: `-${NOT_RESTORED}`, + }, + ], + layout: {x: 0, y: 7, w: 3, h: 3, minH: 2}, + }, + { + id: 'bfcache-outcome-by-browser', + title: t('BFCache Outcome by Browser'), + displayType: DisplayType.CATEGORICAL_BAR, + widgetType: WidgetType.TRACEMETRICS, + interval: INTERVAL, + queries: [ + { + name: '', + conditions: '', + fields: ['browser.name', 'browser.bfcache.outcome', NAVIGATION], + aggregates: [NAVIGATION], + columns: ['browser.name', 'browser.bfcache.outcome'], + orderby: `-${NAVIGATION}`, + }, + ], + layout: {x: 3, y: 7, w: 3, h: 3, minH: 2}, + }, + { + id: 'bfcache-blockers-by-frame', + title: t('BFCache Blockers by Frame'), + displayType: DisplayType.CATEGORICAL_BAR, + widgetType: WidgetType.TRACEMETRICS, + interval: INTERVAL, + queries: [ + { + name: '', + conditions: '', + fields: ['browser.bfcache.frame', NOT_RESTORED], + aggregates: [NOT_RESTORED], + columns: ['browser.bfcache.frame'], + orderby: `-${NOT_RESTORED}`, + }, + ], + layout: {x: 0, y: 10, w: 3, h: 2, minH: 2}, + }, + ], + onboarding: { + type: 'custom', + componentId: 'bfcache-metrics', + // bfcache is browser-only, so gate on a frontend-data proxy. There is no + // bfcache-specific project flag; the data only exists once the SDK's + // bfcacheMetricsIntegration is enabled. + requiredProjectFlags: ['hasInsightsVitals'], + }, +}; From 7be217095de21ff8efcf6319340811ceb239e065 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Tue, 4 Aug 2026 12:41:51 -0400 Subject: [PATCH 2/3] ref(dashboards): point bfcache onboarding at published docs URL --- .../views/dashboards/components/bfcacheMetricsOnboarding.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx b/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx index 4bd4034cef7b..8370dc3c9f70 100644 --- a/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx +++ b/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx @@ -9,9 +9,8 @@ import {Heading, Text} from '@sentry/scraps/text'; import {Panel} from 'sentry/components/panels/panel'; import {t} from 'sentry/locale'; -// TODO(bfcache): point at the real docs page once it is published. const DOCS_URL = - 'https://docs.sentry.io/platforms/javascript/configuration/integrations/bfcachemetrics/'; + 'https://docs.sentry.io/platforms/javascript/configuration/integrations/bfcache/'; const CODE_SNIPPET = `import * as Sentry from '@sentry/browser'; From 508f31c3ccaab446fea0863acb37b2e6b8e4bd07 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Tue, 4 Aug 2026 12:45:03 -0400 Subject: [PATCH 3/3] ref(dashboards): rename bfcacheMetricsIntegration to bfcacheIntegration --- .../views/dashboards/components/bfcacheMetricsOnboarding.tsx | 2 +- static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx b/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx index 8370dc3c9f70..800471e32b53 100644 --- a/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx +++ b/static/app/views/dashboards/components/bfcacheMetricsOnboarding.tsx @@ -16,7 +16,7 @@ const CODE_SNIPPET = `import * as Sentry from '@sentry/browser'; Sentry.init({ dsn: '__YOUR_DSN__', - integrations: [Sentry.bfcacheMetricsIntegration()], + integrations: [Sentry.bfcacheIntegration()], });`; export function BfcacheMetricsOnboarding() { diff --git a/static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts b/static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts index 80f5ea18ffc7..232774c15052 100644 --- a/static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts +++ b/static/app/views/dashboards/utils/prebuiltConfigs/bfcache.ts @@ -5,7 +5,7 @@ import {traceMetricField} from 'sentry/views/dashboards/utils/prebuiltConfigs/ut const INTERVAL = '5m'; -// Emitted by the browser SDK's `bfcacheMetricsIntegration`. +// Emitted by the browser SDK's `bfcacheIntegration`. const NAVIGATION = traceMetricField('sum', 'browser.bfcache.navigation', 'counter', null); const NOT_RESTORED = traceMetricField( 'sum', @@ -172,7 +172,7 @@ export const BFCACHE_METRICS_PREBUILT_CONFIG: PrebuiltDashboard = { componentId: 'bfcache-metrics', // bfcache is browser-only, so gate on a frontend-data proxy. There is no // bfcache-specific project flag; the data only exists once the SDK's - // bfcacheMetricsIntegration is enabled. + // bfcacheIntegration is enabled. requiredProjectFlags: ['hasInsightsVitals'], }, };