[APM] Fixes service count API and modal scroll (#141725)

* [APM] Fixes multiple parallel search calls for service count to one call with a filter agg (#141242)

* updates the description text when saving a group

* fixes scrolling issue in save group modal

* adds descripion for service count time range in service group list

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Add comment about only aggregating over metrics documents.

* Update x-pack/plugins/apm/server/routes/service_groups/get_services_counts.ts

PR feedback

Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>
This commit is contained in:
Oliver Gupte 2022-09-28 18:26:49 -04:00 committed by GitHub
parent e0b486051a
commit c71ab04eb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 120 additions and 124 deletions

View file

@ -36,7 +36,7 @@ const CentralizedContainer = styled.div`
`;
const MAX_CONTAINER_HEIGHT = 600;
const MODAL_HEADER_HEIGHT = 122;
const MODAL_HEADER_HEIGHT = 180;
const MODAL_FOOTER_HEIGHT = 80;
const suggestedFieldsWhitelist = [
@ -118,10 +118,73 @@ export function SelectServices({
'xpack.apm.serviceGroups.selectServicesForm.subtitle',
{
defaultMessage:
'Use a query to select services for this group. Services that match this query within the last 24 hours will be assigned to the group.',
'Use a query to select services for this group. The preview shows services that match this query within the last 24 hours.',
}
)}
</EuiText>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<KueryBar
placeholder={i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.kql',
{ defaultMessage: 'E.g. labels.team: "web"' }
)}
onSubmit={(value) => {
setKuery(value);
}}
onChange={(value) => {
setStagedKuery(value);
}}
value={kuery}
suggestionFilter={(querySuggestion) => {
if ('field' in querySuggestion) {
const {
field: {
spec: { name: fieldName },
},
} = querySuggestion;
return (
fieldName.startsWith('label') ||
suggestedFieldsWhitelist.includes(fieldName)
);
}
return true;
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
onClick={() => {
setKuery(stagedKuery);
}}
iconType={!kuery ? 'search' : 'refresh'}
isDisabled={isServiceListPreviewLoading || !stagedKuery}
>
{!kuery
? i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.preview',
{ defaultMessage: 'Preview' }
)
: i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.refresh',
{ defaultMessage: 'Refresh' }
)}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
{kuery && data?.items && (
<EuiText color="success" size="s">
{i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.matchingServiceCount',
{
defaultMessage:
'{servicesCount} {servicesCount, plural, =0 {services} one {service} other {services}} match the query',
values: { servicesCount: data?.items.length },
}
)}
</EuiText>
)}
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody
@ -136,73 +199,6 @@ export function SelectServices({
gutterSize="s"
style={{ height: '100%' }}
>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<KueryBar
placeholder={i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.kql',
{ defaultMessage: 'E.g. labels.team: "web"' }
)}
onSubmit={(value) => {
setKuery(value);
}}
onChange={(value) => {
setStagedKuery(value);
}}
value={kuery}
suggestionFilter={(querySuggestion) => {
if ('field' in querySuggestion) {
const {
field: {
spec: { name: fieldName },
},
} = querySuggestion;
return (
fieldName.startsWith('label') ||
suggestedFieldsWhitelist.includes(fieldName)
);
}
return true;
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
onClick={() => {
setKuery(stagedKuery);
}}
iconType={!kuery ? 'search' : 'refresh'}
isDisabled={isServiceListPreviewLoading || !stagedKuery}
>
{!kuery
? i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.preview',
{ defaultMessage: 'Preview' }
)
: i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.refresh',
{ defaultMessage: 'Refresh' }
)}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
{kuery && data?.items && (
<EuiFlexItem grow={false}>
<EuiText color="success" size="s">
{i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.matchingServiceCount',
{
defaultMessage:
'{servicesCount} {servicesCount, plural, =0 {services} one {service} other {services}} match the query',
values: { servicesCount: data?.items.length },
}
)}
</EuiText>
</EuiFlexItem>
)}
<EuiFlexItem>
<EuiPanel hasShadow={false} hasBorder paddingSize="s">
{!kuery && (

View file

@ -37,7 +37,7 @@ type SORT_FIELD = 'serviceName' | 'environments' | 'agentName';
export function ServiceListPreview({ items, isLoading }: Props) {
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(5);
const [pageSize, setPageSize] = useState(10);
const [sortField, setSortField] = useState<SORT_FIELD>(DEFAULT_SORT_FIELD);
const [sortDirection, setSortDirection] = useState<DIRECTION>(
DEFAULT_SORT_DIRECTION

View file

@ -156,6 +156,12 @@ export function ServiceGroupsList() {
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiText color="subdued" size="s">
{i18n.translate('xpack.apm.serviceGroups.listDescription', {
defaultMessage:
'Displayed service counts reflect the last 24 hours.',
})}
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
{items.length ? (

View file

@ -7,50 +7,72 @@
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import { rangeQuery, kqlQuery } from '@kbn/observability-plugin/server';
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import { Setup } from '../../lib/helpers/setup_request';
import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames';
import { SavedServiceGroup } from '../../../common/service_groups';
export async function getServicesCounts({
setup,
kuery,
maxNumberOfServices,
start,
end,
serviceGroups,
}: {
setup: Setup;
kuery: string;
maxNumberOfServices: number;
start: number;
end: number;
serviceGroups: SavedServiceGroup[];
}) {
const { apmEventClient } = setup;
const response = await apmEventClient.search('get_services_count', {
const serviceGroupsKueryMap: Record<string, QueryDslQueryContainer> =
serviceGroups.reduce((acc, sg) => {
return {
...acc,
[sg.id]: kqlQuery(sg.kuery)[0],
};
}, {});
const params = {
apm: {
events: [
ProcessorEvent.metric,
ProcessorEvent.transaction,
ProcessorEvent.span,
ProcessorEvent.error,
],
// We're limiting the service count to only metrics documents. If a user
// actively disables system/app metrics and a service only ingests error
// events, that service will not be included in the service groups count.
// This is an edge case that only effects the count preview label.
events: [ProcessorEvent.metric],
},
body: {
track_total_hits: 0,
size: 0,
query: {
bool: {
filter: [...rangeQuery(start, end), ...kqlQuery(kuery)],
filter: rangeQuery(start, end),
},
},
aggs: {
services_count: {
cardinality: {
field: SERVICE_NAME,
service_groups: {
filters: {
filters: serviceGroupsKueryMap,
},
aggs: {
services_count: {
cardinality: {
field: SERVICE_NAME,
},
},
},
},
},
},
});
};
const response = await apmEventClient.search('get_services_count', params);
return response?.aggregations?.services_count.value ?? 0;
const buckets: Record<string, { services_count: { value: number } }> =
response?.aggregations?.service_groups.buckets ?? {};
return Object.keys(buckets).reduce((acc, key) => {
return {
...acc,
[key]: buckets[key].services_count.value,
};
}, {});
}

View file

@ -7,7 +7,6 @@
import * as t from 'io-ts';
import { apmServiceGroupMaxNumberOfServices } from '@kbn/observability-plugin/common';
import { keyBy, mapValues } from 'lodash';
import { setupRequest } from '../../lib/helpers/setup_request';
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';
import { kueryRt, rangeRt } from '../default_api_types';
@ -52,50 +51,26 @@ const serviceGroupsWithServiceCountRoute = createApmServerRoute({
const { context, params } = resources;
const {
savedObjects: { client: savedObjectsClient },
uiSettings: { client: uiSettingsClient },
} = await context.core;
const {
query: { start, end },
} = params;
const [setup, maxNumberOfServices] = await Promise.all([
setupRequest(resources),
uiSettingsClient.get<number>(apmServiceGroupMaxNumberOfServices),
]);
const setup = await setupRequest(resources);
const serviceGroups = await getServiceGroups({
savedObjectsClient,
});
const serviceGroupsWithServiceCount = await Promise.all(
serviceGroups.map(
async ({
id,
kuery,
}): Promise<{ id: string; servicesCount: number }> => {
const servicesCount = await getServicesCounts({
setup,
kuery,
maxNumberOfServices,
start,
end,
});
return {
id,
servicesCount,
};
}
)
);
const servicesCounts = mapValues(
keyBy(serviceGroupsWithServiceCount, 'id'),
'servicesCount'
);
return { servicesCounts };
return {
servicesCounts: await getServicesCounts({
setup,
serviceGroups,
start,
end,
}),
};
},
});

View file

@ -7542,7 +7542,6 @@
"xpack.apm.serviceGroups.selectServicesForm.preview": "Aperçu",
"xpack.apm.serviceGroups.selectServicesForm.refresh": "Actualiser",
"xpack.apm.serviceGroups.selectServicesForm.saveGroup": "Enregistrer le groupe",
"xpack.apm.serviceGroups.selectServicesForm.subtitle": "Utilisez une requête pour sélectionner les services pour ce groupe. Les services qui correspondent à cette requête dans les dernières 24 heures seront affectés au groupe.",
"xpack.apm.serviceGroups.selectServicesForm.title": "Sélectionner des services",
"xpack.apm.serviceGroups.selectServicesList.environmentColumnLabel": "Environnements",
"xpack.apm.serviceGroups.selectServicesList.nameColumnLabel": "Nom",

View file

@ -7529,7 +7529,6 @@
"xpack.apm.serviceGroups.selectServicesForm.preview": "プレビュー",
"xpack.apm.serviceGroups.selectServicesForm.refresh": "更新",
"xpack.apm.serviceGroups.selectServicesForm.saveGroup": "グループを保存",
"xpack.apm.serviceGroups.selectServicesForm.subtitle": "クエリを使用してこのグループのサービスを選択します。過去24時間以内にこのクエリと一致したサービスはグループに割り当てられます。",
"xpack.apm.serviceGroups.selectServicesForm.title": "サービスを選択",
"xpack.apm.serviceGroups.selectServicesList.environmentColumnLabel": "環境",
"xpack.apm.serviceGroups.selectServicesList.nameColumnLabel": "名前",

View file

@ -7546,7 +7546,6 @@
"xpack.apm.serviceGroups.selectServicesForm.preview": "预览",
"xpack.apm.serviceGroups.selectServicesForm.refresh": "刷新",
"xpack.apm.serviceGroups.selectServicesForm.saveGroup": "保存组",
"xpack.apm.serviceGroups.selectServicesForm.subtitle": "使用查询为该组选择服务。过去 24 小时内与此查询匹配的服务将分配给该组。",
"xpack.apm.serviceGroups.selectServicesForm.title": "选择服务",
"xpack.apm.serviceGroups.selectServicesList.environmentColumnLabel": "环境",
"xpack.apm.serviceGroups.selectServicesList.nameColumnLabel": "名称",