mirror of https://github.com/grafana/grafana.git
				
				
				
			Performance logging architecture improvements and service consolidation
- Rename ScenePerformanceService to ScenePerformanceLogger for clarity - Consolidate performance service initialization into dashboardPerformanceInitializer - Remove global singleton pattern in favor of per-dashboard lifecycle management - Add comprehensive collapsible analytics logging with detailed panel breakdowns - Remove duplicate completion logs between services - Implement proper cleanup for both ScenePerformanceLogger and DashboardAnalyticsAggregator - Update all imports and references to use new naming conventions
This commit is contained in:
		
							parent
							
								
									c13cf18cf0
								
							
						
					
					
						commit
						d6d071e7c0
					
				|  | @ -1,44 +1,55 @@ | |||
| import { getScenePerformanceTracker, writePerformanceLog } from '@grafana/scenes'; | ||||
| 
 | ||||
| import { getDashboardAnalyticsAggregator } from '../../dashboard/services/DashboardAnalyticsAggregator'; | ||||
| import { getScenePerformanceLogger } from '../../dashboard/services/ScenePerformanceLogger'; | ||||
| import { DashboardScene } from '../scene/DashboardScene'; | ||||
| 
 | ||||
| /** | ||||
|  * Scene behavior function that manages the initialization and lifecycle of | ||||
|  * DashboardAnalyticsAggregator for each dashboard session. | ||||
|  * dashboard performance services for each dashboard session. | ||||
|  * | ||||
|  * Initializes the aggregator with dashboard metadata and registers it as a | ||||
|  * performance observer. Returns a cleanup function for deactivation. | ||||
|  * Initializes both the analytics aggregator and performance logger, registering | ||||
|  * them as performance observers. Returns a cleanup function for deactivation. | ||||
|  */ | ||||
| export function dashboardAnalyticsInitializer(dashboard: DashboardScene) { | ||||
| export function dashboardPerformanceInitializer(dashboard: DashboardScene) { | ||||
|   const { uid, title } = dashboard.state; | ||||
| 
 | ||||
|   if (!uid) { | ||||
|     console.warn('dashboardAnalyticsInitializer: Dashboard UID is missing'); | ||||
|     console.warn('dashboardPerformanceInitializer: Dashboard UID is missing'); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   // writePerformanceLog('DashboardAnalyticsInitializer', 'Initializing dashboard analytics behavior');
 | ||||
|   writePerformanceLog('DAI', 'Initializing dashboard performance services'); | ||||
| 
 | ||||
|   // Initialize analytics aggregator
 | ||||
|   const aggregator = getDashboardAnalyticsAggregator(); | ||||
|   aggregator.initialize(uid, title || 'Untitled Dashboard'); | ||||
| 
 | ||||
|   // Register as performance observer
 | ||||
|   const tracker = getScenePerformanceTracker(); | ||||
|   const unsubscribe = tracker.addObserver(aggregator); | ||||
|   // Initialize performance logger
 | ||||
|   const logger = getScenePerformanceLogger(); | ||||
|   logger.initialize(); | ||||
| 
 | ||||
|   writePerformanceLog('DashboardAnalyticsInitializer', 'DashboardAnalyticsAggregator initialized:', { uid, title }); | ||||
|   // Register both as performance observers
 | ||||
|   const tracker = getScenePerformanceTracker(); | ||||
|   const unsubscribeAggregator = tracker.addObserver(aggregator); | ||||
|   const unsubscribeLogger = tracker.addObserver(logger); | ||||
| 
 | ||||
|   writePerformanceLog('DAI', 'Dashboard performance services initialized:', { uid, title }); | ||||
| 
 | ||||
|   // Return cleanup function
 | ||||
|   return () => { | ||||
|     // Unsubscribe from performance tracker
 | ||||
|     if (unsubscribe) { | ||||
|       unsubscribe(); | ||||
|     if (unsubscribeAggregator) { | ||||
|       unsubscribeAggregator(); | ||||
|     } | ||||
|     if (unsubscribeLogger) { | ||||
|       unsubscribeLogger(); | ||||
|     } | ||||
| 
 | ||||
|     // Clean up aggregator state
 | ||||
|     // Clean up service states
 | ||||
|     aggregator.destroy(); | ||||
|     logger.destroy(); | ||||
| 
 | ||||
|     writePerformanceLog('DashboardAnalyticsInitializer', 'DashboardAnalyticsAggregator cleaned up'); | ||||
|     writePerformanceLog('DAI', 'Dashboard performance services cleaned up'); | ||||
|   }; | ||||
| } | ||||
|  |  | |||
|  | @ -60,7 +60,7 @@ import { | |||
| import { DashboardMeta } from 'app/types/dashboard'; | ||||
| 
 | ||||
| import { addPanelsOnLoadBehavior } from '../addToDashboard/addPanelsOnLoadBehavior'; | ||||
| import { dashboardAnalyticsInitializer } from '../behaviors/DashboardAnalyticsInitializerBehavior'; | ||||
| import { dashboardPerformanceInitializer } from '../behaviors/DashboardAnalyticsInitializerBehavior'; | ||||
| import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer'; | ||||
| import { DashboardControls } from '../scene/DashboardControls'; | ||||
| import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; | ||||
|  | @ -228,7 +228,7 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo<D | |||
|           uid: dashboardId?.toString(), | ||||
|         }), | ||||
|         // Analytics aggregator lifecycle management (initialization, observer registration, cleanup)
 | ||||
|         dashboardAnalyticsInitializer, | ||||
|         dashboardPerformanceInitializer, | ||||
|         // Panel profiling is now handled by composed SceneRenderProfiler
 | ||||
|       ], | ||||
|       $data: new DashboardDataLayerSet({ | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ import { PanelModel } from 'app/features/dashboard/state/PanelModel'; | |||
| import { DashboardDTO, DashboardDataDTO } from 'app/types/dashboard'; | ||||
| 
 | ||||
| import { addPanelsOnLoadBehavior } from '../addToDashboard/addPanelsOnLoadBehavior'; | ||||
| import { dashboardAnalyticsInitializer } from '../behaviors/DashboardAnalyticsInitializerBehavior'; | ||||
| import { dashboardPerformanceInitializer } from '../behaviors/DashboardAnalyticsInitializerBehavior'; | ||||
| import { AlertStatesDataLayer } from '../scene/AlertStatesDataLayer'; | ||||
| import { CustomTimeRangeCompare } from '../scene/CustomTimeRangeCompare'; | ||||
| import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer'; | ||||
|  | @ -331,7 +331,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, | |||
|       uid, | ||||
|     }), | ||||
|     // Analytics aggregator lifecycle management (initialization, observer registration, cleanup)
 | ||||
|     dashboardAnalyticsInitializer, | ||||
|     dashboardPerformanceInitializer, | ||||
|   ]; | ||||
| 
 | ||||
|   // Panel profiling is now handled by composed SceneRenderProfiler
 | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ import { | |||
|   type DashboardInteractionCompleteData, | ||||
|   type PanelPerformanceData, | ||||
|   type QueryPerformanceData, | ||||
|   writePerformanceLog, | ||||
| } from '@grafana/scenes'; | ||||
| 
 | ||||
| /** | ||||
|  | @ -120,8 +119,7 @@ export class DashboardAnalyticsAggregator implements ScenePerformanceObserver { | |||
|   } | ||||
| 
 | ||||
|   onPanelOperationComplete(data: PanelPerformanceData): void { | ||||
|     writePerformanceLog('DashboardAnalyticsAggregator', '🔍 onPanelOperationComplete called with:', data); | ||||
| 
 | ||||
|     // Aggregate panel metrics without verbose logging (handled by ScenePerformanceLogger)
 | ||||
|     const panel = this.panelMetrics.get(data.panelKey); | ||||
|     if (!panel) { | ||||
|       console.warn('Panel not found for operation completion:', data.panelKey); | ||||
|  | @ -176,27 +174,14 @@ export class DashboardAnalyticsAggregator implements ScenePerformanceObserver { | |||
|   onQueryStart(data: QueryPerformanceData): void { | ||||
|     // Non-panel queries (annotations, variables, plugins, datasources) don't need aggregation
 | ||||
|     // These are infrastructure queries that don't belong to specific panels
 | ||||
|     writePerformanceLog('DashboardAnalyticsAggregator', '📊 Non-Panel Query Started:', { | ||||
|       queryId: data.queryId, | ||||
|       queryType: data.queryType, | ||||
|       querySource: data.querySource, | ||||
|       origin: data.origin, | ||||
|     }); | ||||
|     // Logging handled by ScenePerformanceLogger to avoid duplication
 | ||||
|   } | ||||
| 
 | ||||
|   onQueryComplete(data: QueryPerformanceData): void { | ||||
|     // Non-panel queries (annotations, variables, plugins, datasources) don't need panel aggregation
 | ||||
|     // These are infrastructure queries that don't belong to specific panels
 | ||||
|     writePerformanceLog('DashboardAnalyticsAggregator', '📊 Non-Panel Query Complete:', { | ||||
|       queryId: data.queryId, | ||||
|       queryType: data.queryType, | ||||
|       querySource: data.querySource, | ||||
|       origin: data.origin, | ||||
|       duration: data.duration, | ||||
|     }); | ||||
| 
 | ||||
|     // Logging handled by ScenePerformanceLogger to avoid duplication
 | ||||
|     // Could track infrastructure query metrics separately in the future if needed
 | ||||
|     // For now, just log for observability
 | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|  | @ -252,7 +237,7 @@ export class DashboardAnalyticsAggregator implements ScenePerformanceObserver { | |||
|     // S4.0/S5.0: Log complete analytics event including panel metrics
 | ||||
|     this.logDashboardAnalyticsEvent(data, payload, panelMetrics); | ||||
| 
 | ||||
|     writePerformanceLog('DashboardAnalyticsAggregator', 'Analytics payload:', payload); | ||||
|     // Analytics payload logged separately if needed for debugging
 | ||||
| 
 | ||||
|     // Send the same analytics as before
 | ||||
|     reportInteraction('dashboard_render', { | ||||
|  | @ -276,66 +261,124 @@ export class DashboardAnalyticsAggregator implements ScenePerformanceObserver { | |||
|     payload: Record<string, unknown>, | ||||
|     panelMetrics: PanelAnalyticsMetrics[] | null | ||||
|   ): void { | ||||
|     // Calculate performance insights if panel metrics are available
 | ||||
|     let performanceInsights = null; | ||||
|     const panelCount = panelMetrics?.length || 0; | ||||
|     const panelSummary = panelCount ? `${panelCount} panels analyzed` : 'No panel metrics'; | ||||
| 
 | ||||
|     // Main analytics summary
 | ||||
|     const slowPanelCount = | ||||
|       panelMetrics?.filter( | ||||
|         (p) => | ||||
|           p.totalQueryTime + p.totalTransformationTime + p.totalRenderTime + p.totalFieldConfigTime + p.pluginLoadTime > | ||||
|           100 | ||||
|       ).length || 0; | ||||
| 
 | ||||
|     // eslint-disable-next-line no-console
 | ||||
|     console.groupCollapsed( | ||||
|       `DAA: [ANALYTICS] ${data.interactionType} | ${panelSummary}${slowPanelCount > 0 ? ` | ${slowPanelCount} slow panels ⚠️` : ''}` | ||||
|     ); | ||||
| 
 | ||||
|     // Dashboard overview
 | ||||
|     console.log('📊 Dashboard:', { | ||||
|       duration: `${(data.duration || 0).toFixed(1)}ms`, | ||||
|       network: `${(data.networkDuration || 0).toFixed(1)}ms`, | ||||
|       interactionType: data.interactionType, | ||||
|       slowPanels: slowPanelCount, | ||||
|     }); | ||||
| 
 | ||||
|     // Analytics payload
 | ||||
|     console.log('📈 Analytics payload:', payload); | ||||
| 
 | ||||
|     // Individual collapsible panel logs with detailed breakdown
 | ||||
|     if (panelMetrics && panelMetrics.length > 0) { | ||||
|       const totalPanelTime = panelMetrics.reduce((sum: number, panel) => { | ||||
|         const panelTotal = | ||||
|       panelMetrics.forEach((panel) => { | ||||
|         const totalPanelTime = | ||||
|           panel.totalQueryTime + | ||||
|           panel.totalFieldConfigTime + | ||||
|           panel.totalTransformationTime + | ||||
|           panel.totalRenderTime + | ||||
|           panel.pluginLoadTime; | ||||
|         return sum + panelTotal; | ||||
|       }, 0); | ||||
| 
 | ||||
|       const avgPanelTime = totalPanelTime / panelMetrics.length; | ||||
| 
 | ||||
|       const slowestPanel = panelMetrics.reduce((slowest, panel) => { | ||||
|         const panelTotal = | ||||
|           panel.totalQueryTime + | ||||
|           panel.totalFieldConfigTime + | ||||
|           panel.totalTransformationTime + | ||||
|           panel.totalRenderTime + | ||||
|           panel.pluginLoadTime; | ||||
|         const slowestTotal = | ||||
|           slowest.totalQueryTime + | ||||
|           slowest.totalFieldConfigTime + | ||||
|           slowest.totalTransformationTime + | ||||
|           slowest.totalRenderTime + | ||||
|           slowest.pluginLoadTime; | ||||
| 
 | ||||
|         return panelTotal > slowestTotal ? panel : slowest; | ||||
|         const isSlowPanel = totalPanelTime > 100; | ||||
|         const slowWarning = isSlowPanel ? ' ⚠️ SLOW' : ''; | ||||
| 
 | ||||
|         // eslint-disable-next-line no-console
 | ||||
|         console.groupCollapsed( | ||||
|           `🎨 Panel ${panel.pluginId}-${panel.panelId}: ${totalPanelTime.toFixed(1)}ms total${slowWarning}` | ||||
|         ); | ||||
| 
 | ||||
|         console.log('🔧 Plugin:', { | ||||
|           id: panel.pluginId, | ||||
|           version: panel.pluginVersion || 'unknown', | ||||
|           panelId: panel.panelId, | ||||
|           panelKey: panel.panelKey, | ||||
|         }); | ||||
| 
 | ||||
|         console.log('⚡ Performance:', { | ||||
|           totalTime: `${totalPanelTime.toFixed(1)}ms`, | ||||
|           isSlowPanel: isSlowPanel, | ||||
|           breakdown: { | ||||
|             query: `${panel.totalQueryTime.toFixed(1)}ms`, | ||||
|             transform: `${panel.totalTransformationTime.toFixed(1)}ms`, | ||||
|             render: `${panel.totalRenderTime.toFixed(1)}ms`, | ||||
|             fieldConfig: `${panel.totalFieldConfigTime.toFixed(1)}ms`, | ||||
|             pluginLoad: `${panel.pluginLoadTime.toFixed(1)}ms`, | ||||
|           }, | ||||
|         }); | ||||
| 
 | ||||
|         if (panel.queryOperations.length > 0) { | ||||
|           console.log('📊 Queries:', { | ||||
|             count: panel.queryOperations.length, | ||||
|             details: panel.queryOperations.map((op, index) => ({ | ||||
|               operation: index + 1, | ||||
|               duration: `${op.duration.toFixed(1)}ms`, | ||||
|               timestamp: op.timestamp, | ||||
|               queryType: op.queryType || 'unknown', | ||||
|             })), | ||||
|           }); | ||||
|         } | ||||
| 
 | ||||
|         if (panel.transformationOperations.length > 0) { | ||||
|           console.log('🔄 Transformations:', { | ||||
|             count: panel.transformationOperations.length, | ||||
|             details: panel.transformationOperations.map((op, index) => ({ | ||||
|               operation: index + 1, | ||||
|               duration: `${op.duration.toFixed(1)}ms`, | ||||
|               timestamp: op.timestamp, | ||||
|               transformationId: op.transformationId || 'unknown', | ||||
|               success: op.success !== false, | ||||
|             })), | ||||
|           }); | ||||
|         } | ||||
| 
 | ||||
|         if (panel.renderOperations.length > 0) { | ||||
|           console.log('🎨 Renders:', { | ||||
|             count: panel.renderOperations.length, | ||||
|             details: panel.renderOperations.map((op, index) => ({ | ||||
|               operation: index + 1, | ||||
|               duration: `${op.duration.toFixed(1)}ms`, | ||||
|               timestamp: op.timestamp, | ||||
|             })), | ||||
|           }); | ||||
|         } | ||||
| 
 | ||||
|         if (panel.fieldConfigOperations.length > 0) { | ||||
|           console.log('⚙️ FieldConfigs:', { | ||||
|             count: panel.fieldConfigOperations.length, | ||||
|             details: panel.fieldConfigOperations.map((op, index) => ({ | ||||
|               operation: index + 1, | ||||
|               duration: `${op.duration.toFixed(1)}ms`, | ||||
|               timestamp: op.timestamp, | ||||
|             })), | ||||
|           }); | ||||
|         } | ||||
| 
 | ||||
|         // eslint-disable-next-line no-console
 | ||||
|         console.groupEnd(); | ||||
|       }); | ||||
| 
 | ||||
|       const slowestPanelTime = | ||||
|         slowestPanel.totalQueryTime + | ||||
|         slowestPanel.totalFieldConfigTime + | ||||
|         slowestPanel.totalTransformationTime + | ||||
|         slowestPanel.totalRenderTime + | ||||
|         slowestPanel.pluginLoadTime; | ||||
| 
 | ||||
|       performanceInsights = { | ||||
|         totalPanelTime: `${totalPanelTime.toFixed(2)}ms`, | ||||
|         averagePanelTime: `${avgPanelTime.toFixed(2)}ms`, | ||||
|         slowestPanel: { | ||||
|           panelId: slowestPanel.panelId, | ||||
|           pluginId: slowestPanel.pluginId, | ||||
|           time: `${slowestPanelTime.toFixed(2)}ms`, | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
| 
 | ||||
|     writePerformanceLog('DashboardAnalyticsAggregator', '🎯 Dashboard Analytics Event:', { | ||||
|       uid: this.dashboardUID, | ||||
|       title: this.dashboardTitle, | ||||
|       interactionType: data.interactionType, | ||||
|       dashboardMetrics: payload, | ||||
|       panelMetricsMapSize: this.panelMetrics.size, | ||||
|       panelMetricsArrayLength: panelMetrics?.length || 0, | ||||
|       panelMetrics: (panelMetrics?.length ?? 0) > 0 ? panelMetrics : 'No panel metrics found', | ||||
|       performanceInsights: performanceInsights || 'No performance insights available', | ||||
|     }); | ||||
|     // eslint-disable-next-line no-console
 | ||||
|     console.groupEnd(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { logMeasurement, reportInteraction, config } from '@grafana/runtime'; | ||||
| import { SceneRenderProfiler, type SceneObject } from '@grafana/scenes'; | ||||
| 
 | ||||
| import { initializeScenePerformanceService } from './ScenePerformanceService'; | ||||
| import { initializeScenePerformanceLogger } from './ScenePerformanceLogger'; | ||||
| 
 | ||||
| interface SceneInteractionProfileEvent { | ||||
|   origin: string; | ||||
|  | @ -26,9 +26,6 @@ export function getDashboardSceneProfiler() { | |||
|     }; | ||||
| 
 | ||||
|     dashboardSceneProfiler = new SceneRenderProfiler(panelProfilingConfig); | ||||
| 
 | ||||
|     // Initialize the Scene performance service to start listening to events
 | ||||
|     initializeScenePerformanceService(); | ||||
|   } | ||||
|   return dashboardSceneProfiler; | ||||
| } | ||||
|  |  | |||
|  | @ -17,13 +17,13 @@ import { | |||
| } from './performanceConstants'; | ||||
| 
 | ||||
| /** | ||||
|  * Grafana service that subscribes to Scene performance events | ||||
|  * and integrates them with Grafana's observability systems. | ||||
|  * Also creates Chrome DevTools performance marks and measurements for debugging. | ||||
|  * Grafana logger that subscribes to Scene performance events | ||||
|  * and logs them to console with Chrome DevTools performance marks and measurements for debugging. | ||||
|  */ | ||||
| export class ScenePerformanceService implements ScenePerformanceObserver { | ||||
| export class ScenePerformanceLogger implements ScenePerformanceObserver { | ||||
|   private isInitialized = false; | ||||
|   private unsubscribe: (() => void) | null = null; | ||||
|   private panelGroupsOpen = new Set<string>(); // Track which panels we've seen
 | ||||
| 
 | ||||
|   constructor() { | ||||
|     // Bind all observer methods to preserve 'this' context when called by ScenePerformanceTracker
 | ||||
|  | @ -48,7 +48,7 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     // Note: Analytics aggregator will be initialized separately with dashboard context
 | ||||
| 
 | ||||
|     this.isInitialized = true; | ||||
|     writePerformanceLog('ScenePerformanceService', 'Initialized and subscribed to Scene performance events'); | ||||
|     writePerformanceLog('SPL', 'Initialized and subscribed to Scene performance events'); | ||||
|   } | ||||
| 
 | ||||
|   public destroy() { | ||||
|  | @ -63,7 +63,7 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     } | ||||
| 
 | ||||
|     this.isInitialized = false; | ||||
|     writePerformanceLog('ScenePerformanceService', 'Destroyed and unsubscribed from Scene performance events'); | ||||
|     writePerformanceLog('SPL', 'Destroyed and unsubscribed from Scene performance events'); | ||||
|   } | ||||
| 
 | ||||
|   // Dashboard-level events
 | ||||
|  | @ -72,14 +72,10 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     const dashboardStartMark = PERFORMANCE_MARKS.DASHBOARD_INTERACTION_START(data.operationId); | ||||
|     createPerformanceMark(dashboardStartMark, data.timestamp); | ||||
| 
 | ||||
|     writePerformanceLog('ScenePerformanceService', '🎯 Dashboard Interaction Started:', { | ||||
|       type: data.interactionType, | ||||
|       uid: data.metadata?.dashboardUID, | ||||
|       title: data.metadata?.dashboardTitle, | ||||
|       panelCount: data.metadata?.panelCount, | ||||
|       timestamp: data.timestamp, | ||||
|       operationId: data.operationId, | ||||
|     }); | ||||
|     const title = data.metadata?.dashboardTitle || 'Unknown Dashboard'; | ||||
|     const panelCount = data.metadata?.panelCount || 0; | ||||
| 
 | ||||
|     writePerformanceLog('SPL', `[DASHBOARD] ${data.interactionType} started: ${title} (${panelCount} panels)`); | ||||
|   } | ||||
| 
 | ||||
