Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Infra] Limit the number of metrics accepted by Metrics Explorer API #188112

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common/parse_te
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
import {
fifteenMinutesInMilliseconds,
HOST_FIELD,
HOST_NAME_FIELD,
LINK_TO_INVENTORY,
METRICS_EXPLORER_URL,
} from '../../constants';
Expand Down Expand Up @@ -54,7 +54,7 @@ export const getInventoryViewInAppUrl = (

const nodeTypeField = `${ALERT_RULE_PARAMETERS}.nodeType`;
const nodeType = inventoryFields[nodeTypeField] as InventoryItemType;
const hostName = inventoryFields[HOST_FIELD];
const hostName = inventoryFields[HOST_NAME_FIELD];

if (nodeType) {
if (hostName) {
Expand Down Expand Up @@ -95,7 +95,7 @@ export const getInventoryViewInAppUrl = (
};

export const getMetricsViewInAppUrl = (fields: ParsedTechnicalFields & Record<string, any>) => {
const hostName = fields[HOST_FIELD];
const hostName = fields[HOST_NAME_FIELD];
const timestamp = fields[TIMESTAMP];

return hostName ? getLinkToHostDetails({ hostName, timestamp }) : METRICS_EXPLORER_URL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ export const LOGS_FEATURE_ID = 'logs';
export type InfraFeatureId = typeof METRICS_FEATURE_ID | typeof LOGS_FEATURE_ID;

export const TIMESTAMP_FIELD = '@timestamp';
export const MESSAGE_FIELD = 'message';
export const TIEBREAKER_FIELD = '_doc';
export const HOST_FIELD = 'host.name';
export const CONTAINER_FIELD = 'container.id';
export const POD_FIELD = 'kubernetes.pod.uid';
export const CMDLINE_FIELD = 'system.process.cmdline';

// system
export const HOST_NAME_FIELD = 'host.name';
export const CONTAINER_ID_FIELD = 'container.id';
export const KUBERNETES_POD_UID_FIELD = 'kubernetes.pod.uid';
export const SYSTEM_PROCESS_CMDLINE_FIELD = 'system.process.cmdline';

// logs
export const MESSAGE_FIELD = 'message';

export const O11Y_AAD_FIELDS = [
'cloud.*',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
*/

import { useState } from 'react';
import { HOST_FIELD } from '../../../../common/constants';
import { HOST_NAME_FIELD } from '../../../../common/constants';
import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props';
import { useAssetDetailsUrlState } from './use_asset_details_url_state';

function buildFullProfilingKuery(assetName: string, profilingSearch?: string) {
const defaultKuery = `${HOST_FIELD} : "${assetName}"`;
const defaultKuery = `${HOST_NAME_FIELD} : "${assetName}"`;
const customKuery = profilingSearch?.trim() ?? '';

return customKuery !== '' ? `${defaultKuery} and ${customKuery}` : defaultKuery;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useLinkProps } from '@kbn/observability-shared-plugin/public';
import { Section } from '../../components/section';
import { ServicesSectionTitle } from './section_titles';
import { useServices } from '../../hooks/use_services';
import { HOST_FIELD } from '../../../../../common/constants';
import { HOST_NAME_FIELD } from '../../../../../common/constants';
import { LinkToApmServices } from '../../links';
import { APM_HOST_FILTER_FIELD } from '../../constants';
import { LinkToApmService } from '../../links/link_to_apm_service';
Expand All @@ -37,7 +37,7 @@ export const ServicesContent = ({
});
const params = useMemo(
() => ({
filters: { [HOST_FIELD]: hostName },
filters: { [HOST_NAME_FIELD]: hostName },
from: dateRange.from,
to: dateRange.to,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
import { FlamegraphLocator } from '@kbn/observability-shared-plugin/public/locators/profiling/flamegraph_locator';
import { TopNFunctionsLocator } from '@kbn/observability-shared-plugin/public/locators/profiling/topn_functions_locator';
import { StacktracesLocator } from '@kbn/observability-shared-plugin/public/locators/profiling/stacktraces_locator';
import { HOST_FIELD } from '../../../../../common/constants';
import { HOST_NAME_FIELD } from '../../../../../common/constants';

const PROFILING_FEEDBACK_URL = 'https://ela.st/profiling-feedback';

Expand All @@ -31,7 +31,7 @@ export function ProfilingLinks({
profilingLinkLabel,
}: Props) {
const profilingLinkURL = profilingLinkLocator.getRedirectUrl({
kuery: `${HOST_FIELD}:"${hostname}"`,
kuery: `${HOST_NAME_FIELD}:"${hostname}"`,
rangeFrom: from,
rangeTo: to,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ import {
} from '../hooks/use_metrics_explorer_options';
import { createTSVBLink, TSVB_WORKAROUND_INDEX_PATTERN } from './helpers/create_tsvb_link';
import { useNodeDetailsRedirect } from '../../../link_to';
import { HOST_FIELD, POD_FIELD, CONTAINER_FIELD } from '../../../../../common/constants';
import {
HOST_NAME_FIELD,
KUBERNETES_POD_UID_FIELD,
CONTAINER_ID_FIELD,
} from '../../../../../common/constants';

export interface Props {
options: MetricsExplorerOptions;
Expand All @@ -41,13 +45,13 @@ export interface Props {

const fieldToNodeType = (groupBy: string | string[]): InventoryItemType | undefined => {
const fields = Array.isArray(groupBy) ? groupBy : [groupBy];
if (fields.includes(HOST_FIELD)) {
if (fields.includes(HOST_NAME_FIELD)) {
return 'host';
}
if (fields.includes(POD_FIELD)) {
if (fields.includes(KUBERNETES_POD_UID_FIELD)) {
return 'pod';
}
if (fields.includes(CONTAINER_FIELD)) {
if (fields.includes(CONTAINER_ID_FIELD)) {
return 'container';
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@
* 2.0.
*/

import { EuiComboBox } from '@elastic/eui';
import {
EuiComboBox,
EuiFlexGroup,
EuiFlexItem,
EuiText,
EuiIcon,
EuiComboBoxOptionOption,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useState } from 'react';
import React, { useCallback, useState, useMemo } from 'react';
import { METRICS_EXPLORER_API_MAX_METRICS } from '@kbn/metrics-data-access-plugin/common';
import { useMetricsDataViewContext } from '../../../../containers/metrics_source';
import { colorTransformer, Color } from '../../../../../common/color_palette';
import { MetricsExplorerMetric } from '../../../../../common/http_api/metrics_explorer';
Expand All @@ -24,11 +32,25 @@ interface SelectedOption {
label: string;
}

const placeholderText = i18n.translate('xpack.infra.metricsExplorer.metricComboBoxPlaceholder', {
defaultMessage: 'choose a metric to plot',
});

const comboValidationText = i18n.translate('xpack.infra.metricsExplorer.maxItemsSelected', {
defaultMessage: 'Maximum number of {maxMetrics} metrics reached.',
values: { maxMetrics: METRICS_EXPLORER_API_MAX_METRICS },
});

export const MetricsExplorerMetrics = ({ options, onChange, autoFocus = false }: Props) => {
const { metricsView } = useMetricsDataViewContext();
const colors = Object.keys(Color) as Array<keyof typeof Color>;
const [shouldFocus, setShouldFocus] = useState(autoFocus);

const maxMetricsReached = useMemo(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this useMemo needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's not needed.

() => options.metrics.length >= METRICS_EXPLORER_API_MAX_METRICS,
[options.metrics.length]
);

// the EuiCombobox forwards the ref to an input element
const autoFocusInputElement = useCallback(
(inputElement: HTMLInputElement | null) => {
Expand All @@ -53,10 +75,17 @@ export const MetricsExplorerMetrics = ({ options, onChange, autoFocus = false }:
[onChange, options.aggregation, colors]
);

const comboOptions = (metricsView?.fields ?? []).map((field) => ({
label: field.name,
value: field.name,
}));
const comboOptions = useMemo(
(): EuiComboBoxOptionOption[] =>
maxMetricsReached
? [{ label: comboValidationText, disabled: true }]
: (metricsView?.fields ?? []).map((field) => ({
label: field.name,
value: field.name,
})),
[maxMetricsReached, metricsView?.fields]
);

const selectedOptions = options.metrics
.filter((m) => m.aggregation !== 'count')
.map((metric) => ({
Expand All @@ -65,9 +94,37 @@ export const MetricsExplorerMetrics = ({ options, onChange, autoFocus = false }:
color: colorTransformer(metric.color || Color.color0),
}));

const placeholderText = i18n.translate('xpack.infra.metricsExplorer.metricComboBoxPlaceholder', {
defaultMessage: 'choose a metric to plot',
});
const handleOnKeyDown = (ev: React.KeyboardEvent<HTMLDivElement>) => {
if (maxMetricsReached) {
ev.preventDefault();
}

return ev;
};

const renderFields = useCallback((option: EuiComboBoxOptionOption) => {
const { label, disabled } = option;

if (disabled) {
return (
<EuiFlexGroup
direction="column"
justifyContent="center"
alignItems="center"
data-test-subj="infraMetricsExplorerMaxMetricsReached"
>
<EuiFlexItem>
<EuiFlexGroup gutterSize="xs" justifyContent="center" alignItems="center">
<EuiIcon type="iInCircle" size="s" />
<EuiText size="xs">{label}</EuiText>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
}

return label;
}, []);

return (
<EuiComboBox
Expand All @@ -79,8 +136,10 @@ export const MetricsExplorerMetrics = ({ options, onChange, autoFocus = false }:
options={comboOptions}
selectedOptions={selectedOptions}
onChange={handleChange}
isClearable={true}
onKeyDown={handleOnKeyDown}
isClearable
inputRef={autoFocusInputElement}
renderOption={renderFields}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { useInfiniteQuery } from '@tanstack/react-query';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { InfraHttpError } from '../../../../types';
import { useMetricsDataViewContext } from '../../../../containers/metrics_source';
import {
MetricsExplorerResponse,
Expand All @@ -30,7 +31,7 @@ export function useMetricsExplorerData({

const { isLoading, data, error, refetch, fetchNextPage } = useInfiniteQuery<
MetricsExplorerResponse,
Error
InfraHttpError
>({
queryKey: ['metricExplorer', options, fromTimestamp, toTimestamp],
queryFn: async ({ signal, pageParam = { afterKey: null } }) => {
Expand Down Expand Up @@ -77,11 +78,12 @@ export function useMetricsExplorerData({
getNextPageParam: (lastPage) => lastPage.pageInfo,
enabled: enabled && !!fromTimestamp && !!toTimestamp && !!http && !!metricsView,
refetchOnWindowFocus: false,
retry: false,
});

return {
data,
error,
error: error?.body || error,
fetchNextPage,
isLoading,
refetch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { TIMESTAMP_FIELD, CMDLINE_FIELD } from '../../../common/constants';
import { TIMESTAMP_FIELD, SYSTEM_PROCESS_CMDLINE_FIELD } from '../../../common/constants';
import { ProcessListAPIRequest, ProcessListAPIQueryAggregation } from '../../../common/http_api';
import { ESSearchClient } from '../metrics/types';
import type { InfraSourceConfiguration } from '../sources';
Expand Down Expand Up @@ -69,7 +69,7 @@ export const getProcessList = async (
aggs: {
filteredProcs: {
terms: {
field: CMDLINE_FIELD,
field: SYSTEM_PROCESS_CMDLINE_FIELD,
size: TOP_N,
order: {
[sortBy.name]: sortBy.isAscending ? 'asc' : 'desc',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { first } from 'lodash';
import { TIMESTAMP_FIELD, CMDLINE_FIELD } from '../../../common/constants';
import { TIMESTAMP_FIELD, SYSTEM_PROCESS_CMDLINE_FIELD } from '../../../common/constants';
import {
ProcessListAPIChartRequest,
ProcessListAPIChartQueryAggregation,
Expand Down Expand Up @@ -48,7 +48,7 @@ export const getProcessListChart = async (
must: [
{
match: {
[CMDLINE_FIELD]: command,
[SYSTEM_PROCESS_CMDLINE_FIELD]: command,
},
},
],
Expand All @@ -57,7 +57,7 @@ export const getProcessListChart = async (
aggs: {
filteredProc: {
terms: {
field: CMDLINE_FIELD,
field: SYSTEM_PROCESS_CMDLINE_FIELD,
size: 1,
},
aggs: {
Expand Down
Loading