[Infrastructure UI] Metrics explanation tooltips (#159941)

## Summary

This PR adds better tooltip content to the KPI tiles and table columns.
It also adds links to hosts metrics documentation


![image](3df60a99-32c0-4e08-9a84-e489db2d53fc)

The formulas displayed on the tooltips/popovers are the same ones used
in the Lens charts. Even though the table content executes a query in
elasticsearch, with pipeline aggregations, they can be translated into
the equivalent Lens formulas.

### How to test this PR

- Start a local kibana instance
- Navigate to `Inventory > Hosts`
- Click on `What are these metrics?` links, they should redirect to
https://www.elastic.co/guide/en/observability/current/host-metrics.html
- Hover over the KPI tiles and verify their content
- Click on `?` icons next to the table column label and verify their
content
  - Clicking on `?` icon must not sort the table
This commit is contained in:
Carlos Crespo 2023-06-20 15:33:18 +02:00 committed by GitHub
parent ca0b7395fb
commit e7c7445d0f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 462 additions and 138 deletions

View file

@ -0,0 +1,24 @@
/*
* 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 { useEuiTheme } from '@elastic/eui';
import { useMemo } from 'react';
export const useKibanaHeader = () => {
const { euiTheme } = useEuiTheme();
const headerHeight = useMemo(() => {
const wrapper = document.querySelector(`[data-test-subj="kibanaChrome"]`);
if (!wrapper) {
return parseInt(euiTheme.size.xxxl, 10) * 2;
}
return wrapper.getBoundingClientRect().top;
}, [euiTheme]);
return { headerHeight };
};

View file

@ -58,13 +58,13 @@ export const useLensAttributes = ({
const { navigateToPrefilledEditor } = lens;
const { value, error } = useAsync(lens.stateHelperApi, [lens]);
const { formula: formulaAPI } = value ?? {};
const lensChartConfig = hostLensFormulas[type];
const attributes = useMemo(() => {
if (!dataView || !formulaAPI) {
return null;
}
const lensChartConfig = hostLensFormulas[type];
const VisualizationType = visualizationTypes[visualizationType];
const visualizationAttributes = buildLensAttributes(
@ -72,7 +72,7 @@ export const useLensAttributes = ({
);
return visualizationAttributes;
}, [dataView, formulaAPI, options, type, visualizationType]);
}, [dataView, formulaAPI, options, visualizationType, lensChartConfig]);
const injectFilters = ({
filters,
@ -141,6 +141,9 @@ export const useLensAttributes = ({
},
};
};
const {
formula: { formula },
} = lensChartConfig;
return { attributes, getExtraActions, error };
return { formula, attributes, getExtraActions, error };
};

View file

@ -14,7 +14,7 @@ export interface Props extends Pick<MetricWTrend, 'title' | 'color' | 'extra' |
id: string;
loading: boolean;
value: number;
toolTip: string;
toolTip: React.ReactNode;
['data-test-subj']?: string;
}

View file

@ -13,6 +13,7 @@ import { useMetricsDataViewContext } from '../hooks/use_data_view';
import { UnifiedSearchBar } from './search_bar/unified_search_bar';
import { HostsContent } from './hosts_content';
import { ErrorCallout } from './error_callout';
import { UnifiedSearchProvider } from '../hooks/use_unified_search';
export const HostContainer = () => {
const { dataView, loading, error, metricAlias, loadDataView } = useMetricsDataViewContext();
@ -45,10 +46,10 @@ export const HostContainer = () => {
hasTryAgainButton
/>
) : (
<>
<UnifiedSearchProvider>
<UnifiedSearchBar />
<EuiSpacer />
<HostsContent />
</>
</UnifiedSearchProvider>
);
};

View file

@ -12,10 +12,19 @@ import { KPIGrid } from './kpis/kpi_grid';
import { Tabs } from './tabs/tabs';
import { AlertsQueryProvider } from '../hooks/use_alerts_query';
import { HostsViewProvider } from '../hooks/use_hosts_view';
import { HostsTableProvider } from '../hooks/use_hosts_table';
import { HostsTableProvider, useHostsTableContext } from '../hooks/use_hosts_table';
import { ErrorCallout } from './error_callout';
import { useUnifiedSearchContext } from '../hooks/use_unified_search';
const Container = ({ children }: { children: React.ReactNode }) => {
const { refs } = useHostsTableContext();
return (
<>
{children}
<div data-test-subj="popover-container" ref={refs.popoverContainerRef} />
</>
);
};
export const HostsContent = () => {
const { error } = useUnifiedSearchContext();
@ -26,19 +35,21 @@ export const HostsContent = () => {
) : (
<HostsViewProvider>
<HostsTableProvider>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<KPIGrid />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<HostsTable />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AlertsQueryProvider>
<Tabs />
</AlertsQueryProvider>
</EuiFlexItem>
</EuiFlexGroup>
<Container>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<KPIGrid />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<HostsTable />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AlertsQueryProvider>
<Tabs />
</AlertsQueryProvider>
</EuiFlexItem>
</EuiFlexGroup>
</Container>
</HostsTableProvider>
</HostsViewProvider>
)}

View file

@ -6,20 +6,20 @@
*/
import { i18n } from '@kbn/i18n';
import React from 'react';
import { hostLensFormulas } from '../../../../../common/visualizations';
import { useHostCountContext } from '../../hooks/use_host_count';
import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
import { TOOLTIP } from '../../translations';
import { type Props, MetricChartWrapper } from '../chart/metric_chart_wrapper';
import { TooltipContent } from '../metric_explanation/tooltip_content';
const HOSTS_CHART: Omit<Props, 'loading' | 'value'> = {
const HOSTS_CHART: Omit<Props, 'loading' | 'value' | 'toolTip'> = {
id: `metric-hostCount`,
color: '#6DCCB1',
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.title', {
defaultMessage: 'Hosts',
}),
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip', {
defaultMessage: 'The number of hosts returned by your current search criteria.',
}),
['data-test-subj']: 'hostsViewKPI-hostsCount',
};
@ -43,6 +43,12 @@ export const HostsTile = () => {
{...HOSTS_CHART}
value={hostCountData?.count.value ?? 0}
subtitle={getSubtitle()}
toolTip={
<TooltipContent
formula={hostLensFormulas.hostCount.formula.formula}
description={TOOLTIP.hostCount}
/>
}
loading={hostCountLoading}
/>
);

