mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardScene: Measure and report scene load time (#86267)
* measure scene load time * Fix tests that fail due to performance not being the proper global performance object in jest * add isScene parameter to tracking test
This commit is contained in:
parent
d46b163810
commit
e7f40493e4
@ -1,7 +1,7 @@
|
||||
import { reportPerformance } from '../services/echo/EchoSrv';
|
||||
|
||||
export function startMeasure(eventName: string) {
|
||||
if (!performance) {
|
||||
if (!performance || !performance.mark) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ export function startMeasure(eventName: string) {
|
||||
}
|
||||
|
||||
export function stopMeasure(eventName: string) {
|
||||
if (!performance) {
|
||||
if (!performance || !performance.mark) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -29,7 +29,9 @@ export function stopMeasure(eventName: string) {
|
||||
performance.clearMarks(started);
|
||||
performance.clearMarks(completed);
|
||||
performance.clearMeasures(measured);
|
||||
return measure;
|
||||
} catch (error) {
|
||||
console.error(`[Metrics] Failed to stopMeasure ${eventName}`, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ import { locationUtil } from '@grafana/data';
|
||||
import { config, getBackendSrv, isFetchError, locationService } from '@grafana/runtime';
|
||||
import { StateManagerBase } from 'app/core/services/StateManagerBase';
|
||||
import { default as localStorageStore } from 'app/core/store';
|
||||
import { startMeasure, stopMeasure } from 'app/core/utils/metrics';
|
||||
import { dashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import {
|
||||
DASHBOARD_FROM_LS_KEY,
|
||||
removeDashboardToFetchFromLocalStorage,
|
||||
} from 'app/features/dashboard/state/initDashboard';
|
||||
import { trackDashboardSceneLoaded } from 'app/features/dashboard/utils/tracking';
|
||||
import { DashboardDTO, DashboardRoutes } from 'app/types';
|
||||
|
||||
import { PanelEditor } from '../panel-edit/PanelEditor';
|
||||
@ -26,6 +28,8 @@ export interface DashboardScenePageState {
|
||||
|
||||
export const DASHBOARD_CACHE_TTL = 500;
|
||||
|
||||
const LOAD_SCENE_MEASUREMENT = 'loadDashboardScene';
|
||||
|
||||
/** Only used by cache in loading home in DashboardPageProxy and initDashboard (Old arch), can remove this after old dashboard arch is gone */
|
||||
export const HOME_DASHBOARD_CACHE_KEY = '__grafana_home_uid__';
|
||||
|
||||
@ -167,6 +171,7 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
||||
|
||||
public async loadDashboard(options: LoadDashboardOptions) {
|
||||
try {
|
||||
startMeasure(LOAD_SCENE_MEASUREMENT);
|
||||
const dashboard = await this.loadScene(options);
|
||||
if (!dashboard) {
|
||||
return;
|
||||
@ -177,6 +182,8 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
||||
}
|
||||
|
||||
this.setState({ dashboard: dashboard, isLoading: false });
|
||||
const measure = stopMeasure(LOAD_SCENE_MEASUREMENT);
|
||||
trackDashboardSceneLoaded(dashboard, measure?.duration);
|
||||
} catch (err) {
|
||||
this.setState({ isLoading: false, loadError: String(err) });
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import {
|
||||
AdHocFiltersVariable,
|
||||
} from '@grafana/scenes';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { trackDashboardLoaded } from 'app/features/dashboard/utils/tracking';
|
||||
import { DashboardDTO } from 'app/types';
|
||||
|
||||
import { AlertStatesDataLayer } from '../scene/AlertStatesDataLayer';
|
||||
@ -284,7 +283,6 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
|
||||
}),
|
||||
new behaviors.SceneQueryController(),
|
||||
registerDashboardMacro,
|
||||
registerDashboardSceneTracking(oldModel),
|
||||
registerPanelInteractionsReporter,
|
||||
new behaviors.LiveNowTimer({ enabled: oldModel.liveNow }),
|
||||
],
|
||||
@ -517,18 +515,6 @@ export function buildGridItemForPanel(panel: PanelModel): DashboardGridItem {
|
||||
});
|
||||
}
|
||||
|
||||
function registerDashboardSceneTracking(model: DashboardModel) {
|
||||
return () => {
|
||||
const unsetDashboardInteractionsScenesContext = DashboardInteractions.setScenesContext();
|
||||
|
||||
trackDashboardLoaded(model, model.version);
|
||||
|
||||
return () => {
|
||||
unsetDashboardInteractionsScenesContext();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function registerPanelInteractionsReporter(scene: DashboardScene) {
|
||||
// Subscriptions set with subscribeToEvent are automatically unsubscribed when the scene deactivated
|
||||
scene.subscribeToEvent(UserActionEvent, (e) => {
|
||||
|
@ -6,6 +6,7 @@ import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import { KeybindingSrv } from 'app/core/services/keybindingSrv';
|
||||
import store from 'app/core/store';
|
||||
import { startMeasure, stopMeasure } from 'app/core/utils/metrics';
|
||||
import { dashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
|
||||
import { DashboardSrv, getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
@ -30,6 +31,8 @@ import { PanelModel } from './PanelModel';
|
||||
import { emitDashboardViewEvent } from './analyticsProcessor';
|
||||
import { dashboardInitCompleted, dashboardInitFailed, dashboardInitFetching, dashboardInitServices } from './reducers';
|
||||
|
||||
const INIT_DASHBOARD_MEASUREMENT = 'initDashboard';
|
||||
|
||||
export interface InitDashboardArgs {
|
||||
urlUid?: string;
|
||||
urlSlug?: string;
|
||||
@ -174,7 +177,7 @@ const getQueriesByDatasource = (
|
||||
*/
|
||||
export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
|
||||
return async (dispatch, getState) => {
|
||||
const initStart = performance.now();
|
||||
startMeasure(INIT_DASHBOARD_MEASUREMENT);
|
||||
|
||||
// set fetching state
|
||||
dispatch(dashboardInitFetching());
|
||||
@ -287,8 +290,8 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
|
||||
})
|
||||
);
|
||||
|
||||
const duration = performance.now() - initStart;
|
||||
trackDashboardLoaded(dashboard, duration, versionBeforeMigration);
|
||||
const measure = stopMeasure(INIT_DASHBOARD_MEASUREMENT);
|
||||
trackDashboardLoaded(dashboard, measure?.duration, versionBeforeMigration);
|
||||
|
||||
// yay we are done
|
||||
dispatch(dashboardInitCompleted(dashboard));
|
||||
|
@ -37,6 +37,7 @@ describe('trackDashboardLoaded', () => {
|
||||
|
||||
expect(reportInteractionSpy).toHaveBeenCalledWith('dashboards_init_dashboard_completed', {
|
||||
duration: 200,
|
||||
isScene: false,
|
||||
uid: 'dashboard-123',
|
||||
title: 'Test Dashboard',
|
||||
schemaVersion: model.schemaVersion, // This value is based on public/app/features/dashboard/state/DashboardMigrator.ts#L81
|
||||
|
@ -1,23 +1,14 @@
|
||||
import { Panel, VariableModel } from '@grafana/schema/dist/esm/index';
|
||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||
|
||||
import { DashboardModel } from '../state';
|
||||
import { DashboardModel, PanelModel } from '../state';
|
||||
|
||||
export function trackDashboardLoaded(dashboard: DashboardModel, duration: number, versionBeforeMigration?: number) {
|
||||
export function trackDashboardLoaded(dashboard: DashboardModel, duration?: number, versionBeforeMigration?: number) {
|
||||
// Count the different types of variables
|
||||
const variables = dashboard.templating.list
|
||||
.map((v) => v.type)
|
||||
.reduce((r: Record<string, number>, k) => {
|
||||
r[variableName(k)] = 1 + r[variableName(k)] || 1;
|
||||
return r;
|
||||
}, {});
|
||||
|
||||
const variables = getVariables(dashboard.templating.list);
|
||||
// Count the different types of panels
|
||||
const panels = dashboard.panels
|
||||
.map((p) => p.type)
|
||||
.reduce((r: Record<string, number>, p) => {
|
||||
r[panelName(p)] = 1 + r[panelName(p)] || 1;
|
||||
return r;
|
||||
}, {});
|
||||
const panels = getPanelCounts(dashboard.panels);
|
||||
|
||||
DashboardInteractions.dashboardInitialized({
|
||||
uid: dashboard.uid,
|
||||
@ -31,8 +22,49 @@ export function trackDashboardLoaded(dashboard: DashboardModel, duration: number
|
||||
settings_nowdelay: dashboard.timepicker.nowDelay,
|
||||
settings_livenow: !!dashboard.liveNow,
|
||||
duration,
|
||||
isScene: false,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackDashboardSceneLoaded(dashboard: DashboardScene, duration?: number) {
|
||||
const initialSaveModel = dashboard.getInitialSaveModel();
|
||||
if (initialSaveModel) {
|
||||
const panels = getPanelCounts(initialSaveModel.panels || []);
|
||||
const variables = getVariables(initialSaveModel.templating?.list || []);
|
||||
DashboardInteractions.dashboardInitialized({
|
||||
uid: initialSaveModel.uid,
|
||||
title: initialSaveModel.title,
|
||||
theme: undefined,
|
||||
schemaVersion: initialSaveModel.schemaVersion,
|
||||
version_before_migration: initialSaveModel.version,
|
||||
panels_count: initialSaveModel.panels?.length || 0,
|
||||
...panels,
|
||||
...variables,
|
||||
settings_nowdelay: undefined,
|
||||
settings_livenow: !!initialSaveModel.liveNow,
|
||||
duration,
|
||||
isScene: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getPanelCounts(panels: Panel[] | PanelModel[]) {
|
||||
return panels
|
||||
.map((p) => p.type)
|
||||
.reduce((r: Record<string, number>, p) => {
|
||||
r[panelName(p)] = 1 + r[panelName(p)] || 1;
|
||||
return r;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function getVariables(variableList: VariableModel[]) {
|
||||
return variableList
|
||||
.map((v) => v.type)
|
||||
.reduce((r: Record<string, number>, k) => {
|
||||
r[variableName(k)] = 1 + r[variableName(k)] || 1;
|
||||
return r;
|
||||
}, {});
|
||||
}
|
||||
|
||||
const variableName = (type: string) => `variable_type_${type}_count`;
|
||||
const panelName = (type: string) => `panel_type_${type}_count`;
|
||||
|
Loading…
Reference in New Issue
Block a user