diff --git a/pkg/api/frontend_metrics.go b/pkg/api/frontend_metrics.go index 70516447258..6c6901786d2 100644 --- a/pkg/api/frontend_metrics.go +++ b/pkg/api/frontend_metrics.go @@ -2,7 +2,6 @@ package api import ( "net/http" - "strings" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/infra/metrics" @@ -16,11 +15,10 @@ func (hs *HTTPServer) PostFrontendMetrics(c *models.ReqContext) response.Respons return response.Error(http.StatusBadRequest, "bad request data", err) } for _, event := range cmd.Events { - name := strings.Replace(event.Name, "-", "_", -1) - if recorder, ok := metrics.FrontendMetrics[name]; ok { + if recorder, ok := metrics.FrontendMetrics[event.Name]; ok { recorder(event) } else { - c.Logger.Debug("Received unknown frontend metric", "metric", name) + c.Logger.Debug("Received unknown frontend metric", "metric", event.Name) } } return response.Empty(200) diff --git a/pkg/infra/metrics/frontendmetrics.go b/pkg/infra/metrics/frontendmetrics.go index 885e22e5d02..f88643a69de 100644 --- a/pkg/infra/metrics/frontendmetrics.go +++ b/pkg/infra/metrics/frontendmetrics.go @@ -39,5 +39,7 @@ func registerFrontendHistogram(name string, help string) { func initFrontendMetrics() { registerFrontendHistogram("frontend_boot_load_time_seconds", "Frontend boot time measurement") registerFrontendHistogram("frontend_boot_first_paint_time_seconds", "Frontend boot first paint") + registerFrontendHistogram("frontend_boot_first_contentful_paint_time_seconds", "Frontend boot first contentful paint") registerFrontendHistogram("frontend_boot_js_done_time_seconds", "Frontend boot initial js load") + registerFrontendHistogram("frontend_boot_css_time_seconds", "Frontend boot initial css load") } diff --git a/public/app/app.ts b/public/app/app.ts index 9561c683cfd..473d122d973 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -166,21 +166,17 @@ function initEchoSrv() { window.addEventListener('load', (e) => { const loadMetricName = 'frontend_boot_load_time_seconds'; + // Metrics below are marked in public/views/index-template.html + const jsLoadMetricName = 'frontend_boot_js_done_time_seconds'; + const cssLoadMetricName = 'frontend_boot_css_time_seconds'; - if (performance && performance.getEntriesByType) { + if (performance) { performance.mark(loadMetricName); - - const paintMetrics = performance.getEntriesByType('paint'); - - for (const metric of paintMetrics) { - reportPerformance( - `frontend_boot_${metric.name}_time_seconds`, - Math.round(metric.startTime + metric.duration) / 1000 - ); - } - - const loadMetric = performance.getEntriesByName(loadMetricName)[0]; - reportPerformance(loadMetric.name, Math.round(loadMetric.startTime + loadMetric.duration) / 1000); + reportMetricPerformanceMark('first-paint', 'frontend_boot_', '_time_seconds'); + reportMetricPerformanceMark('first-contentful-paint', 'frontend_boot_', '_time_seconds'); + reportMetricPerformanceMark(loadMetricName); + reportMetricPerformanceMark(jsLoadMetricName); + reportMetricPerformanceMark(cssLoadMetricName); } }); @@ -232,4 +228,16 @@ function addClassIfNoOverlayScrollbar() { } } +/** + * Report when a metric of a given name was marked during the document lifecycle. Works for markers with no duration, + * like PerformanceMark or PerformancePaintTiming (e.g. created with performance.mark, or first-contentful-paint) + */ +function reportMetricPerformanceMark(metricName: string, prefix = '', suffix = ''): void { + const metric = _.first(performance.getEntriesByName(metricName)); + if (metric) { + const metricName = metric.name.replace(/-/g, '_'); + reportPerformance(`${prefix}${metricName}${suffix}`, Math.round(metric.startTime) / 1000); + } +} + export default new GrafanaApp();