From 7bea99a54b4c3176079f40ed48db6bb228e719f7 Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Fri, 5 Dec 2025 16:41:31 +0100 Subject: [PATCH] Enhancement: Implement panel profiling toggle and update performance metrics display This commit introduces a global toggle for panel profiling, allowing users to enable or disable performance metrics via keyboard shortcuts. The PanelPerformanceMetrics component now conditionally renders based on the profiling state, ensuring metrics are displayed appropriately. Additionally, the logic for enabling profiling has been refined to check both global settings and specific dashboard configurations. --- public/app/core/components/help/HelpModal.tsx | 4 ++ .../scene/PanelPerformanceMetrics.tsx | 59 +++++++++++++++---- .../scene/keyboardShortcuts.ts | 13 ++++ .../transformSaveModelSchemaV2ToScene.ts | 3 + .../transformSaveModelToScene.ts | 6 +- .../services/DashboardAnalyticsAggregator.ts | 2 - .../dashboard/services/DashboardProfiler.ts | 25 +++++++- 7 files changed, 93 insertions(+), 19 deletions(-) diff --git a/public/app/core/components/help/HelpModal.tsx b/public/app/core/components/help/HelpModal.tsx index e334d23982e..68fa4a53c6a 100644 --- a/public/app/core/components/help/HelpModal.tsx +++ b/public/app/core/components/help/HelpModal.tsx @@ -202,6 +202,10 @@ export const useShortcuts = () => { keys: ['d', 'x'], description: t('help-modal.shortcuts-description.toggle-exemplars', 'Toggle exemplars in all panel'), }, + { + keys: ['d', 'p'], + description: t('help-modal.shortcuts-description.toggle-performance-metrics', 'Toggle performance metrics'), + }, ], }, { diff --git a/public/app/features/dashboard-scene/scene/PanelPerformanceMetrics.tsx b/public/app/features/dashboard-scene/scene/PanelPerformanceMetrics.tsx index b14676213c2..9e4cd149f63 100644 --- a/public/app/features/dashboard-scene/scene/PanelPerformanceMetrics.tsx +++ b/public/app/features/dashboard-scene/scene/PanelPerformanceMetrics.tsx @@ -9,6 +9,7 @@ import { getDashboardAnalyticsAggregator, PanelAnalyticsMetrics, } from '../../dashboard/services/DashboardAnalyticsAggregator'; +import { isPanelProfilingEnabled } from '../../dashboard/services/DashboardProfiler'; import { getPanelIdForVizPanel } from '../utils/utils'; interface PanelPerformanceMetricsState extends SceneObjectState { @@ -68,9 +69,27 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps(null); const startTimeRef = useRef(null); + // Watch for profiling state changes (when toggled via hotkey) + useEffect(() => { + // Poll for profiling state changes - this allows the component to react when profiling is toggled + const checkProfilingState = () => { + const currentState = isPanelProfilingEnabled(); + if (currentState !== isProfilingEnabled) { + setIsProfilingEnabled(currentState); + } + }; + + // Check immediately and then periodically + checkProfilingState(); + const interval = setInterval(checkProfilingState, 100); + + return () => clearInterval(interval); + }, [isProfilingEnabled]); + // Get last operation times (most recent operation in each array) const lastQueryTime = metrics && metrics.queryOperations.length > 0 @@ -124,16 +143,18 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps 0 ? lastQueryTime : fakeQueryTime; const lastTotalTime = displayQueryTime + lastRenderTime + lastTransformTime; - // Don't render if panel or metrics are not available (and no fake timer running) - if (!panel || (!metrics && fakeQueryTime === 0)) { + // Don't render if panel is not available + if (!panel) { return null; } - // Show component if we have any metrics or fake query time is running - if (lastTotalTime === 0 && fakeQueryTime === 0) { + // If profiling is disabled, don't show the component at all + if (!isProfilingEnabled) { return null; } + // If profiling is enabled, always show the component (even with 0 values) + const renderMetricRow = (label: string, current: number) => { return (
@@ -143,11 +164,14 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps 0; + const tooltipContent = (
{renderMetricRow('Query', displayQueryTime)} + {hasTransformations && renderMetricRow('Transform', lastTransformTime)} {renderMetricRow('Render', lastRenderTime)} - {renderMetricRow('Transform', lastTransformTime)}
{/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */} Total: {formatDuration(lastTotalTime)} @@ -155,13 +179,24 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps ); - const metricsText = [ - displayQueryTime > 0 && `Q:${formatDuration(displayQueryTime)}`, - lastRenderTime > 0 && `R:${formatDuration(lastRenderTime)}`, - lastTransformTime > 0 && `T:${formatDuration(lastTransformTime)}`, - ] - .filter(Boolean) - .join(' '); + // Show metrics text - if profiling is enabled, show all metrics even if 0 + // Transform is only shown if there are transformation operations + // Otherwise, only show non-zero metrics + const metricsText = isProfilingEnabled + ? [ + `Q:${formatDuration(displayQueryTime)}`, + hasTransformations && `T:${formatDuration(lastTransformTime)}`, + `R:${formatDuration(lastRenderTime)}`, + ] + .filter(Boolean) + .join(' ') + : [ + displayQueryTime > 0 && `Q:${formatDuration(displayQueryTime)}`, + lastTransformTime > 0 && `T:${formatDuration(lastTransformTime)}`, + lastRenderTime > 0 && `R:${formatDuration(lastRenderTime)}`, + ] + .filter(Boolean) + .join(' '); return ( diff --git a/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts b/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts index 1113b8a8a8c..d52c17b43b5 100644 --- a/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts +++ b/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts @@ -8,6 +8,7 @@ import { InspectTab } from 'app/features/inspector/types'; import { AccessControlAction } from 'app/types/accessControl'; import { shareDashboardType } from '../../dashboard/components/ShareModal/utils'; +import { enablePanelProfilingForDashboard, togglePanelProfiling } from '../../dashboard/services/DashboardProfiler'; import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { dashboardSceneGraph } from '../utils/dashboardSceneGraph'; @@ -130,6 +131,18 @@ export function setupKeyboardShortcuts(scene: DashboardScene) { onTrigger: () => sceneGraph.getTimeRange(scene).onRefresh(), }); + // Toggle performance metrics + keybindings.addBinding({ + key: 'd p', + onTrigger: () => { + const newState = togglePanelProfiling(); + // If toggling on, enable profiling for the current dashboard + if (newState && scene.state.uid) { + enablePanelProfilingForDashboard(scene, scene.state.uid); + } + }, + }); + if (config.featureToggles.newTimeRangeZoomShortcuts) { keybindings.addBinding({ key: 't +', diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts index 23a2b125aac..2dafb58a88e 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts @@ -60,6 +60,7 @@ import { getDashboardSceneProfilerWithMetadata, enablePanelProfilingForDashboard, getDashboardComponentInteractionCallback, + isPanelProfilingEnabled, } from 'app/features/dashboard/services/DashboardProfiler'; import { DashboardMeta } from 'app/types/dashboard'; @@ -184,7 +185,9 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo uid === '*' || uid === metadata.name) !== -1; const queryController = new behaviors.SceneQueryController( { diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index df36d555ba9..3ce9a0b8b1d 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -26,6 +26,7 @@ import { getDashboardSceneProfilerWithMetadata, enablePanelProfilingForDashboard, getDashboardComponentInteractionCallback, + isPanelProfilingEnabled, } from 'app/features/dashboard/services/DashboardProfiler'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; @@ -313,9 +314,10 @@ export function createDashboardSceneFromDashboardModel( // Create profiler once and reuse to avoid duplicate metadata setting const dashboardProfiler = getDashboardSceneProfilerWithMetadata(oldModel.uid, oldModel.title); - // HACK always on + // Check if profiling should be enabled (global toggle or config) const enableProfiling = - config.dashboardPerformanceMetrics.findIndex((uid) => uid === '*' || uid === oldModel.uid) !== -1 || true; + isPanelProfilingEnabled() || + config.dashboardPerformanceMetrics.findIndex((uid) => uid === '*' || uid === oldModel.uid) !== -1; const queryController = new behaviors.SceneQueryController( { enableProfiling, diff --git a/public/app/features/dashboard/services/DashboardAnalyticsAggregator.ts b/public/app/features/dashboard/services/DashboardAnalyticsAggregator.ts index 9a4d242361f..4d77db37b87 100644 --- a/public/app/features/dashboard/services/DashboardAnalyticsAggregator.ts +++ b/public/app/features/dashboard/services/DashboardAnalyticsAggregator.ts @@ -137,13 +137,11 @@ export class DashboardAnalyticsAggregator implements performanceUtils.ScenePerfo // Panel-level events onPanelOperationStart = (data: performanceUtils.PanelPerformanceData): void => { - console.log('onPanelOperationStart', data.operation); // Start events don't need aggregation, just ensure panel exists this.ensurePanelExists(data.panelKey, data.panelId, data.pluginId, data.pluginVersion); }; onPanelOperationComplete = (data: performanceUtils.PanelPerformanceData): void => { - console.log('onPanelOperationComplete', data.operation); // Aggregate panel metrics without verbose logging (handled by ScenePerformanceLogger) // Ensure panel exists - it may not have been created by onPanelOperationStart if the panel // was loaded from saved state or if start events were missed diff --git a/public/app/features/dashboard/services/DashboardProfiler.ts b/public/app/features/dashboard/services/DashboardProfiler.ts index 217172a32f6..bdbbc081b24 100644 --- a/public/app/features/dashboard/services/DashboardProfiler.ts +++ b/public/app/features/dashboard/services/DashboardProfiler.ts @@ -10,6 +10,7 @@ interface SceneInteractionProfileEvent { } let dashboardSceneProfiler: performanceUtils.SceneRenderProfiler | undefined; +let isProfilingEnabled = false; export function getDashboardSceneProfiler() { if (!dashboardSceneProfiler) { @@ -23,6 +24,23 @@ export function getDashboardSceneProfiler() { return dashboardSceneProfiler; } +/** + * Toggle panel profiling on/off globally + * @returns The new profiling state (true if enabled, false if disabled) + */ +export function togglePanelProfiling(): boolean { + isProfilingEnabled = !isProfilingEnabled; + return isProfilingEnabled; +} + +/** + * Get the current panel profiling state + * @returns true if profiling is enabled, false otherwise + */ +export function isPanelProfilingEnabled(): boolean { + return isProfilingEnabled; +} + export function getDashboardComponentInteractionCallback(uid: string, title: string) { return (e: SceneInteractionProfileEvent) => { const payload = { @@ -61,10 +79,11 @@ export function getDashboardSceneProfilerWithMetadata(uid: string, title: string // Function to enable panel profiling for a specific dashboard export function enablePanelProfilingForDashboard(dashboard: SceneObject, uid: string) { - // Check if panel profiling should be enabled for this dashboard - // HACK always on + // Check if panel profiling should be enabled + // First check the global toggle state, then fall back to config const shouldEnablePanelProfiling = - config.dashboardPerformanceMetrics.findIndex((configUid) => configUid === '*' || configUid === uid) !== -1 || true; // HACK always on + isProfilingEnabled || + config.dashboardPerformanceMetrics.findIndex((configUid) => configUid === '*' || configUid === uid) !== -1; if (shouldEnablePanelProfiling) { const profiler = getDashboardSceneProfiler();