mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution][Detections] Updates Indexing/Query Time columns in Rule Monitoring table to be SUM instead of MAX (#114023)
## Summary Updates the `Indexing Time` & `Query Time` columns in the `Rule Monitoring` table to be `SUM` instead of `MAX`, thus showing the total duration of indexing/querying phases within a Rule's execution rather than just the phase that took the longest. Also adds tooltips to columns for better understanding these metrics. ~Note: Wanted to add a link to documentation for `Last Gap` column, but cannot add links within `EuiToolTip` and didn't want to mis-align design of other column tooltips by introducing a popover. @elastic/security-design please advise on desired action or copy changes here -- thanks!~ 🙂 Update: As guided by design, changed `Last Gap` tooltip to be `EuiPopover` and added link to docs. ##### Indexing Time: <p align="center"> <img width="700" src="https://user-images.githubusercontent.com/2946766/136475361-cedd7c6a-6a0e-4a86-8467-c929aed0f16e.png" /> </p> ##### Query Time: <p align="center"> <img width="700" src="https://user-images.githubusercontent.com/2946766/136475378-1228dfcf-a921-4c0e-8f1e-7594e9c220d4.png" /> </p> ##### Last Gap: <p align="center"> <img width="700" src="https://user-images.githubusercontent.com/2946766/136475412-b54f2419-ced8-43d8-8643-09c8e2cacc44.png" /> </p> ### Checklist Delete any items that are not applicable to this PR. - [X] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)
This commit is contained in:
parent
02822a66fa
commit
15c7bd0192
7 changed files with 129 additions and 21 deletions
|
@ -143,6 +143,7 @@ readonly links: {
|
|||
readonly ruleChangeLog: string;
|
||||
readonly detectionsReq: string;
|
||||
readonly networkMap: string;
|
||||
readonly troubleshootGaps: string;
|
||||
};
|
||||
readonly query: {
|
||||
readonly eql: string;
|
||||
|
|
|
@ -25,6 +25,7 @@ export class DocLinksService {
|
|||
const FLEET_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/fleet/${DOC_LINK_VERSION}/`;
|
||||
const PLUGIN_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/plugins/${DOC_LINK_VERSION}/`;
|
||||
const APM_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/apm/`;
|
||||
const SECURITY_SOLUTION_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/`;
|
||||
|
||||
return deepFreeze({
|
||||
DOC_LINK_VERSION,
|
||||
|
@ -223,13 +224,14 @@ export class DocLinksService {
|
|||
typesRemoval: `${ELASTICSEARCH_DOCS}removal-of-types.html`,
|
||||
},
|
||||
siem: {
|
||||
guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`,
|
||||
gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`,
|
||||
privileges: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/sec-requirements.html`,
|
||||
ml: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/machine-learning.html`,
|
||||
ruleChangeLog: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/prebuilt-rules-changelog.html`,
|
||||
detectionsReq: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/detections-permissions-section.html`,
|
||||
networkMap: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/conf-map-ui.html`,
|
||||
guide: `${SECURITY_SOLUTION_DOCS}index.html`,
|
||||
gettingStarted: `${SECURITY_SOLUTION_DOCS}index.html`,
|
||||
privileges: `${SECURITY_SOLUTION_DOCS}sec-requirements.html`,
|
||||
ml: `${SECURITY_SOLUTION_DOCS}machine-learning.html`,
|
||||
ruleChangeLog: `${SECURITY_SOLUTION_DOCS}prebuilt-rules-changelog.html`,
|
||||
detectionsReq: `${SECURITY_SOLUTION_DOCS}detections-permissions-section.html`,
|
||||
networkMap: `${SECURITY_SOLUTION_DOCS}conf-map-ui.html`,
|
||||
troubleshootGaps: `${SECURITY_SOLUTION_DOCS}alerts-ui-monitor.html#troubleshoot-gaps`,
|
||||
},
|
||||
query: {
|
||||
eql: `${ELASTICSEARCH_DOCS}eql.html`,
|
||||
|
@ -636,6 +638,7 @@ export interface DocLinksStart {
|
|||
readonly ruleChangeLog: string;
|
||||
readonly detectionsReq: string;
|
||||
readonly networkMap: string;
|
||||
readonly troubleshootGaps: string;
|
||||
};
|
||||
readonly query: {
|
||||
readonly eql: string;
|
||||
|
|
|
@ -612,6 +612,7 @@ export interface DocLinksStart {
|
|||
readonly ruleChangeLog: string;
|
||||
readonly detectionsReq: string;
|
||||
readonly networkMap: string;
|
||||
readonly troubleshootGaps: string;
|
||||
};
|
||||
readonly query: {
|
||||
readonly eql: string;
|
||||
|
|
|
@ -11,9 +11,12 @@ import {
|
|||
EuiText,
|
||||
EuiHealth,
|
||||
EuiToolTip,
|
||||
EuiIcon,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedRelative } from '@kbn/i18n/react';
|
||||
import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react';
|
||||
import * as H from 'history';
|
||||
import { sum } from 'lodash';
|
||||
import React, { Dispatch } from 'react';
|
||||
|
||||
import { isMlRule } from '../../../../../../common/machine_learning/helpers';
|
||||
|
@ -36,10 +39,11 @@ import { RulesTableAction } from '../../../../containers/detection_engine/rules/
|
|||
import { LocalizedDateTooltip } from '../../../../../common/components/localized_date_tooltip';
|
||||
import { LinkAnchor } from '../../../../../common/components/links';
|
||||
import { getToolTipContent, canEditRuleWithActions } from '../../../../../common/utils/privileges';
|
||||
import { PopoverTooltip } from './popover_tooltip';
|
||||
import { TagsDisplay } from './tag_display';
|
||||
import { getRuleStatusText } from '../../../../../../common/detection_engine/utils';
|
||||
import { APP_ID, SecurityPageName } from '../../../../../../common/constants';
|
||||
import { NavigateToAppOptions } from '../../../../../../../../../src/core/public';
|
||||
import { DocLinksStart, NavigateToAppOptions } from '../../../../../../../../../src/core/public';
|
||||
|
||||
export const getActions = (
|
||||
dispatch: React.Dispatch<RulesTableAction>,
|
||||
|
@ -312,7 +316,8 @@ export const getColumns = ({
|
|||
|
||||
export const getMonitoringColumns = (
|
||||
navigateToApp: (appId: string, options?: NavigateToAppOptions | undefined) => Promise<void>,
|
||||
formatUrl: FormatUrl
|
||||
formatUrl: FormatUrl,
|
||||
docLinks: DocLinksStart
|
||||
): RulesStatusesColumns[] => {
|
||||
const cols: RulesStatusesColumns[] = [
|
||||
{
|
||||
|
@ -344,12 +349,17 @@ export const getMonitoringColumns = (
|
|||
},
|
||||
{
|
||||
field: 'current_status.bulk_create_time_durations',
|
||||
name: i18n.COLUMN_INDEXING_TIMES,
|
||||
name: (
|
||||
<>
|
||||
{i18n.COLUMN_INDEXING_TIMES}{' '}
|
||||
<EuiToolTip content={i18n.COLUMN_INDEXING_TIMES_TOOLTIP}>
|
||||
<EuiIcon size="m" color="subdued" type="questionInCircle" style={{ margin: 4 }} />
|
||||
</EuiToolTip>
|
||||
</>
|
||||
),
|
||||
render: (value: RuleStatus['current_status']['bulk_create_time_durations']) => (
|
||||
<EuiText data-test-subj="bulk_create_time_durations" size="s">
|
||||
{value != null && value.length > 0
|
||||
? Math.max(...value?.map((item) => Number.parseFloat(item)))
|
||||
: getEmptyTagValue()}
|
||||
{value?.length ? sum(value.map(Number)).toFixed() : getEmptyTagValue()}
|
||||
</EuiText>
|
||||
),
|
||||
truncateText: true,
|
||||
|
@ -357,12 +367,17 @@ export const getMonitoringColumns = (
|
|||
},
|
||||
{
|
||||
field: 'current_status.search_after_time_durations',
|
||||
name: i18n.COLUMN_QUERY_TIMES,
|
||||
name: (
|
||||
<>
|
||||
{i18n.COLUMN_QUERY_TIMES}{' '}
|
||||
<EuiToolTip content={i18n.COLUMN_QUERY_TIMES_TOOLTIP}>
|
||||
<EuiIcon size="m" color="subdued" type="questionInCircle" style={{ margin: 4 }} />
|
||||
</EuiToolTip>
|
||||
</>
|
||||
),
|
||||
render: (value: RuleStatus['current_status']['search_after_time_durations']) => (
|
||||
<EuiText data-test-subj="search_after_time_durations" size="s">
|
||||
{value != null && value.length > 0
|
||||
? Math.max(...value?.map((item) => Number.parseFloat(item)))
|
||||
: getEmptyTagValue()}
|
||||
{value?.length ? sum(value.map(Number)).toFixed() : getEmptyTagValue()}
|
||||
</EuiText>
|
||||
),
|
||||
truncateText: true,
|
||||
|
@ -370,7 +385,28 @@ export const getMonitoringColumns = (
|
|||
},
|
||||
{
|
||||
field: 'current_status.gap',
|
||||
name: i18n.COLUMN_GAP,
|
||||
name: (
|
||||
<>
|
||||
{i18n.COLUMN_GAP}
|
||||
<PopoverTooltip columnName={i18n.COLUMN_GAP}>
|
||||
<EuiText style={{ width: 300 }}>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Duration of most recent gap in Rule execution. Adjust Rule look-back or {seeDocs} for mitigating gaps."
|
||||
id="xpack.securitySolution.detectionEngine.rules.allRules.columns.gapTooltip"
|
||||
values={{
|
||||
seeDocs: (
|
||||
<EuiLink href={`${docLinks.links.siem.troubleshootGaps}`} target="_blank">
|
||||
{'see documentation'}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</PopoverTooltip>
|
||||
</>
|
||||
),
|
||||
render: (value: RuleStatus['current_status']['gap']) => (
|
||||
<EuiText data-test-subj="gap" size="s">
|
||||
{value ?? getEmptyTagValue()}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { EuiPopover, EuiButtonIcon } from '@elastic/eui';
|
||||
import * as i18n from '../translations';
|
||||
|
||||
interface PopoverTooltipProps {
|
||||
columnName: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Table column tooltip component utilizing EuiPopover for rich content like documentation links
|
||||
* @param columnName string Name of column to use as aria-label of button
|
||||
* @param children React.ReactNode of content to display in popover tooltip
|
||||
*/
|
||||
const PopoverTooltipComponent = ({ columnName, children }: PopoverTooltipProps) => {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
anchorPosition={'upCenter'}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={() => setIsPopoverOpen(false)}
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
aria-label={i18n.POPOVER_TOOLTIP_ARIA_LABEL(columnName)}
|
||||
onClick={() => setIsPopoverOpen(!isPopoverOpen)}
|
||||
size="s"
|
||||
color="primary"
|
||||
iconType="questionInCircle"
|
||||
/>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</EuiPopover>
|
||||
);
|
||||
};
|
||||
|
||||
export const PopoverTooltip = React.memo(PopoverTooltipComponent);
|
||||
|
||||
PopoverTooltip.displayName = 'PopoverTooltip';
|
|
@ -96,6 +96,7 @@ export const RulesTables = React.memo<RulesTableProps>(
|
|||
setRefreshRulesData,
|
||||
selectedTab,
|
||||
}) => {
|
||||
const docLinks = useKibana().services.docLinks;
|
||||
const [initLoading, setInitLoading] = useState(true);
|
||||
|
||||
const {
|
||||
|
@ -299,8 +300,8 @@ export const RulesTables = React.memo<RulesTableProps>(
|
|||
]);
|
||||
|
||||
const monitoringColumns = useMemo(
|
||||
() => getMonitoringColumns(navigateToApp, formatUrl),
|
||||
[navigateToApp, formatUrl]
|
||||
() => getMonitoringColumns(navigateToApp, formatUrl, docLinks),
|
||||
[navigateToApp, formatUrl, docLinks]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -13,6 +13,11 @@ export const BACK_TO_DETECTIONS = i18n.translate(
|
|||
defaultMessage: 'Back to detections',
|
||||
}
|
||||
);
|
||||
export const POPOVER_TOOLTIP_ARIA_LABEL = (columnName: string) =>
|
||||
i18n.translate('xpack.securitySolution.detectionEngine.rules.popoverTooltip.ariaLabel', {
|
||||
defaultMessage: 'Tooltip for column: {columnName}',
|
||||
values: { columnName },
|
||||
});
|
||||
|
||||
export const IMPORT_RULE = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.importRuleTitle',
|
||||
|
@ -364,6 +369,13 @@ export const COLUMN_INDEXING_TIMES = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const COLUMN_INDEXING_TIMES_TOOLTIP = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.allRules.columns.indexingTimesTooltip',
|
||||
{
|
||||
defaultMessage: 'Total time spent indexing alerts during last Rule execution',
|
||||
}
|
||||
);
|
||||
|
||||
export const COLUMN_QUERY_TIMES = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.allRules.columns.queryTimes',
|
||||
{
|
||||
|
@ -371,6 +383,13 @@ export const COLUMN_QUERY_TIMES = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const COLUMN_QUERY_TIMES_TOOLTIP = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.allRules.columns.queryTimesTooltip',
|
||||
{
|
||||
defaultMessage: 'Total time spent querying source indices during last Rule execution',
|
||||
}
|
||||
);
|
||||
|
||||
export const COLUMN_GAP = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.allRules.columns.gap',
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue