diff --git a/public/app/features/dashboard/services/DashboardAnalyticsAggregator.ts b/public/app/features/dashboard/services/DashboardAnalyticsAggregator.ts index 8671c2b13e5..a36e42c702c 100644 --- a/public/app/features/dashboard/services/DashboardAnalyticsAggregator.ts +++ b/public/app/features/dashboard/services/DashboardAnalyticsAggregator.ts @@ -196,6 +196,39 @@ export class DashboardAnalyticsAggregator implements performanceUtils.ScenePerfo return panel; } + /** + * Send panel_render interactions for each panel with aggregated metrics + */ + private sendPanelRenderInteractions(data: performanceUtils.DashboardInteractionCompleteData): void { + const panelMetrics = this.getPanelMetrics(); + + panelMetrics.forEach((panel) => { + const totalPanelTime = + panel.totalQueryTime + + panel.totalTransformationTime + + panel.totalRenderTime + + panel.totalFieldConfigTime + + panel.pluginLoadTime; + + // logMeasurement requires numeric values in second parameter, metadata in third + const measurementValues = { + totalTime: Math.round(totalPanelTime * 10) / 10, + queryCount: panel.queryOperations.length, + transformCount: panel.transformationOperations.length, + renderCount: panel.renderOperations.length, + fieldConfigCount: panel.fieldConfigOperations.length, + pluginLoadCount: panel.pluginLoadTime > 0 ? 1 : 0, + }; + + logMeasurement('panel_render', measurementValues, { + panelKey: panel.panelKey, + pluginId: panel.pluginId, + panelId: panel.panelId, + operationId: data.operationId, // Shared operationId for correlating with dashboard_render + }); + }); + } + /** * Send analytics report for dashboard interactions */ @@ -218,6 +251,7 @@ export class DashboardAnalyticsAggregator implements performanceUtils.ScenePerfo reportInteraction('dashboard_render', { interactionType: data.interactionType, uid: this.dashboardUID, + operationId: data.operationId, // OperationId for correlating with panel_render interactions ...payload, }); @@ -225,7 +259,11 @@ export class DashboardAnalyticsAggregator implements performanceUtils.ScenePerfo interactionType: data.interactionType, dashboard: this.dashboardUID, title: this.dashboardTitle, + operationId: data.operationId, // OperationId for correlating with panel_render interactions }); + + // Send individual panel_render interactions + this.sendPanelRenderInteractions(data); } /** @@ -351,6 +389,37 @@ export class DashboardAnalyticsAggregator implements performanceUtils.ScenePerfo }); } + // Panel render interactions summary + if (panelMetrics && panelMetrics.length > 0) { + writePerformanceGroupStart('DAA', `📤 Panel render interactions: ${panelMetrics.length} panels reported`); + + panelMetrics.forEach((panel) => { + const totalPanelTime = + panel.totalQueryTime + + panel.totalTransformationTime + + panel.totalRenderTime + + panel.totalFieldConfigTime + + panel.pluginLoadTime; + + const isSlowPanel = totalPanelTime > SLOW_OPERATION_THRESHOLD_MS; + + writePerformanceGroupLog('DAA', `🎨 ${panel.pluginId}-${panel.panelId}:`, { + totalTime: Math.round(totalPanelTime * 10) / 10, + operations: { + queries: panel.queryOperations.length, + transforms: panel.transformationOperations.length, + renders: panel.renderOperations.length, + fieldConfigs: panel.fieldConfigOperations.length, + pluginLoads: panel.pluginLoadTime > 0 ? 1 : 0, + }, + isSlowPanel: isSlowPanel, + ...(isSlowPanel && { warning: 'SLOW' }), + }); + }); + + writePerformanceGroupEnd(); + } + writePerformanceGroupEnd(); } } diff --git a/public/app/features/dashboard/services/dashboard-render-performance-profiling.md b/public/app/features/dashboard/services/dashboard-render-performance-profiling.md index cbef48d7411..c34fb109e39 100644 --- a/public/app/features/dashboard/services/dashboard-render-performance-profiling.md +++ b/public/app/features/dashboard/services/dashboard-render-performance-profiling.md @@ -109,10 +109,16 @@ const payload = { reportInteraction('dashboard_render', { interactionType: e.origin, uid, + operationId: e.operationId, // Shared operationId for correlating with panel_render measurements ...payload, }); -logMeasurement(`dashboard_render`, payload, { interactionType: e.origin, dashboard: uid, title: title }); +logMeasurement('dashboard_render', payload, { + interactionType: e.origin, + dashboard: uid, + title: title, + operationId: e.operationId, // Shared operationId for correlating with panel_render measurements +}); ``` ### Interaction Origin Mapping @@ -301,6 +307,7 @@ Aggregates panel-level performance metrics for analytics reporting: - Tracks operation counts and total time spent per panel - Sends comprehensive analytics reports via `reportInteraction` and `logMeasurement` - Provides detailed panel breakdowns including slow panel detection +- Sends individual `panel_render` measurements for each panel with aggregated metrics via `logMeasurement` #### ScenePerformanceLogger @@ -344,6 +351,7 @@ Reported for each interaction via `reportInteraction` and `logMeasurement`: { interactionType: string, // Type of interaction uid: string, // Dashboard UID + operationId: string, // Unique operationId for correlating with panel_render measurements duration: number, // Total duration networkDuration: number, // Network time processingTime: number, // Client-side processing time @@ -358,6 +366,8 @@ Reported for each interaction via `reportInteraction` and `logMeasurement`: } ``` +**Correlation**: The `operationId` field is shared between `dashboard_render` and all associated `panel_render` measurements, enabling correlation of panel metrics with their parent dashboard interaction. + #### Panel-Level Metrics Aggregated by `DashboardAnalyticsAggregator` for each panel with detailed operation tracking: @@ -406,6 +416,44 @@ Aggregated by `DashboardAnalyticsAggregator` for each panel with detailed operat } ``` +#### Panel Render Measurements + +For each dashboard interaction, individual `panel_render` measurements are sent separately from `dashboard_render` via `logMeasurement`. These measurements are sent for all panels with collected metrics (even if all times are 0), providing granular panel-level analytics. + +**When sent**: After `dashboard_render` interaction, one `panel_render` measurement per panel + +**Correlation**: All `panel_render` measurements share the same `operationId` as their parent `dashboard_render` interaction, enabling correlation between dashboard and panel-level metrics. + +**Measurement values** (via `logMeasurement`): + +```typescript +{ + totalTime: number, // Sum of all operation times + queryCount: number, // Number of query operations + transformCount: number, // Number of transformation operations + renderCount: number, // Number of render operations + fieldConfigCount: number, // Number of field config operations + pluginLoadCount: number // Number of plugin load operations (0 or 1) +} +``` + +**Measurement metadata** (via `logMeasurement` context): + +```typescript +{ + panelKey: string, // Panel key identifier + pluginId: string, // Panel plugin identifier + panelId: string, // Panel identifier + operationId: string // Shared operationId for correlating with dashboard_render +} +``` + +**Note**: `dashboard`, `title`, and `interactionType` are not included in `panel_render` metadata since they can be obtained by correlating with the parent `dashboard_render` interaction using `operationId`. + +**Correlating panel_render with dashboard_render**: + +All `panel_render` measurements share the same `operationId` as their parent `dashboard_render` interaction. + ## Debugging and Development ### Enable Performance Debug Logging @@ -500,7 +548,6 @@ DAA: [ANALYTICS] dashboard_view | 4 panels analyzed | 1 slow panels ⚠️ transform: 12.3, render: 23.8, fieldConfig: 5.0, - pluginLoad: 39.0 } } DAA: 📊 Queries: { @@ -526,6 +573,59 @@ DAA: [ANALYTICS] dashboard_view | 4 panels analyzed | 1 slow panels ⚠️ **Note**: The indentation shows the **console group hierarchy**. In the browser console, each panel creates a collapsible group that can be expanded to see detailed operation breakdowns. The main dashboard analytics group contains nested panel groups for organized analysis. +#### Panel Render Measurements Summary + +After the detailed panel breakdown, a summary of all `panel_render` measurements sent is displayed: + +``` +DAA: 📤 Panel render interactions: 4 panels reported + DAA: 🎨 timeseries-panel-1: { + totalTime: 125.3, + operations: { + queries: 2, + transforms: 1, + renders: 1, + fieldConfigs: 1, + pluginLoads: 1 + }, + isSlowPanel: true, + warning: "SLOW" + } + DAA: 🎨 stat-panel-2: { + totalTime: 45.2, + operations: { + queries: 1, + transforms: 0, + renders: 1, + fieldConfigs: 1, + pluginLoads: 0 + }, + isSlowPanel: false + } + DAA: 🎨 table-panel-3: { + totalTime: 89.7, + operations: { + queries: 1, + transforms: 2, + renders: 1, + fieldConfigs: 1 + }, + isSlowPanel: false + } + DAA: 🎨 graph-panel-4: { + totalTime: 234.1, + operations: { + queries: 3, + transforms: 1, + renders: 2, + fieldConfigs: 1 + }, + isSlowPanel: false + } +``` + +This summary provides a quick overview of all panels that had `panel_render` measurements sent, showing total time, operation counts, and slow panel warnings. + ### Enable Echo Service Debug Logging To observe Echo events in the browser console: