Skip to content

Commit

Permalink
Adds event loop delay percentiles to mean delay (elastic#121052) (ela…
Browse files Browse the repository at this point in the history
…stic#121432)

Co-authored-by: Kibana Machine <[email protected]>

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
TinaHeiligers and kibanamachine authored Dec 16, 2021
1 parent 88fbedb commit 21d1296
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 33 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions src/core/public/core_app/status/components/metric_tiles.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ const timeMetric: Metric = {
value: 1234,
};

const metricWithMeta: Metric = {
name: 'Delay',
type: 'time',
value: 1,
meta: {
description: 'Percentiles',
title: '',
value: [1, 5, 10],
type: 'time',
},
};

describe('MetricTile', () => {
it('correct displays an untyped metric', () => {
const component = shallow(<MetricTile metric={untypedMetric} />);
Expand All @@ -55,4 +67,9 @@ describe('MetricTile', () => {
const component = shallow(<MetricTile metric={timeMetric} />);
expect(component).toMatchSnapshot();
});

it('correctly displays a metric with metadata', () => {
const component = shallow(<MetricTile metric={metricWithMeta} />);
expect(component).toMatchSnapshot();
});
});
106 changes: 98 additions & 8 deletions src/core/public/core_app/status/components/metric_tiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,105 @@
*/

import React, { FunctionComponent } from 'react';
import { EuiFlexGrid, EuiFlexItem, EuiCard } from '@elastic/eui';
import { formatNumber, Metric } from '../lib';
import { EuiFlexGrid, EuiFlexItem, EuiCard, EuiStat } from '@elastic/eui';
import { DataType, formatNumber, Metric } from '../lib';

/*
* Displays a metric with the correct format.
* Displays metadata for a metric.
*/
export const MetricTile: FunctionComponent<{ metric: Metric }> = ({ metric }) => {
const { name } = metric;
const MetricCardFooter: FunctionComponent<{
title: string;
description: string;
}> = ({ title, description }) => {
return (
<EuiStat
data-test-subj="serverMetricMeta"
title={title}
titleSize="xxs"
description={description}
reverse
/>
);
};

const DelayMetricTile: FunctionComponent<{ metric: Metric }> = ({ metric }) => {
const { name, meta } = metric;
return (
<EuiCard
data-test-subj={`serverMetric-${formatMetricId(metric)}`}
layout="horizontal"
data-test-subj={`serverMetric-${formatMetricId(name)}`}
title={formatMetric(metric)}
textAlign="left"
description={`${name} avg`}
footer={
meta?.value && (
<MetricCardFooter
title={formatDelayFooterTitle(meta.value, meta.type)}
description={meta.description}
/>
)
}
/>
);
};

const LoadMetricTile: FunctionComponent<{ metric: Metric }> = ({ metric }) => {
const { name, meta } = metric;
return (
<EuiCard
data-test-subj={`serverMetric-${formatMetricId(name)}`}
title={formatMetric(metric)}
textAlign="left"
description={name}
footer={meta && <MetricCardFooter title={meta.title} description={meta.description} />}
/>
);
};

const ResponseTimeMetric: FunctionComponent<{ metric: Metric }> = ({ metric }) => {
const { name, meta } = metric;
return (
<EuiCard
data-test-subj={`serverMetric-${formatMetricId(name)}`}
title={formatMetric(metric)}
textAlign="left"
description={name}
footer={
meta?.value &&
Array.isArray(meta.value) && (
<MetricCardFooter
title={formatNumber(meta.value[0], meta.type)}
description={meta.description}
/>
)
}
/>
);
};

/*
* Displays a metric with the correct format.
*/
export const MetricTile: FunctionComponent<{ metric: Metric }> = ({ metric }) => {
const { name } = metric;
switch (name) {
case 'Delay':
return <DelayMetricTile metric={metric} />;
case 'Load':
return <LoadMetricTile metric={metric} />;
case 'Response time avg':
return <ResponseTimeMetric metric={metric} />;
default:
return (
<EuiCard
data-test-subj={`serverMetric-${formatMetricId(name)}`}
textAlign="left"
title={formatMetric(metric)}
description={name}
/>
);
}
};

/*
* Wrapper component that simply maps each metric to MetricTile inside a FlexGroup
*/
Expand All @@ -38,11 +119,20 @@ export const MetricTiles: FunctionComponent<{ metrics: Metric[] }> = ({ metrics
</EuiFlexGrid>
);

// formatting helper functions

const formatMetric = ({ value, type }: Metric) => {
const metrics = Array.isArray(value) ? value : [value];
return metrics.map((metric) => formatNumber(metric, type)).join(', ');
};

const formatMetricId = ({ name }: Metric) => {
const formatMetricId = (name: Metric['name']) => {
return name.toLowerCase().replace(/[ ]+/g, '-');
};

const formatDelayFooterTitle = (values: number[], type?: DataType) => {
return `
50: ${formatNumber(values[0], type)};
95: ${formatNumber(values[1], type)};
99: ${formatNumber(values[2], type)}`;
};
18 changes: 14 additions & 4 deletions src/core/public/core_app/status/lib/load_status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,23 @@ describe('response processing', () => {
expect(names).toEqual([
'Heap total',
'Heap used',
'Requests per second',
'Load',
'Delay',
'Response time avg',
'Response time max',
'Requests per second',
]);

const values = data.metrics.map((m) => m.value);
expect(values).toEqual([1000000, 100, [4.1, 2.1, 0.1], 4000, 8000, 400]);
expect(values).toEqual([1000000, 100, 400, [4.1, 2.1, 0.1], 1, 4000]);
});

test('adds meta details to Load, Delay and Response time', async () => {
const data = await loadStatus({ http, notifications });
const metricNames = data.metrics.filter((met) => met.meta);
expect(metricNames.map((item) => item.name)).toEqual(['Load', 'Delay', 'Response time avg']);
expect(metricNames.map((item) => item.meta!.description)).toEqual([
'Load interval',
'Percentiles',
'Response time max',
]);
});
});
62 changes: 49 additions & 13 deletions src/core/public/core_app/status/lib/load_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ import type { HttpSetup } from '../../../http';
import type { NotificationsSetup } from '../../../notifications';
import type { DataType } from '../lib';

interface MetricMeta {
title: string;
description: string;
value?: number[];
type?: DataType;
}
export interface Metric {
name: string;
value: number | number[];
type?: DataType;
meta?: MetricMeta;
}

export interface FormattedStatus {
Expand Down Expand Up @@ -57,33 +64,62 @@ function formatMetrics({ metrics }: StatusResponse): Metric[] {
value: metrics.process.memory.heap.used_in_bytes,
type: 'byte',
},
{
name: i18n.translate('core.statusPage.metricsTiles.columns.requestsPerSecHeader', {
defaultMessage: 'Requests per second',
}),
value: (metrics.requests.total * 1000) / metrics.collection_interval_in_millis,
type: 'float',
},
{
name: i18n.translate('core.statusPage.metricsTiles.columns.loadHeader', {
defaultMessage: 'Load',
}),
value: [metrics.os.load['1m'], metrics.os.load['5m'], metrics.os.load['15m']],
type: 'float',
meta: {
description: i18n.translate('core.statusPage.metricsTiles.columns.load.metaHeader', {
defaultMessage: 'Load interval',
}),
title: Object.keys(metrics.os.load).join('; '),
},
},
{
name: i18n.translate('core.statusPage.metricsTiles.columns.resTimeAvgHeader', {
defaultMessage: 'Response time avg',
name: i18n.translate('core.statusPage.metricsTiles.columns.processDelayHeader', {
defaultMessage: 'Delay',
}),
value: metrics.response_times.avg_in_millis,
value: metrics.process.event_loop_delay,
type: 'time',
meta: {
description: i18n.translate(
'core.statusPage.metricsTiles.columns.processDelayDetailsHeader',
{
defaultMessage: 'Percentiles',
}
),
title: '',
value: [
metrics.process.event_loop_delay_histogram?.percentiles['50'],
metrics.process.event_loop_delay_histogram?.percentiles['95'],
metrics.process.event_loop_delay_histogram?.percentiles['99'],
],
type: 'time',
},
},
{
name: i18n.translate('core.statusPage.metricsTiles.columns.resTimeMaxHeader', {
defaultMessage: 'Response time max',
name: i18n.translate('core.statusPage.metricsTiles.columns.resTimeAvgHeader', {
defaultMessage: 'Response time avg',
}),
value: metrics.response_times.max_in_millis,
value: metrics.response_times.avg_in_millis,
type: 'time',
},
{
name: i18n.translate('core.statusPage.metricsTiles.columns.requestsPerSecHeader', {
defaultMessage: 'Requests per second',
}),
value: (metrics.requests.total * 1000) / metrics.collection_interval_in_millis,
type: 'float',
meta: {
description: i18n.translate('core.statusPage.metricsTiles.columns.resTimeMaxHeader', {
defaultMessage: 'Response time max',
}),
title: '',
value: [metrics.response_times.max_in_millis],
type: 'time',
},
},
];
}
Expand Down
5 changes: 5 additions & 0 deletions test/functional/apps/status_page/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(metrics).to.have.length(6);
});

it('should display the server metrics meta', async () => {
const metricsMetas = await testSubjects.findAll('serverMetricMeta');
expect(metricsMetas).to.have.length(3);
});

it('should display the server status', async () => {
const titleText = await testSubjects.getVisibleText('serverStatusTitle');
expect(titleText).to.contain('Kibana status is');
Expand Down

0 comments on commit 21d1296

Please sign in to comment.