Instrumentation: Measure app init load times (#67900)

* adding load time metrics for app init.

* chore: add the `metrics.ts` to the CODEOWNERS

* moved file according to PR feedback.

---------

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
This commit is contained in:
Marcus Andersson
2023-05-10 11:03:15 +02:00
committed by GitHub
parent e1ff434917
commit 047763978d
4 changed files with 49 additions and 1 deletions

1
.github/CODEOWNERS vendored
View File

@ -453,6 +453,7 @@ lerna.json @grafana/frontend-ops
/public/app/angular/ @torkelo /public/app/angular/ @torkelo
/public/app/app.ts @grafana/frontend-ops /public/app/app.ts @grafana/frontend-ops
/public/app/dev.ts @grafana/frontend-ops /public/app/dev.ts @grafana/frontend-ops
/public/app/core/utils/metrics.ts @grafana/plugins-platform-frontend
/public/app/index.ts @grafana/frontend-ops /public/app/index.ts @grafana/frontend-ops
/public/app/AppWrapper.tsx @grafana/frontend-ops /public/app/AppWrapper.tsx @grafana/frontend-ops
/public/app/partials/ @grafana/grafana-frontend-platform /public/app/partials/ @grafana/grafana-frontend-platform

View File

@ -67,6 +67,7 @@ import { GAEchoBackend } from './core/services/echo/backends/analytics/GABackend
import { RudderstackBackend } from './core/services/echo/backends/analytics/RudderstackBackend'; import { RudderstackBackend } from './core/services/echo/backends/analytics/RudderstackBackend';
import { GrafanaJavascriptAgentBackend } from './core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend'; import { GrafanaJavascriptAgentBackend } from './core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend';
import { KeybindingSrv } from './core/services/keybindingSrv'; import { KeybindingSrv } from './core/services/keybindingSrv';
import { startMeasure, stopMeasure } from './core/utils/metrics';
import { initDevFeatures } from './dev'; import { initDevFeatures } from './dev';
import { getTimeSrv } from './features/dashboard/services/TimeSrv'; import { getTimeSrv } from './features/dashboard/services/TimeSrv';
import { initGrafanaLive } from './features/live'; import { initGrafanaLive } from './features/live';
@ -119,6 +120,8 @@ export class GrafanaApp {
setBackendSrv(backendSrv); setBackendSrv(backendSrv);
initEchoSrv(); initEchoSrv();
// This needs to be done after the `initEchoSrv` since it is being used under the hood.
startMeasure('frontend_app_init');
addClassIfNoOverlayScrollbar(); addClassIfNoOverlayScrollbar();
setLocale(config.bootData.user.locale); setLocale(config.bootData.user.locale);
setWeekStart(config.bootData.user.weekStart); setWeekStart(config.bootData.user.weekStart);
@ -219,6 +222,8 @@ export class GrafanaApp {
} catch (error) { } catch (error) {
console.error('Failed to start Grafana', error); console.error('Failed to start Grafana', error);
window.__grafana_load_failed(); window.__grafana_load_failed();
} finally {
stopMeasure('frontend_app_init');
} }
} }
} }

View File

@ -0,0 +1,35 @@
import { reportPerformance } from '../services/echo/EchoSrv';
export function startMeasure(eventName: string) {
if (!performance) {
return;
}
try {
performance.mark(`${eventName}_started`);
} catch (error) {
console.error(`[Metrics] Failed to startMeasure ${eventName}`, error);
}
}
export function stopMeasure(eventName: string) {
if (!performance) {
return;
}
try {
const started = `${eventName}_started`;
const completed = `${eventName}_completed`;
const measured = `${eventName}_measured`;
performance.mark(completed);
const measure = performance.measure(measured, started, completed);
reportPerformance(`${eventName}_ms`, measure.duration);
performance.clearMarks(started);
performance.clearMarks(completed);
performance.clearMeasures(measured);
} catch (error) {
console.error(`[Metrics] Failed to stopMeasure ${eventName}`, error);
}
}

View File

@ -1,5 +1,6 @@
import type { PluginExtensionLinkConfig } from '@grafana/data'; import type { PluginExtensionLinkConfig } from '@grafana/data';
import type { AppPluginConfig } from '@grafana/runtime'; import type { AppPluginConfig } from '@grafana/runtime';
import { startMeasure, stopMeasure } from 'app/core/utils/metrics';
import * as pluginLoader from './plugin_loader'; import * as pluginLoader from './plugin_loader';
@ -10,18 +11,24 @@ export type PluginPreloadResult = {
}; };
export async function preloadPlugins(apps: Record<string, AppPluginConfig> = {}): Promise<PluginPreloadResult[]> { export async function preloadPlugins(apps: Record<string, AppPluginConfig> = {}): Promise<PluginPreloadResult[]> {
startMeasure('frontend_plugins_preload');
const pluginsToPreload = Object.values(apps).filter((app) => app.preload); const pluginsToPreload = Object.values(apps).filter((app) => app.preload);
return Promise.all(pluginsToPreload.map(preload)); const result = await Promise.all(pluginsToPreload.map(preload));
stopMeasure('frontend_plugins_preload');
return result;
} }
async function preload(config: AppPluginConfig): Promise<PluginPreloadResult> { async function preload(config: AppPluginConfig): Promise<PluginPreloadResult> {
const { path, version, id: pluginId } = config; const { path, version, id: pluginId } = config;
try { try {
startMeasure(`frontend_plugin_preload_${pluginId}`);
const { plugin } = await pluginLoader.importPluginModule(path, version); const { plugin } = await pluginLoader.importPluginModule(path, version);
const { extensionConfigs = [] } = plugin; const { extensionConfigs = [] } = plugin;
return { pluginId, extensionConfigs }; return { pluginId, extensionConfigs };
} catch (error) { } catch (error) {
console.error(`[Plugins] Failed to preload plugin: ${path} (version: ${version})`, error); console.error(`[Plugins] Failed to preload plugin: ${path} (version: ${version})`, error);
return { pluginId, extensionConfigs: [], error }; return { pluginId, extensionConfigs: [], error };
} finally {
stopMeasure(`frontend_plugin_preload_${pluginId}`);
} }
} }