View file

@ -8,9 +8,12 @@ import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiSpacer } from '@elastic/eui';
import { KPIChartProps, Tile } from './tile';
import { HostCountProvider } from '../../hooks/use_host_count';
import { TOOLTIP } from '../../translations';
import { HostsTile } from './hosts_tile';
import { HostMetricsDocsLink } from '../metric_explanation/host_metrics_docs_link';
const KPI_CHARTS: Array<Omit<KPIChartProps, 'loading' | 'subtitle'>> = [
{
@ -20,10 +23,7 @@ const KPI_CHARTS: Array<Omit<KPIChartProps, 'loading' | 'subtitle'>> = [
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpuUsage.title', {
defaultMessage: 'CPU Usage',
}),
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpuUsage.tooltip', {
defaultMessage:
'Percentage of CPU time spent in states other than Idle and IOWait, normalized by the number of CPU cores. This includes both time spent on user space and kernel space.',
}),
toolTip: TOOLTIP.cpuUsage,
},
{
type: 'normalizedLoad1m',
@ -32,9 +32,7 @@ const KPI_CHARTS: Array<Omit<KPIChartProps, 'loading' | 'subtitle'>> = [
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.normalizedLoad1m.title', {
defaultMessage: 'Normalized Load',
}),
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.normalizedLoad1m.tooltip', {
defaultMessage: '1 minute load average normalized by the number of CPU cores.',
}),
toolTip: TOOLTIP.rx,
},
{
type: 'memoryUsage',
@ -54,15 +52,15 @@ const KPI_CHARTS: Array<Omit<KPIChartProps, 'loading' | 'subtitle'>> = [
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.diskSpaceUsage.title', {
defaultMessage: 'Disk Space Usage',
}),
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.diskSpaceUsage.tooltip', {
defaultMessage: 'Percentage of disk space used.',
}),
toolTip: TOOLTIP.tx,
},
];
export const KPIGrid = () => {
return (
<HostCountProvider>
<HostMetricsDocsLink />
<EuiSpacer size="s" />
<EuiFlexGroup
direction="row"
gutterSize="s"

View file

@ -27,6 +27,7 @@ import { LensWrapper } from '../chart/lens_wrapper';
import { createHostsFilter } from '../../utils';
import { useHostCountContext } from '../../hooks/use_host_count';
import { useAfterLoadedState } from '../../hooks/use_after_loaded_state';
import { TooltipContent } from '../metric_explanation/tooltip_content';
export interface KPIChartProps {
title: string;
@ -66,7 +67,7 @@ export const Tile = ({
});
};
const { attributes, getExtraActions, error } = useLensAttributes({
const { formula, attributes, getExtraActions, error } = useLensAttributes({
type,
dataView,
options: {
@ -142,9 +143,8 @@ export const Tile = ({
</EuiFlexGroup>
) : (
<EuiToolTip
className="eui-fullWidth"
delay="regular"
content={toolTip}
content={<TooltipContent formula={formula} description={toolTip} />}
anchorClassName="eui-fullWidth"
>
<LensWrapper

View file

@ -0,0 +1,28 @@
/*
* 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 from 'react';
import { EuiLink, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { HOST_METRICS_DOC_HREF } from '../../constants';
export const HostMetricsDocsLink = () => {
return (
<EuiText size="xs">
<EuiLink
data-test-subj="hostsViewMetricsDocumentationLink"
href={HOST_METRICS_DOC_HREF}
target="_blank"
>
<FormattedMessage
id="xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink"
defaultMessage="What are these metrics?"
/>
</EuiLink>
</EuiText>
);
};

View file

@ -0,0 +1,71 @@
/*
* 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, { HTMLAttributes } from 'react';
import { EuiText, EuiLink } from '@elastic/eui';
import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react';
import { HOST_METRICS_DOC_HREF } from '../../constants';
interface Props extends Pick<HTMLAttributes<HTMLDivElement>, 'style'> {
description: string;
formula?: string;
showDocumentationLink?: boolean;
}
export const TooltipContent = ({
description,
formula,
showDocumentationLink = false,
style,
}: Props) => {
return (
<EuiText size="xs" style={style}>
<p>{description}</p>
{formula && (
<p>
<strong>
<FormattedMessage
id="xpack.infra.hostsViewPage.table.tooltip.formula"
defaultMessage="Formula Calculation:"
/>
</strong>
<br />
<code
css={css`
word-break: break-word;
`}
>
{formula}
</code>
</p>
)}
{showDocumentationLink && (
<p>
<FormattedMessage
id="xpack.infra.hostsViewPage.table.tooltip.documentationLabel"
defaultMessage="See {documentation} for more information"
values={{
documentation: (
<EuiLink
data-test-subj="hostsViewTooltipDocumentationLink"
href={HOST_METRICS_DOC_HREF}
target="_blank"
>
<FormattedMessage
id="xpack.infra.hostsViewPage.table.tooltip.documentationLink"
defaultMessage="documentation"
/>
</EuiLink>
),
}}
/>
</p>
)}
</EuiText>
);
};

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useCallback, useMemo } from 'react';
import React, { useCallback } from 'react';
import type { Query, TimeRange, Filter } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import {
@ -16,6 +16,7 @@ import {
EuiFlexItem,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { useKibanaHeader } from '../../../../../hooks/use_kibana_header';
import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana';
import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
import { ControlsContent } from './controls_content';
@ -98,26 +99,18 @@ export const UnifiedSearchBar = () => {
const StickyContainer = (props: { children: React.ReactNode }) => {
const { euiTheme } = useEuiTheme();
const top = useMemo(() => {
const wrapper = document.querySelector(`[data-test-subj="kibanaChrome"]`);
if (!wrapper) {
return `calc(${euiTheme.size.xxxl} * 2)`;
}
return `${wrapper.getBoundingClientRect().top}px`;
}, [euiTheme]);
const { headerHeight } = useKibanaHeader();
return (
<EuiFlexGrid
gutterSize="none"
css={css`
position: sticky;
top: ${top};
z-index: ${euiTheme.levels.header};
top: ${headerHeight}px;
z-index: ${euiTheme.levels.navigation};
background: ${euiTheme.colors.emptyShade};
padding-top: ${euiTheme.size.m};
margin-top: -${euiTheme.size.l};
padding: ${euiTheme.size.m} ${euiTheme.size.l} 0px;
margin: -${euiTheme.size.l} -${euiTheme.size.l} 0px;
`}
{...props}
/>

View file

@ -0,0 +1,93 @@
/*
* 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, useRef, useCallback } from 'react';
import { EuiPopover, EuiIcon, EuiFlexGroup, useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { useBoolean } from '../../../../../hooks/use_boolean';
import { useKibanaHeader } from '../../../../../hooks/use_kibana_header';
import { TooltipContent } from '../metric_explanation/tooltip_content';
interface Props {
label: string;
toolTip?: string;
formula?: string;
popoverContainerRef?: React.RefObject<HTMLDivElement>;
}
const SEARCH_BAR_OFFSET = 250;
const ANCHOR_SPACING = 10;
export const ColumnHeader = React.memo(
({ label, toolTip, formula, popoverContainerRef }: Props) => {
const containerRef = useRef<HTMLDivElement>(null);
const [offset, setOffset] = useState(0);
const [isPopoverOpen, { off: closePopover, toggle: togglePopover }] = useBoolean(false);
const { euiTheme } = useEuiTheme();
const { headerHeight } = useKibanaHeader();
const onButtonClick = useCallback(
(e: React.MouseEvent<SVGElement>) => {
e.preventDefault();
e.stopPropagation();
const scrollPosition =
(containerRef.current?.getBoundingClientRect().y ?? 0) - SEARCH_BAR_OFFSET - headerHeight;
setOffset(headerHeight * (scrollPosition <= 0 ? -1 : 1) + ANCHOR_SPACING);
togglePopover();
},
[headerHeight, togglePopover]
);
return (
<EuiFlexGroup gutterSize="xs" ref={containerRef}>
<div
css={css`
overflow-wrap: break-word !important;
word-break: break-word;
min-width: 0;
text-overflow: ellipsis;
overflow: hidden;
`}
>
{label}
</div>
{toolTip && (
<EuiPopover
display=""
panelPaddingSize="s"
button={
<EuiIcon
data-test-subj="hostsViewTableColumnPopoverButton"
type="questionInCircle"
onClick={onButtonClick}
/>
}
insert={
popoverContainerRef && popoverContainerRef?.current
? {
sibling: popoverContainerRef.current,
position: 'after',
}
: undefined
}
offset={offset}
anchorPosition={offset <= 0 ? 'downCenter' : 'upCenter'}
isOpen={isPopoverOpen}
closePopover={closePopover}
zIndex={Number(euiTheme.levels.header) - 1}
panelStyle={{ maxWidth: 350 }}
>
<TooltipContent formula={formula} description={toolTip} showDocumentationLink />
</EuiPopover>
)}
</EuiFlexGroup>
);
}
);

View file

@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiToolTip, IconType } fro
import { TimeRange } from '@kbn/es-query';
import { useLinkProps } from '@kbn/observability-shared-plugin/public';
import { encode } from '@kbn/rison';
import type { CloudProvider, HostNodeRow } from '../hooks/use_hosts_table';
import type { CloudProvider, HostNodeRow } from '../../hooks/use_hosts_table';
const cloudIcons: Record<CloudProvider, IconType> = {
gcp: 'logoGCP',
@ -18,13 +18,13 @@ const cloudIcons: Record<CloudProvider, IconType> = {
unknownProvider: 'cloudSunny',
};
interface HostsTableEntryTitleProps {
interface EntryTitleProps {
onClick: () => void;
time: TimeRange;
title: HostNodeRow['title'];
}
export const HostsTableEntryTitle = ({ onClick, time, title }: HostsTableEntryTitleProps) => {
export const EntryTitle = ({ onClick, time, title }: EntryTitleProps) => {
const { name, cloudProvider } = title;
const link = useLinkProps({
@ -53,7 +53,7 @@ export const HostsTableEntryTitle = ({ onClick, time, title }: HostsTableEntryTi
<EuiFlexItem grow={false} className="eui-textTruncate" onClick={onClick}>
<EuiToolTip delay="long" content={name}>
<EuiLink
data-test-subj="infraHostsTableEntryTitleLink"
data-test-subj="hostsViewTableEntryTitleLink"
className="eui-displayBlock eui-textTruncate"
{...link}
>

View file

@ -8,7 +8,9 @@ import React from 'react';
import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiSpacer } from '@elastic/eui';
import { MetricChart, MetricChartProps } from './metric_chart';
import { HostMetricsDocsLink } from '../../metric_explanation/host_metrics_docs_link';
const DEFAULT_BREAKDOWN_SIZE = 20;
const CHARTS_IN_ORDER: Array<Pick<MetricChartProps, 'title' | 'type'> & { fullRow?: boolean }> = [
@ -88,12 +90,16 @@ const CHARTS_IN_ORDER: Array<Pick<MetricChartProps, 'title' | 'type'> & { fullRo
export const MetricsGrid = React.memo(() => {
return (
<EuiFlexGrid columns={2} gutterSize="s" data-test-subj="hostsView-metricChart">
{CHARTS_IN_ORDER.map(({ fullRow, ...chartProp }) => (
<EuiFlexItem key={chartProp.type} style={fullRow ? { gridColumn: '1/-1' } : {}}>
<MetricChart breakdownSize={DEFAULT_BREAKDOWN_SIZE} {...chartProp} />
</EuiFlexItem>
))}
</EuiFlexGrid>
<>
<HostMetricsDocsLink />
<EuiSpacer size="s" />
<EuiFlexGrid columns={2} gutterSize="s" data-test-subj="hostsView-metricChart">
{CHARTS_IN_ORDER.map(({ fullRow, ...chartProp }) => (
<EuiFlexItem key={chartProp.type} style={fullRow ? { gridColumn: '1/-1' } : {}}>
<MetricChart breakdownSize={DEFAULT_BREAKDOWN_SIZE} {...chartProp} />
</EuiFlexItem>
))}
</EuiFlexGrid>
</>
);
});

View file

@ -59,3 +59,4 @@ export const ALERT_STATUS_QUERY = {
};
export const HOST_LIMIT_OPTIONS = [10, 20, 50, 100, 500] as const;
export const HOST_METRICS_DOC_HREF = 'https://ela.st/docs-infra-host-metrics';

View file

@ -6,15 +6,14 @@
*/
import React, { useCallback, useMemo } from 'react';
import { EuiBasicTableColumn } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import createContainer from 'constate';
import { EuiBasicTableColumn, CriteriaWithPagination } from '@elastic/eui';
import { isEqual } from 'lodash';
import { CriteriaWithPagination } from '@elastic/eui';
import { isNumber } from 'lodash/fp';
import createContainer from 'constate';
import { hostLensFormulas } from '../../../../common/visualizations';
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter';
import { HostsTableEntryTitle } from '../components/hosts_table_entry_title';
import { EntryTitle } from '../components/table/entry_title';
import {
InfraAssetMetadataType,
InfraAssetMetricsItem,
@ -24,6 +23,8 @@ import { useHostFlyoutUrlState } from './use_host_flyout_url_state';
import { Sorting, useHostsTableUrlState } from './use_hosts_table_url_state';
import { useHostsViewContext } from './use_hosts_view';
import { useUnifiedSearchContext } from './use_unified_search';
import { ColumnHeader } from '../components/table/column_header';
import { TOOLTIP, TABLE_COLUMN_LABEL } from '../translations';
/**
* Columns and items types
@ -112,50 +113,6 @@ const sortTableData =
/**
* Columns translations
*/
const titleLabel = i18n.translate('xpack.infra.hostsViewPage.table.nameColumnHeader', {
defaultMessage: 'Name',
});
const cpuUsageLabel = i18n.translate('xpack.infra.hostsViewPage.table.cpuUsageColumnHeader', {
defaultMessage: 'CPU usage (avg.)',
});
const diskSpaceUsageLabel = i18n.translate(
'xpack.infra.hostsViewPage.table.diskSpaceUsageColumnHeader',
{
defaultMessage: 'Disk Space Usage (avg.)',
}
);
const txLabel = i18n.translate('xpack.infra.hostsViewPage.table.txColumnHeader', {
defaultMessage: 'TX (avg.)',
});
const rxLabel = i18n.translate('xpack.infra.hostsViewPage.table.rxColumnHeader', {
defaultMessage: 'RX (avg.)',
});
const memoryFreeLabel = i18n.translate('xpack.infra.hostsViewPage.table.memoryFreeColumnHeader', {
defaultMessage: 'Memory Free (avg.)',
});
const memoryUsageLabel = i18n.translate('xpack.infra.hostsViewPage.table.memoryUsageColumnHeader', {
defaultMessage: 'Memory Usage (avg.)',
});
const normalizedLoad1mLabel = i18n.translate(
'xpack.infra.hostsViewPage.table.normalizedLoad1mColumnHeader',
{
defaultMessage: 'Normalized Load (avg.)',
}
);
const toggleDialogActionLabel = i18n.translate(
'xpack.infra.hostsViewPage.table.toggleDialogWithDetails',
{
defaultMessage: 'Toggle dialog with details',
}
);
/**
* Build a table columns and items starting from the snapshot nodes.
@ -167,11 +124,10 @@ export const useHostsTable = () => {
const {
services: { telemetry },
} = useKibanaContextForPlugin();
const [hostFlyoutState, setHostFlyoutState] = useHostFlyoutUrlState();
const popoverContainerRef = React.createRef<HTMLDivElement>();
const closeFlyout = useCallback(() => setHostFlyoutState(null), [setHostFlyoutState]);
const reportHostEntryClick = useCallback(
({ name, cloudProvider }: HostNodeRow['title']) => {
telemetry.reportHostEntryClicked({
@ -222,8 +178,8 @@ export const useHostsTable = () => {
field: 'id',
actions: [
{
name: toggleDialogActionLabel,
description: toggleDialogActionLabel,
name: TABLE_COLUMN_LABEL.toggleDialogAction,
description: TABLE_COLUMN_LABEL.toggleDialogAction,
icon: ({ id }) =>
hostFlyoutState?.clickedItemId && id === hostFlyoutState?.clickedItemId
? 'minimize'
@ -244,13 +200,13 @@ export const useHostsTable = () => {
],
},
{
name: titleLabel,
name: TABLE_COLUMN_LABEL.title,
field: 'title',
sortable: true,
truncateText: true,
'data-test-subj': 'hostsView-tableRow-title',
render: (title: HostNodeRow['title']) => (
<HostsTableEntryTitle
<EntryTitle
title={title}
time={searchCriteria.dateRange}
onClick={() => reportHostEntryClick(title)}
@ -259,7 +215,14 @@ export const useHostsTable = () => {
width: '20%',
},
{
name: cpuUsageLabel,
name: (
<ColumnHeader
label={TABLE_COLUMN_LABEL.cpuUsage}
toolTip={TOOLTIP.cpuUsage}
formula={hostLensFormulas.cpuUsage.formula.formula}
popoverContainerRef={popoverContainerRef}
/>
),
field: 'cpu',
sortable: true,
'data-test-subj': 'hostsView-tableRow-cpuUsage',
@ -267,7 +230,14 @@ export const useHostsTable = () => {
align: 'right',
},
{
name: normalizedLoad1mLabel,
name: (
<ColumnHeader
label={TABLE_COLUMN_LABEL.normalizedLoad1m}
toolTip={TOOLTIP.normalizedLoad1m}
formula={hostLensFormulas.normalizedLoad1m.formula.formula}
popoverContainerRef={popoverContainerRef}
/>
),
field: 'normalizedLoad1m',
sortable: true,
'data-test-subj': 'hostsView-tableRow-normalizedLoad1m',
@ -275,15 +245,29 @@ export const useHostsTable = () => {
align: 'right',
},
{
name: memoryUsageLabel,
name: (
<ColumnHeader
label={TABLE_COLUMN_LABEL.memoryUsage}
toolTip={TOOLTIP.memoryUsage}
formula={hostLensFormulas.memoryUsage.formula.formula}
popoverContainerRef={popoverContainerRef}
/>
),
field: 'memory',
sortable: true,
'data-test-subj': 'hostsView-tableRow-memoryTotal',
'data-test-subj': 'hostsView-tableRow-memoryUsage',
render: (avg: number) => formatMetric('memory', avg),
align: 'right',
},
{
name: memoryFreeLabel,
name: (
<ColumnHeader
label={TABLE_COLUMN_LABEL.memoryFree}
toolTip={TOOLTIP.memoryFree}
formula={hostLensFormulas.memoryFree.formula.formula}
popoverContainerRef={popoverContainerRef}
/>
),
field: 'memoryFree',
sortable: true,
'data-test-subj': 'hostsView-tableRow-memoryFree',
@ -291,7 +275,14 @@ export const useHostsTable = () => {
align: 'right',
},
{
name: diskSpaceUsageLabel,
name: (
<ColumnHeader
label={TABLE_COLUMN_LABEL.diskSpaceUsage}
toolTip={TOOLTIP.diskSpaceUsage}
formula={hostLensFormulas.diskSpaceUsage.formula.formula}
popoverContainerRef={popoverContainerRef}
/>
),
field: 'diskSpaceUsage',
sortable: true,
'data-test-subj': 'hostsView-tableRow-diskSpaceUsage',
@ -299,7 +290,14 @@ export const useHostsTable = () => {
align: 'right',
},
{
name: rxLabel,
name: (
<ColumnHeader
label={TABLE_COLUMN_LABEL.rx}
toolTip={TOOLTIP.rx}
formula={hostLensFormulas.rx.formula.formula}
popoverContainerRef={popoverContainerRef}
/>
),
field: 'rx',
sortable: true,
'data-test-subj': 'hostsView-tableRow-rx',
@ -308,7 +306,14 @@ export const useHostsTable = () => {
width: '120px',
},
{
name: txLabel,
name: (
<ColumnHeader
label={TABLE_COLUMN_LABEL.tx}
toolTip={TOOLTIP.tx}
formula={hostLensFormulas.tx.formula.formula}
popoverContainerRef={popoverContainerRef}
/>
),
field: 'tx',
sortable: true,
'data-test-subj': 'hostsView-tableRow-tx',
@ -322,6 +327,7 @@ export const useHostsTable = () => {
reportHostEntryClick,
searchCriteria.dateRange,
setHostFlyoutState,
popoverContainerRef,
]
);
@ -335,6 +341,9 @@ export const useHostsTable = () => {
onTableChange,
pagination,
sorting,
refs: {
popoverContainerRef,
},
};
};

View file

@ -21,7 +21,6 @@ import { MetricsPageTemplate } from '../page_template';
import { hostsTitle } from '../../../translations';
import { MetricsDataViewProvider } from './hooks/use_data_view';
import { fullHeightContentStyles } from '../../../page_template.styles';
import { UnifiedSearchProvider } from './hooks/use_unified_search';
import { HostContainer } from './components/hosts_container';
import { BetaBadge } from '../../../components/beta_badge';
import { NoRemoteCluster } from '../../../components/empty_states';
@ -118,9 +117,7 @@ export const HostsPage = () => {
>
{source && (
<MetricsDataViewProvider metricAlias={source.configuration.metricAlias}>
<UnifiedSearchProvider>
<HostContainer />
</UnifiedSearchProvider>
<HostContainer />
</MetricsDataViewProvider>
)}
</MetricsPageTemplate>

View file

@ -0,0 +1,83 @@
/*
* 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 { i18n } from '@kbn/i18n';
export const TOOLTIP = {
hostCount: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.hostCount', {
defaultMessage: 'Number of hosts returned by your search criteria.',
}),
cpuUsage: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.cpuUsage', {
defaultMessage:
'Percentage of CPU time spent in states other than Idle and IOWait, normalized by the number of CPU cores. This includes both time spent on user space and kernel space.',
}),
diskSpaceUsage: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.diskSpaceUsage', {
defaultMessage: 'Percentage of disk space used.',
}),
diskLatency: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.diskLatency', {
defaultMessage: 'Time spent to service disk requests.',
}),
memoryFree: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.memoryFree', {
defaultMessage: 'Total available memory including page cache.',
}),
memoryTotal: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.memoryTotal', {
defaultMessage: 'Total memory capacity.',
}),
memoryUsage: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.memoryUsage', {
defaultMessage: 'Percentage of main memory usage excluding page cache.',
}),
normalizedLoad1m: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.normalizedLoad1m', {
defaultMessage: '1 minute load average normalized by the number of CPU cores. ',
}),
rx: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.rx', {
defaultMessage:
'Number of bytes which have been received per second on the public interfaces of the hosts.',
}),
tx: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.tx', {
defaultMessage:
'Number of bytes which have been sent per second on the public interfaces of the hosts.',
}),
};
export const TABLE_COLUMN_LABEL = {
title: i18n.translate('xpack.infra.hostsViewPage.table.nameColumnHeader', {
defaultMessage: 'Name',
}),
cpuUsage: i18n.translate('xpack.infra.hostsViewPage.table.cpuUsageColumnHeader', {
defaultMessage: 'CPU usage (avg.)',
}),
diskSpaceUsage: i18n.translate('xpack.infra.hostsViewPage.table.diskSpaceUsageColumnHeader', {
defaultMessage: 'Disk Space Usage (avg.)',
}),
tx: i18n.translate('xpack.infra.hostsViewPage.table.txColumnHeader', {
defaultMessage: 'TX (avg.)',
}),
rx: i18n.translate('xpack.infra.hostsViewPage.table.rxColumnHeader', {
defaultMessage: 'RX (avg.)',
}),
memoryFree: i18n.translate('xpack.infra.hostsViewPage.table.memoryFreeColumnHeader', {
defaultMessage: 'Memory Free (avg.)',
}),
memoryUsage: i18n.translate('xpack.infra.hostsViewPage.table.memoryUsageColumnHeader', {
defaultMessage: 'Memory Usage (avg.)',
}),
normalizedLoad1m: i18n.translate('xpack.infra.hostsViewPage.table.normalizedLoad1mColumnHeader', {
defaultMessage: 'Normalized Load (avg.)',
}),
toggleDialogAction: i18n.translate('xpack.infra.hostsViewPage.table.toggleDialogWithDetails', {
defaultMessage: 'Toggle dialog with details',
}),
};

View file

@ -18454,10 +18454,10 @@
"xpack.infra.hostsViewPage.landing.introTitle": "Présentation : Analyse de l'hôte",
"xpack.infra.hostsViewPage.landing.learnMore": "En savoir plus",
"xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "Il s'agit d'une version préliminaire de la fonctionnalité, et nous souhaiterions vivement connaître votre avis tandis que nous continuons\n à la développer et à l'améliorer. Pour accéder à cette fonctionnalité, il suffit de l'activer ci-dessous. Ne passez pas à côté\n de cette nouvelle et puissante fonctionnalité ajoutée à notre plateforme... Essayez-la aujourd'hui même !",
"xpack.infra.hostsViewPage.metrics.tooltip.cpuUsage": "Moyenne de pourcentage de temps CPU utilisé dans les états autres que Inactif et IOWait, normalisée par le nombre de cœurs de processeur. Inclut le temps passé à la fois sur l'espace utilisateur et sur l'espace du noyau. 100 % signifie que tous les processeurs de l'hôte sont occupés.",
"xpack.infra.hostsViewPage.metrics.tooltip.hostCount": "Nombre d'hôtes renvoyé par vos critères de recherche actuels.",
"xpack.infra.hostsViewPage.metricTrend.cpuUsage.title": "Utilisation CPU",
"xpack.infra.hostsViewPage.metricTrend.cpuUsage.tooltip": "Moyenne de pourcentage de temps CPU utilisé dans les états autres que Inactif et IOWait, normalisée par le nombre de cœurs de processeur. Inclut le temps passé à la fois sur l'espace utilisateur et sur l'espace du noyau. 100 % signifie que tous les processeurs de l'hôte sont occupés.",
"xpack.infra.hostsViewPage.metricTrend.hostCount.title": "Hôtes",
"xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "Nombre d'hôtes renvoyé par vos critères de recherche actuels.",
"xpack.infra.hostsViewPage.metricTrend.memoryUsage.title": "Utilisation mémoire",
"xpack.infra.hostsViewPage.metricTrend.normalizedLoad1m.title": "Charge normalisée",
"xpack.infra.hostsViewPage.metricTrend.subtitle.average": "Moyenne",
@ -39575,4 +39575,4 @@
"xpack.painlessLab.title": "Painless Lab",
"xpack.painlessLab.walkthroughButtonLabel": "Présentation"
}
}
}

View file

@ -18453,10 +18453,10 @@
"xpack.infra.hostsViewPage.landing.introTitle": "導入:ホスト分析",
"xpack.infra.hostsViewPage.landing.learnMore": "詳細",
"xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "この機能は初期バージョンであり、今後継続する中で、開発、改善するうえで皆様からのフィードバックをお願いします\n 。機能にアクセスするには、以下を有効にします。プラットフォームに\n 追加されたこの強力な新機能をお見逃しなく。今すぐお試しください!",
"xpack.infra.hostsViewPage.metrics.tooltip.hostCount": "現在の検索条件から返されたホストの数です。",
"xpack.infra.hostsViewPage.metrics.tooltip.cpuUsage": "アイドルおよびIOWait以外の状態で費やされたCPU時間の割合の平均値を、CPUコア数で正規化したもの。ユーザースペースとカーネルスペースの両方で費やされた時間が含まれます。100はホストのすべてのCPUがビジー状態であることを示します。",
"xpack.infra.hostsViewPage.metricTrend.cpuUsage.title": "CPU 使用状況",
"xpack.infra.hostsViewPage.metricTrend.cpuUsage.tooltip": "アイドルおよびIOWait以外の状態で費やされたCPU時間の割合の平均値を、CPUコア数で正規化したもの。ユーザースペースとカーネルスペースの両方で費やされた時間が含まれます。100はホストのすべてのCPUがビジー状態であることを示します。",
"xpack.infra.hostsViewPage.metricTrend.hostCount.title": "ホスト",
"xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "現在の検索条件から返されたホストの数です。",
"xpack.infra.hostsViewPage.metricTrend.memoryUsage.title": "メモリー使用状況",
"xpack.infra.hostsViewPage.metricTrend.normalizedLoad1m.title": "正規化された負荷",
"xpack.infra.hostsViewPage.metricTrend.subtitle.average": "平均",
@ -39545,4 +39545,4 @@
"xpack.painlessLab.title": "Painless Lab",
"xpack.painlessLab.walkthroughButtonLabel": "実地検証"
}
}
}

View file

@ -18453,10 +18453,10 @@
"xpack.infra.hostsViewPage.landing.introTitle": "即将引入:主机分析",
"xpack.infra.hostsViewPage.landing.learnMore": "了解详情",
"xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "这是早期版本的功能,我们需要您的反馈,\n 以便继续开发和改进该功能。要访问该功能,直接在下面启用即可。请抓紧时间,\n 了解新添加到我们平台中的这项强大功能 - 立即试用!",
"xpack.infra.hostsViewPage.metrics.tooltip.cpuUsage": "CPU 在空闲和 IOWait 状态以外所花费时间的平均百分比,按 CPU 核心数进行标准化。包括在用户空间和内核空间上花费的时间。100% 表示主机的所有 CPU 都处于忙碌状态。",
"xpack.infra.hostsViewPage.metrics.tooltip.hostCount": "当前搜索条件返回的主机数。",
"xpack.infra.hostsViewPage.metricTrend.cpuUsage.title": "CPU 使用",
"xpack.infra.hostsViewPage.metricTrend.cpuUsage.tooltip": "CPU 在空闲和 IOWait 状态以外所花费时间的平均百分比,按 CPU 核心数进行标准化。包括在用户空间和内核空间上花费的时间。100% 表示主机的所有 CPU 都处于忙碌状态。",
"xpack.infra.hostsViewPage.metricTrend.hostCount.title": "主机",
"xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "当前搜索条件返回的主机数。",
"xpack.infra.hostsViewPage.metricTrend.memoryUsage.title": "内存使用",
"xpack.infra.hostsViewPage.metricTrend.normalizedLoad1m.title": "正規化された負荷",
"xpack.infra.hostsViewPage.metricTrend.subtitle.average": "平均值",
@ -39539,4 +39539,4 @@
"xpack.painlessLab.title": "Painless 实验室",
"xpack.painlessLab.walkthroughButtonLabel": "指导"
}
}
}