|   onDashboardInteractionMilestone(data: DashboardInteractionMilestoneData): void { | ||||
|  | @ -88,13 +84,8 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     const dashboardMilestoneMark = PERFORMANCE_MARKS.DASHBOARD_MILESTONE(data.operationId, milestone); | ||||
|     createPerformanceMark(dashboardMilestoneMark, data.timestamp); | ||||
| 
 | ||||
|     writePerformanceLog('ScenePerformanceService', '🔄 Dashboard Milestone:', { | ||||
|       type: data.interactionType, | ||||
|       uid: data.metadata?.dashboardUID, | ||||
|       milestone: data.milestone, | ||||
|       timestamp: data.timestamp, | ||||
|       operationId: data.operationId, | ||||
|     }); | ||||
|     // Log milestones quietly - only when verbose debugging is needed
 | ||||
|     // Most milestones are covered by the start/complete query logs
 | ||||
|   } | ||||
| 
 | ||||
|   onDashboardInteractionComplete(data: DashboardInteractionCompleteData): void { | ||||
|  | @ -106,15 +97,11 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     createPerformanceMark(dashboardEndMark, data.timestamp); | ||||
|     createPerformanceMeasure(dashboardMeasureName, dashboardStartMark, dashboardEndMark); | ||||
| 
 | ||||
|     writePerformanceLog('ScenePerformanceService', '✅ Dashboard Interaction Complete:', { | ||||
|       type: data.interactionType, | ||||
|       uid: data.metadata?.dashboardUID, | ||||
|       title: data.metadata?.dashboardTitle, | ||||
|       duration: data.duration, | ||||
|       networkDuration: data.networkDuration, | ||||
|       timestamp: data.timestamp, | ||||
|       operationId: data.operationId, | ||||
|     }); | ||||
|     // Clear tracking state
 | ||||
|     this.panelGroupsOpen.clear(); | ||||
| 
 | ||||
|     // Dashboard completion logging is handled comprehensively by SceneRenderProfiler
 | ||||
|     // This observer focuses on creating DevTools performance marks/measures
 | ||||
|   } | ||||
| 
 | ||||
