mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Perfomance] Add Inline documentation for TTFMP (#212393)
## Summary closes https://github.com/elastic/observability-dev/issues/4101 <img width="1728" alt="image" src="https://github.com/user-attachments/assets/4937722f-f05b-404b-9844-930e80c8e15e" /> ### ⚠️ Instrumentation Pass the `description` as metadata. The prefix [TTFMP] is required. ### How to test - Checkout the PR - make sure you run `yarn kbn bootstrap` - go to any page that has onPageReady function instrumented (ex services)
This commit is contained in:
parent
f74b6b52dc
commit
a16dc711fb
6 changed files with 110 additions and 3 deletions
|
@ -157,7 +157,81 @@ describe('trackPerformanceMeasureEntries', () => {
|
|||
expect(analyticsClientMock.reportEvent).toHaveBeenCalledWith('performance_metric', {
|
||||
duration: 1000,
|
||||
eventName: 'kibana:plugin_render_time',
|
||||
meta: { query_range_secs: 86400, query_offset_secs: 0 },
|
||||
meta: {
|
||||
query_range_secs: 86400,
|
||||
query_offset_secs: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('reports an analytics event with description metadata', () => {
|
||||
setupMockPerformanceObserver([
|
||||
{
|
||||
name: '/',
|
||||
entryType: 'measure',
|
||||
startTime: 100,
|
||||
duration: 1000,
|
||||
detail: {
|
||||
eventName: 'kibana:plugin_render_time',
|
||||
type: 'kibana:performance',
|
||||
meta: {
|
||||
isInitialLoad: false,
|
||||
description:
|
||||
'[ttfmp_dependencies] onPageReady is called when the most important content is rendered',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
trackPerformanceMeasureEntries(analyticsClientMock, true);
|
||||
|
||||
expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(1);
|
||||
expect(analyticsClientMock.reportEvent).toHaveBeenCalledWith('performance_metric', {
|
||||
duration: 1000,
|
||||
eventName: 'kibana:plugin_render_time',
|
||||
meta: {
|
||||
is_initial_load: false,
|
||||
query_range_secs: undefined,
|
||||
query_offset_secs: undefined,
|
||||
description:
|
||||
'[ttfmp_dependencies] onPageReady is called when the most important content is rendered',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('reports an analytics event with truncated description metadata', () => {
|
||||
setupMockPerformanceObserver([
|
||||
{
|
||||
name: '/',
|
||||
entryType: 'measure',
|
||||
startTime: 100,
|
||||
duration: 1000,
|
||||
detail: {
|
||||
eventName: 'kibana:plugin_render_time',
|
||||
type: 'kibana:performance',
|
||||
meta: {
|
||||
isInitialLoad: false,
|
||||
description:
|
||||
'[ttfmp_dependencies] This is a very long long long long long long long long description. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque non risus in nunc tincidunt tincidunt. Proin vehicula, nunc at feugiat cursus, justo nulla fermentum lorem, non ultricies metus libero nec purus. Sed ut perspiciatis unde omnis iste natus.',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
trackPerformanceMeasureEntries(analyticsClientMock, true);
|
||||
const truncatedDescription =
|
||||
'[ttfmp_dependencies] This is a very long long long long long long long long description. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque non risus in nunc tincidunt tincidunt. Proin vehicula, nunc at feugiat cursus, justo nulla fermentum l';
|
||||
|
||||
expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(1);
|
||||
expect(analyticsClientMock.reportEvent).toHaveBeenCalledWith('performance_metric', {
|
||||
duration: 1000,
|
||||
eventName: 'kibana:plugin_render_time',
|
||||
meta: {
|
||||
is_initial_load: false,
|
||||
query_range_secs: undefined,
|
||||
query_offset_secs: undefined,
|
||||
description: truncatedDescription,
|
||||
},
|
||||
});
|
||||
|
||||
expect(truncatedDescription.length).toBe(256);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import type { AnalyticsClient } from '@elastic/ebt/client';
|
|||
import { reportPerformanceMetricEvent } from '@kbn/ebt-tools';
|
||||
|
||||
const MAX_CUSTOM_METRICS = 9;
|
||||
const MAX_DESCRIPTION_LENGTH = 256;
|
||||
// The keys and values for the custom metrics are limited to 9 pairs
|
||||
const ALLOWED_CUSTOM_METRICS_KEYS_VALUES = Array.from({ length: MAX_CUSTOM_METRICS }, (_, i) => [
|
||||
`key${i + 1}`,
|
||||
|
@ -28,6 +29,8 @@ export function trackPerformanceMeasureEntries(analytics: AnalyticsClient, isDev
|
|||
const target = entry?.name;
|
||||
const duration = entry.duration;
|
||||
const meta = entry.detail?.meta;
|
||||
const description = meta?.description;
|
||||
|
||||
const customMetrics = Object.keys(entry.detail?.customMetrics ?? {}).reduce(
|
||||
(acc, metric) => {
|
||||
if (ALLOWED_CUSTOM_METRICS_KEYS_VALUES.includes(metric)) {
|
||||
|
@ -55,6 +58,13 @@ export function trackPerformanceMeasureEntries(analytics: AnalyticsClient, isDev
|
|||
);
|
||||
}
|
||||
|
||||
if (description?.length > MAX_DESCRIPTION_LENGTH) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`The description for the measure: ${target} is too long. The maximum length is ${MAX_DESCRIPTION_LENGTH}. Strings longer than ${MAX_DESCRIPTION_LENGTH} will not be indexed or stored`
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`The measure ${target} completed in ${duration / 1000}s`);
|
||||
}
|
||||
|
@ -74,6 +84,7 @@ export function trackPerformanceMeasureEntries(analytics: AnalyticsClient, isDev
|
|||
meta: {
|
||||
query_range_secs: meta?.queryRangeSecs,
|
||||
query_offset_secs: meta?.queryOffsetSecs,
|
||||
description: description?.slice(0, MAX_DESCRIPTION_LENGTH),
|
||||
is_initial_load: meta?.isInitialLoad,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -14,11 +14,13 @@ import {
|
|||
} from '@kbn/timerange';
|
||||
import { EventData } from '../performance_context';
|
||||
import { perfomanceMarkers } from '../../performance_markers';
|
||||
import { DescriptionWithPrefix } from '../types';
|
||||
|
||||
interface PerformanceMeta {
|
||||
queryRangeSecs: number;
|
||||
queryOffsetSecs: number;
|
||||
isInitialLoad?: boolean;
|
||||
description?: DescriptionWithPrefix;
|
||||
}
|
||||
|
||||
export function measureInteraction(pathname: string) {
|
||||
|
@ -35,7 +37,7 @@ export function measureInteraction(pathname: string) {
|
|||
performance.mark(perfomanceMarkers.endPageReady);
|
||||
|
||||
if (eventData?.meta) {
|
||||
const { rangeFrom, rangeTo } = eventData.meta;
|
||||
const { rangeFrom, rangeTo, description } = eventData.meta;
|
||||
|
||||
// Convert the date range to epoch timestamps (in milliseconds)
|
||||
const dateRangesInEpoch = getDateRange({
|
||||
|
@ -47,6 +49,7 @@ export function measureInteraction(pathname: string) {
|
|||
queryRangeSecs: getTimeDifferenceInSeconds(dateRangesInEpoch),
|
||||
queryOffsetSecs:
|
||||
rangeTo === 'now' ? 0 : getOffsetFromNowInSeconds(dateRangesInEpoch.endDate),
|
||||
description,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,15 @@ import { useLocation } from 'react-router-dom';
|
|||
import { PerformanceApi, PerformanceContext } from './use_performance_context';
|
||||
import { PerformanceMetricEvent } from '../../performance_metric_events';
|
||||
import { measureInteraction } from './measure_interaction';
|
||||
|
||||
import { DescriptionWithPrefix } from './types';
|
||||
export type CustomMetrics = Omit<PerformanceMetricEvent, 'eventName' | 'meta' | 'duration'>;
|
||||
|
||||
export interface Meta {
|
||||
rangeFrom: string;
|
||||
rangeTo: string;
|
||||
description?: DescriptionWithPrefix;
|
||||
}
|
||||
|
||||
export interface EventData {
|
||||
customMetrics?: CustomMetrics;
|
||||
meta?: Meta;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
type ApmPageId = 'services' | 'traces' | 'dependencies';
|
||||
type InfraPageId = 'hosts';
|
||||
|
||||
export type Key = `${ApmPageId}` | `${InfraPageId}`;
|
||||
|
||||
export type DescriptionWithPrefix = `[ttfmp_${Key}] ${string}`;
|
|
@ -59,6 +59,8 @@ export function DependenciesInventoryTable() {
|
|||
meta: {
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
description:
|
||||
'[ttfmp_dependencies] Dependencies table is ready after fetching top_dependencies.',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue