Dashboards: Monitor dashboard loading performance (#99629)

* WIP benchmark dashboard rendering

* Script

* Benchmark with variable and a panel

* Add one more benchmark

* Explicitely enable profiling

* Playwright tests

* update scenes

* Report measurement to faro when config set

* Let user enable metrics reporting in UI

* Fix logging

* Change how performance metrics is enabled per dashboard, now in config file only

* add benchmark run option

* Fix benchmark runs

* fix description for performance config

* remove console.log

* update codeowners

* add back crashDetection init that was lost in merge

* fix yarn.lock

* restore custom.ini

* fix import

* Make sure we have the echoSrv

* fix config type

* Try to limit changes to e2e runs

* remove benchmark

* Fix lint issue

* fix codeowners

---------

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Sergej-Vlasov <sergej.s.vlasov@gmail.com>
This commit is contained in:
Oscar Kilhed
2025-01-28 18:36:10 +01:00
committed by GitHub
parent 92d5e82a33
commit 056b5a7b08
12 changed files with 71 additions and 12 deletions

View File

@@ -2,6 +2,7 @@ import { isEqual } from 'lodash';
import { locationUtil, UrlQueryMap } from '@grafana/data';
import { config, getBackendSrv, isFetchError, locationService } from '@grafana/runtime';
import { sceneGraph } from '@grafana/scenes';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { StateManagerBase } from 'app/core/services/StateManagerBase';
import { getMessageFromError, getMessageIdFromError, getStatusFromError } from 'app/core/utils/errors';
@@ -135,7 +136,10 @@ abstract class DashboardScenePageStateManagerBase<T>
this.setState({ dashboard: dashboard, isLoading: false, options });
const measure = stopMeasure(LOAD_SCENE_MEASUREMENT);
const queryController = sceneGraph.getQueryController(dashboard);
trackDashboardSceneLoaded(dashboard, measure?.duration);
queryController?.startProfile(dashboard);
if (options.route !== DashboardRoutes.New) {
emitDashboardViewEvent({

View File

@@ -1,7 +1,7 @@
import { uniqueId } from 'lodash';
import { DataFrameDTO, DataFrameJSON } from '@grafana/data';
import { config } from '@grafana/runtime';
import { config, logMeasurement, reportInteraction } from '@grafana/runtime';
import {
VizPanel,
SceneTimePicker,
@@ -19,6 +19,7 @@ import {
SceneDataLayerProvider,
SceneDataLayerControls,
UserActionEvent,
SceneInteractionProfileEvent,
SceneObjectState,
} from '@grafana/scenes';
import { contextSrv } from 'app/core/core';
@@ -229,7 +230,11 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel,
new behaviors.CursorSync({
sync: oldModel.graphTooltip,
}),
new behaviors.SceneQueryController(),
new behaviors.SceneQueryController({
enableProfiling:
config.dashboardPerformanceMetrics.findIndex((uid) => uid === '*' || uid === oldModel.uid) !== -1,
onProfileComplete: getDashboardInteractionCallback(oldModel.uid, oldModel.title),
}),
registerDashboardMacro,
registerPanelInteractionsReporter,
new behaviors.LiveNowTimer({ enabled: oldModel.liveNow }),
@@ -434,3 +439,40 @@ function trackIfEmpty(grid: SceneGridLayout) {
sub.unsubscribe();
};
}
function getDashboardInteractionCallback(uid: string, title: string) {
return (e: SceneInteractionProfileEvent) => {
let interactionType = '';
if (e.origin === 'SceneTimeRange') {
interactionType = 'time-range-change';
} else if (e.origin === 'SceneRefreshPicker') {
interactionType = 'refresh';
} else if (e.origin === 'DashboardScene') {
interactionType = 'view';
} else if (e.origin.indexOf('Variable') > -1) {
interactionType = 'variable-change';
}
reportInteraction('dashboard-render', {
interactionType,
duration: e.duration,
networkDuration: e.networkDuration,
totalJSHeapSize: e.totalJSHeapSize,
usedJSHeapSize: e.usedJSHeapSize,
jsHeapSizeLimit: e.jsHeapSizeLimit,
});
logMeasurement(
`dashboard.${interactionType}`,
{
duration: e.duration,
networkDuration: e.networkDuration,
totalJSHeapSize: e.totalJSHeapSize,
usedJSHeapSize: e.usedJSHeapSize,
jsHeapSizeLimit: e.jsHeapSizeLimit,
timeSinceBoot: performance.measure('time_since_boot', 'frontend_boot_js_done_time_seconds').duration,
},
{ dashboard: uid, title: title }
);
};
}