|   // Panel-level events
 | ||||
|  | @ -122,16 +109,10 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     // Create standardized performance marks based on operation type
 | ||||
|     this.createStandardizedPanelMark(data, 'start'); | ||||
| 
 | ||||
|     const operationIcon = this.getOperationIcon(data.operation); | ||||
|     writePerformanceLog('ScenePerformanceService', `${operationIcon} Panel Operation Started [${data.operation}]:`, { | ||||
|       panelId: data.panelId, | ||||
|       panelKey: data.panelKey, | ||||
|       pluginId: data.pluginId, | ||||
|       operation: data.operation, | ||||
|       timestamp: data.timestamp, | ||||
|       metadata: data.metadata, | ||||
|       operationId: data.operationId, | ||||
|     }); | ||||
|     // Track panel for summary logging later
 | ||||
|     this.panelGroupsOpen.add(data.panelKey); | ||||
| 
 | ||||
|     // Don't log start events - they're noise. Only log completions with timing.
 | ||||
|   } | ||||
| 
 | ||||
|   onPanelOperationComplete(data: PanelPerformanceData): void { | ||||
|  | @ -139,17 +120,19 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     this.createStandardizedPanelMark(data, 'end'); | ||||
|     this.createStandardizedPanelMeasure(data); | ||||
| 
 | ||||
|     const operationIcon = this.getOperationIcon(data.operation); | ||||
|     writePerformanceLog('ScenePerformanceService', `${operationIcon} Panel Operation Complete [${data.operation}]:`, { | ||||
|       panelId: data.panelId, | ||||
|       panelKey: data.panelKey, | ||||
|       pluginId: data.pluginId, | ||||
|       operation: data.operation, | ||||
|       duration: data.duration, | ||||
|       timestamp: data.timestamp, | ||||
|       metadata: data.metadata, | ||||
|       operationId: data.operationId, | ||||
|     }); | ||||
|     const duration = (data.duration || 0).toFixed(1); | ||||
|     const slowWarning = (data.duration || 0) > 100 ? ' ⚠️ SLOW' : ''; | ||||
| 
 | ||||
|     // For query operations, include the queryId for correlation
 | ||||
|     let operationDisplay: string = data.operation; | ||||
|     if (data.operation === 'query') { | ||||
|       operationDisplay = `${data.operation} [${data.metadata.queryId}]`; | ||||
|     } | ||||
| 
 | ||||
|     writePerformanceLog( | ||||
|       'SPL', | ||||
|       `[PANEL] ${data.pluginId}-${data.panelId} ${operationDisplay}: ${duration}ms${slowWarning}` | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   // Query-level events
 | ||||
|  | @ -158,14 +141,9 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     const queryStartMark = PERFORMANCE_MARKS.QUERY_START(data.origin, data.queryId); | ||||
|     createPerformanceMark(queryStartMark, data.timestamp); | ||||
| 
 | ||||
|     writePerformanceLog('ScenePerformanceService', '📊 Non-Panel Query Started:', { | ||||
|       queryId: data.queryId, | ||||
|       queryType: data.queryType, | ||||
|       querySource: data.querySource, | ||||
|       origin: data.origin, | ||||
|       timestamp: data.timestamp, | ||||
|       operationId: data.operationId, | ||||
|     }); | ||||
|     // Mark that we're processing infrastructure queries
 | ||||
| 
 | ||||
|     // Don't log start events - they're noise. Only log completions with timing.
 | ||||
|   } | ||||
| 
 | ||||
|   onQueryComplete(data: QueryPerformanceData): void { | ||||
|  | @ -177,15 +155,12 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     createPerformanceMark(queryEndMark, data.timestamp); | ||||
|     createPerformanceMeasure(queryMeasureName, queryStartMark, queryEndMark); | ||||
| 
 | ||||
|     writePerformanceLog('ScenePerformanceService', '📊 Non-Panel Query Complete:', { | ||||
|       queryId: data.queryId, | ||||
|       queryType: data.queryType, | ||||
|       querySource: data.querySource, | ||||
|       origin: data.origin, | ||||
|       duration: data.duration, | ||||
|       timestamp: data.timestamp, | ||||
|       operationId: data.operationId, | ||||
|     }); | ||||
|     const duration = (data.duration || 0).toFixed(1); | ||||
|     const slowWarning = (data.duration || 0) > 100 ? ' ⚠️ SLOW' : ''; | ||||
| 
 | ||||
|     // Simple, clean format
 | ||||
|     const queryType = data.queryType.replace(/^(getDataSource\/|AnnotationsDataLayer\/)/, ''); // Remove prefixes
 | ||||
|     writePerformanceLog('SPL', `[QUERY ${data.origin}] ${queryType} [${data.queryId}]: ${duration}ms${slowWarning}`); | ||||
|   } | ||||
| 
 | ||||
|   // Standardized performance mark creation methods - now with full type safety!
 | ||||
|  | @ -302,44 +277,26 @@ export class ScenePerformanceService implements ScenePerformanceObserver { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Helper method to get appropriate icon for operation type
 | ||||
|   private getOperationIcon(operation: string): string { | ||||
|     switch (operation) { | ||||
|       case 'query': | ||||
|         return '🔍'; | ||||
|       case 'transform': | ||||
|         return '🔄'; | ||||
|       case 'fieldConfig': | ||||
|         return '🔧'; | ||||
|       case 'render': | ||||
|         return '🎨'; | ||||
|       case 'plugin-load': | ||||
|         return '⚡'; | ||||
|       default: | ||||
|         return '⚡'; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // All performance marks now use standardized functions from performanceConstants.ts
 | ||||
| } | ||||
| 
 | ||||
| // Singleton instance
 | ||||
| let scenePerformanceService: ScenePerformanceService | null = null; | ||||
| let scenePerformanceLogger: ScenePerformanceLogger | null = null; | ||||
| 
 | ||||
| export function getScenePerformanceService(): ScenePerformanceService { | ||||
|   if (!scenePerformanceService) { | ||||
|     scenePerformanceService = new ScenePerformanceService(); | ||||
| export function getScenePerformanceLogger(): ScenePerformanceLogger { | ||||
|   if (!scenePerformanceLogger) { | ||||
|     scenePerformanceLogger = new ScenePerformanceLogger(); | ||||
|   } | ||||
|   return scenePerformanceService; | ||||
|   return scenePerformanceLogger; | ||||
| } | ||||
| 
 | ||||
| export function initializeScenePerformanceService(): void { | ||||
|   getScenePerformanceService().initialize(); | ||||
| export function initializeScenePerformanceLogger(): void { | ||||
|   getScenePerformanceLogger().initialize(); | ||||
| } | ||||
| 
 | ||||
| export function destroyScenePerformanceService(): void { | ||||
|   if (scenePerformanceService) { | ||||
|     scenePerformanceService.destroy(); | ||||
|     scenePerformanceService = null; | ||||
| export function destroyScenePerformanceLogger(): void { | ||||
|   if (scenePerformanceLogger) { | ||||
|     scenePerformanceLogger.destroy(); | ||||
|     scenePerformanceLogger = null; | ||||
|   } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue