Skip to content

Commit 8092467

Browse files
[ResponseOps] Change the duration/percentile display format to mm:ss (#124647)
* Change the duration/percentile display format to mm:ss * Addressed comments * Add time format to tooltip * Addressed comments, percentiles can show N/A * Fix flaky test * remove only * address comments, now tests for N/A Co-authored-by: Kibana Machine <[email protected]>
1 parent 153b7e1 commit 8092467

File tree

7 files changed

+126
-47
lines changed

7 files changed

+126
-47
lines changed

x-pack/plugins/triggers_actions_ui/public/application/lib/monitoring_utils.test.ts

+18-5
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,30 @@
44
* 2.0; you may not use this file except in compliance with the Elastic License
55
* 2.0.
66
*/
7-
import { getFormattedSuccessRatio, getFormattedRuleExecutionPercentile } from './monitoring_utils';
7+
import {
8+
getFormattedSuccessRatio,
9+
getFormattedDuration,
10+
getFormattedMilliseconds,
11+
} from './monitoring_utils';
812

913
describe('monitoring_utils', () => {
1014
it('should return a decimal as a percent', () => {
1115
expect(getFormattedSuccessRatio(0.66)).toEqual('66%');
1216
expect(getFormattedSuccessRatio(0.75345345345345)).toEqual('75%');
1317
});
1418

15-
it('should return percentiles as an integer', () => {
16-
expect(getFormattedRuleExecutionPercentile(0)).toEqual('0ms');
17-
expect(getFormattedRuleExecutionPercentile(100.5555)).toEqual('101ms');
18-
expect(getFormattedRuleExecutionPercentile(99.1111)).toEqual('99ms');
19+
it('should return a formatted duration', () => {
20+
expect(getFormattedDuration(0)).toEqual('00:00');
21+
expect(getFormattedDuration(100.111)).toEqual('00:00');
22+
expect(getFormattedDuration(50000)).toEqual('00:50');
23+
expect(getFormattedDuration(500000)).toEqual('08:20');
24+
expect(getFormattedDuration(5000000)).toEqual('83:20');
25+
expect(getFormattedDuration(50000000)).toEqual('833:20');
26+
});
27+
28+
it('should format a duration as an integer', () => {
29+
expect(getFormattedMilliseconds(0)).toEqual('0 ms');
30+
expect(getFormattedMilliseconds(100.5555)).toEqual('101 ms');
31+
expect(getFormattedMilliseconds(99.1111)).toEqual('99 ms');
1932
});
2033
});

x-pack/plugins/triggers_actions_ui/public/application/lib/monitoring_utils.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,25 @@
44
* 2.0; you may not use this file except in compliance with the Elastic License
55
* 2.0.
66
*/
7+
import moment from 'moment';
78
import numeral from '@elastic/numeral';
89

910
export function getFormattedSuccessRatio(successRatio: number) {
1011
const formatted = numeral(successRatio! * 100).format('0,0');
1112
return `${formatted}%`;
1213
}
1314

14-
export function getFormattedRuleExecutionPercentile(percentile: number) {
15-
const formatted = numeral(percentile).format('0,0');
16-
return `${formatted}ms`;
15+
export function getFormattedDuration(value: number) {
16+
if (!value) {
17+
return '00:00';
18+
}
19+
const duration = moment.duration(value);
20+
const minutes = Math.floor(duration.asMinutes()).toString().padStart(2, '0');
21+
const seconds = duration.seconds().toString().padStart(2, '0');
22+
return `${minutes}:${seconds}`;
23+
}
24+
25+
export function getFormattedMilliseconds(value: number) {
26+
const formatted = numeral(value).format('0,0');
27+
return `${formatted} ms`;
1728
}

x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx

+27-15
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
ALERTS_FEATURE_ID,
1919
parseDuration,
2020
} from '../../../../../../alerting/common';
21+
import { getFormattedDuration, getFormattedMilliseconds } from '../../../lib/monitoring_utils';
22+
2123
import { useKibana } from '../../../../common/lib/kibana';
2224
jest.mock('../../../../common/lib/kibana');
2325

@@ -180,22 +182,22 @@ describe('alerts_list component with items', () => {
180182
history: [
181183
{
182184
success: true,
183-
duration: 100,
185+
duration: 1000000,
184186
},
185187
{
186188
success: true,
187-
duration: 200,
189+
duration: 200000,
188190
},
189191
{
190192
success: false,
191-
duration: 300,
193+
duration: 300000,
192194
},
193195
],
194196
calculated_metrics: {
195197
success_ratio: 0.66,
196-
p50: 200,
197-
p95: 300,
198-
p99: 300,
198+
p50: 200000,
199+
p95: 300000,
200+
p99: 300000,
199201
},
200202
},
201203
},
@@ -227,18 +229,18 @@ describe('alerts_list component with items', () => {
227229
history: [
228230
{
229231
success: true,
230-
duration: 100,
232+
duration: 100000,
231233
},
232234
{
233235
success: true,
234-
duration: 500,
236+
duration: 500000,
235237
},
236238
],
237239
calculated_metrics: {
238240
success_ratio: 1,
239241
p50: 0,
240-
p95: 100,
241-
p99: 500,
242+
p95: 100000,
243+
p99: 500000,
242244
},
243245
},
244246
},
@@ -458,7 +460,7 @@ describe('alerts_list component with items', () => {
458460

459461
wrapper.update();
460462
expect(wrapper.find('.euiToolTipPopover').text()).toBe(
461-
'The length of time it took for the rule to run.'
463+
'The length of time it took for the rule to run (mm:ss).'
462464
);
463465

464466
// Status column
@@ -508,14 +510,24 @@ describe('alerts_list component with items', () => {
508510
).toBeTruthy();
509511

510512
let percentiles = wrapper.find(
511-
`EuiTableRowCell[data-test-subj="alertsTableCell-ruleExecutionPercentile"] span[data-test-subj="${Percentiles.P50}Percentile"]`
513+
`EuiTableRowCell[data-test-subj="alertsTableCell-ruleExecutionPercentile"] span[data-test-subj="rule-duration-format-value"]`
512514
);
513515

514516
mockedAlertsData.forEach((rule, index) => {
515517
if (typeof rule.monitoring?.execution.calculated_metrics.p50 === 'number') {
518+
// Ensure the table cells are getting the correct values
516519
expect(percentiles.at(index).text()).toEqual(
517-
`${rule.monitoring.execution.calculated_metrics.p50}ms`
520+
getFormattedDuration(rule.monitoring.execution.calculated_metrics.p50)
518521
);
522+
// Ensure the tooltip is showing the correct content
523+
expect(
524+
wrapper
525+
.find(
526+
'EuiTableRowCell[data-test-subj="alertsTableCell-ruleExecutionPercentile"] [data-test-subj="rule-duration-format-tooltip"]'
527+
)
528+
.at(index)
529+
.props().content
530+
).toEqual(getFormattedMilliseconds(rule.monitoring.execution.calculated_metrics.p50));
519531
} else {
520532
expect(percentiles.at(index).text()).toEqual('N/A');
521533
}
@@ -581,13 +593,13 @@ describe('alerts_list component with items', () => {
581593
).toBeTruthy();
582594

583595
percentiles = wrapper.find(
584-
`EuiTableRowCell[data-test-subj="alertsTableCell-ruleExecutionPercentile"] span[data-test-subj="${Percentiles.P95}Percentile"]`
596+
`EuiTableRowCell[data-test-subj="alertsTableCell-ruleExecutionPercentile"] span[data-test-subj="rule-duration-format-value"]`
585597
);
586598

587599
mockedAlertsData.forEach((rule, index) => {
588600
if (typeof rule.monitoring?.execution.calculated_metrics.p95 === 'number') {
589601
expect(percentiles.at(index).text()).toEqual(
590-
`${rule.monitoring.execution.calculated_metrics.p95}ms`
602+
getFormattedDuration(rule.monitoring.execution.calculated_metrics.p95)
591603
);
592604
} else {
593605
expect(percentiles.at(index).text()).toEqual('N/A');

x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx

+9-14
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,9 @@ import { ManageLicenseModal } from './manage_license_modal';
8686
import { checkAlertTypeEnabled } from '../../../lib/check_alert_type_enabled';
8787
import { RuleEnabledSwitch } from './rule_enabled_switch';
8888
import { PercentileSelectablePopover } from './percentile_selectable_popover';
89-
import {
90-
formatMillisForDisplay,
91-
shouldShowDurationWarning,
92-
} from '../../../lib/execution_duration_utils';
93-
import {
94-
getFormattedSuccessRatio,
95-
getFormattedRuleExecutionPercentile,
96-
} from '../../../lib/monitoring_utils';
89+
import { RuleDurationFormat } from './rule_duration_format';
90+
import { shouldShowDurationWarning } from '../../../lib/execution_duration_utils';
91+
import { getFormattedSuccessRatio } from '../../../lib/monitoring_utils';
9792

9893
const ENTER_KEY = 13;
9994

@@ -396,7 +391,7 @@ export const AlertsList: React.FunctionComponent = () => {
396391
content={i18n.translate(
397392
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.ruleExecutionPercentileTooltip',
398393
{
399-
defaultMessage: `{percentileOrdinal} percentile of this rule's past {sampleLimit} execution durations`,
394+
defaultMessage: `{percentileOrdinal} percentile of this rule's past {sampleLimit} execution durations (mm:ss).`,
400395
values: {
401396
percentileOrdinal: percentileOrdinals[selectedPercentile!],
402397
sampleLimit: MONITORING_HISTORY_LIMIT,
@@ -420,7 +415,7 @@ export const AlertsList: React.FunctionComponent = () => {
420415
const renderPercentileCellValue = (value: number) => {
421416
return (
422417
<span data-test-subj={`${selectedPercentile}Percentile`}>
423-
{typeof value === 'number' ? getFormattedRuleExecutionPercentile(value) : 'N/A'}
418+
<RuleDurationFormat allowZero={false} duration={value} />
424419
</span>
425420
);
426421
};
@@ -630,7 +625,7 @@ export const AlertsList: React.FunctionComponent = () => {
630625
content={i18n.translate(
631626
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.durationTitle',
632627
{
633-
defaultMessage: 'The length of time it took for the rule to run.',
628+
defaultMessage: 'The length of time it took for the rule to run (mm:ss).',
634629
}
635630
)}
636631
>
@@ -651,7 +646,7 @@ export const AlertsList: React.FunctionComponent = () => {
651646

652647
return (
653648
<>
654-
{`${formatMillisForDisplay(value)}`}
649+
{<RuleDurationFormat duration={value} />}
655650
{showDurationWarning && (
656651
<EuiIconTip
657652
data-test-subj="ruleDurationWarning"
@@ -671,6 +666,7 @@ export const AlertsList: React.FunctionComponent = () => {
671666
);
672667
},
673668
},
669+
getPercentileColumn(),
674670
{
675671
field: 'monitoring.execution.calculated_metrics.success_ratio',
676672
width: '12%',
@@ -680,7 +676,7 @@ export const AlertsList: React.FunctionComponent = () => {
680676
content={i18n.translate(
681677
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.successRatioTitle',
682678
{
683-
defaultMessage: 'How often this rule executes successfully',
679+
defaultMessage: 'How often this rule executes successfully.',
684680
}
685681
)}
686682
>
@@ -701,7 +697,6 @@ export const AlertsList: React.FunctionComponent = () => {
701697
);
702698
},
703699
},
704-
getPercentileColumn(),
705700
{
706701
field: 'executionStatus.status',
707702
name: i18n.translate(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React, { memo, useMemo } from 'react';
9+
import { EuiToolTip } from '@elastic/eui';
10+
import { getFormattedDuration, getFormattedMilliseconds } from '../../../lib/monitoring_utils';
11+
12+
interface Props {
13+
duration: number;
14+
allowZero?: boolean;
15+
}
16+
17+
export const RuleDurationFormat = memo((props: Props) => {
18+
const { duration, allowZero = true } = props;
19+
20+
const formattedDuration = useMemo(() => {
21+
if (allowZero || typeof duration === 'number') {
22+
return getFormattedDuration(duration);
23+
}
24+
return 'N/A';
25+
}, [duration, allowZero]);
26+
27+
const formattedTooltip = useMemo(() => {
28+
if (allowZero || typeof duration === 'number') {
29+
return getFormattedMilliseconds(duration);
30+
}
31+
return 'N/A';
32+
}, [duration, allowZero]);
33+
34+
return (
35+
<EuiToolTip data-test-subj="rule-duration-format-tooltip" content={formattedTooltip}>
36+
<span data-test-subj="rule-duration-format-value">{formattedDuration}</span>
37+
</EuiToolTip>
38+
);
39+
});

x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
168168
tags: '',
169169
interval: '1 min',
170170
});
171-
expect(searchResultAfterSave.duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/);
171+
expect(searchResultAfterSave.duration).to.match(/\d{2,}:\d{2}/);
172172

173173
// clean up created alert
174174
const alertsToDelete = await getAlertsByName(alertName);

0 commit comments

Comments
 (0)