Skip to content

Commit

Permalink
[Perfomance] Add Inline documentation for TTFMP (#212393)
Browse files Browse the repository at this point in the history
## Summary

closes elastic/observability-dev#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)
  • Loading branch information
kpatticha authored Mar 4, 2025
1 parent f74b6b5 commit a16dc71
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
Expand All @@ -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)) {
Expand Down Expand Up @@ -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`);
}
Expand All @@ -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,
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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({
Expand All @@ -47,6 +49,7 @@ export function measureInteraction(pathname: string) {
queryRangeSecs: getTimeDifferenceInSeconds(dateRangesInEpoch),
queryOffsetSecs:
rangeTo === 'now' ? 0 : getOffsetFromNowInSeconds(dateRangesInEpoch.endDate),
description,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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}`;
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export function DependenciesInventoryTable() {
meta: {
rangeFrom,
rangeTo,
description:
'[ttfmp_dependencies] Dependencies table is ready after fetching top_dependencies.',
},
});
}
Expand Down

0 comments on commit a16dc71

Please sign in to comment.