diff --git a/x-pack/plugins/apm/common/utils/environment_query.ts b/x-pack/plugins/apm/common/utils/environment_query.ts index e2f9a722e3de2..bc02e4cd2518b 100644 --- a/x-pack/plugins/apm/common/utils/environment_query.ts +++ b/x-pack/plugins/apm/common/utils/environment_query.ts @@ -6,11 +6,15 @@ */ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { SERVICE_ENVIRONMENT } from '../elasticsearch_fieldnames'; +import { + SERVICE_ENVIRONMENT, + SERVICE_NODE_NAME, +} from '../elasticsearch_fieldnames'; import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED, } from '../environment_filter_values'; +import { SERVICE_NODE_NAME_MISSING } from '../service_nodes'; export function environmentQuery( environment: string @@ -25,3 +29,17 @@ export function environmentQuery( return [{ term: { [SERVICE_ENVIRONMENT]: environment } }]; } + +export function serviceNodeNameQuery( + serviceNodeName?: string +): QueryDslQueryContainer[] { + if (!serviceNodeName) { + return []; + } + + if (serviceNodeName === SERVICE_NODE_NAME_MISSING) { + return [{ bool: { must_not: [{ exists: { field: SERVICE_NODE_NAME } }] } }]; + } + + return [{ term: { [SERVICE_NODE_NAME]: serviceNodeName } }]; +} diff --git a/x-pack/plugins/apm/public/components/shared/charts/metrics_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/metrics_chart/index.tsx index 9f437a95e7dd9..1c2b7fdeb7714 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/metrics_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/metrics_chart/index.tsx @@ -7,6 +7,7 @@ import { EuiTitle } from '@elastic/eui'; import React from 'react'; +import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { asDecimal, asInteger, @@ -14,8 +15,6 @@ import { getDurationFormatter, getFixedByteFormatter, } from '../../../../../common/utils/formatters'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { GenericMetricsChart } from '../../../../../server/lib/metrics/transform_metrics_chart'; import { Maybe } from '../../../../../typings/common'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { TimeseriesChart } from '../timeseries_chart'; @@ -24,7 +23,11 @@ import { getResponseTimeTickFormatter, } from '../transaction_charts/helper'; -function getYTickFormatter(chart: GenericMetricsChart) { +type MetricChartApiResponse = + APIReturnType<'GET /internal/apm/services/{serviceName}/metrics/charts'>; +type MetricChart = MetricChartApiResponse['charts'][0]; + +function getYTickFormatter(chart: MetricChart) { const max = getMaxY(chart.series); switch (chart.yUnit) { @@ -50,7 +53,7 @@ function getYTickFormatter(chart: GenericMetricsChart) { interface Props { start: Maybe; end: Maybe; - chart: GenericMetricsChart; + chart: MetricChart; fetchStatus: FETCH_STATUS; } diff --git a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts index ef360698192e1..15037ecf6adb8 100644 --- a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts @@ -5,14 +5,16 @@ * 2.0. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetricsChartsByAgentAPIResponse } from '../../server/lib/metrics/get_metrics_chart_data_by_agent'; +import type { APIReturnType } from '../services/rest/createCallApmApi'; import { useApmServiceContext } from '../context/apm_service/use_apm_service_context'; import { useFetcher } from './use_fetcher'; import { useTimeRange } from './use_time_range'; import { useApmParams } from './use_apm_params'; -const INITIAL_DATA: MetricsChartsByAgentAPIResponse = { +type MetricChartApiResponse = + APIReturnType<'GET /internal/apm/services/{serviceName}/metrics/charts'>; + +const INITIAL_DATA: MetricChartApiResponse = { charts: [], }; diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts index 2c8d0f6998bf8..4d4c935d20e76 100644 --- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts +++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts @@ -5,6 +5,10 @@ * 2.0. */ +import { AggregationsTermsAggregationOrder } from '@elastic/elasticsearch/lib/api/types'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; import { ERROR_CULPRIT, ERROR_EXC_HANDLED, @@ -12,9 +16,8 @@ import { ERROR_EXC_TYPE, ERROR_GROUP_ID, ERROR_LOG_MESSAGE, + SERVICE_NAME, } from '../../../common/elasticsearch_fieldnames'; -import { getErrorGroupsProjection } from '../../projections/errors'; -import { mergeProjection } from '../../projections/util/merge_projection'; import { getErrorName } from '../helpers/get_error_name'; import { Setup } from '../helpers/setup_request'; @@ -42,27 +45,31 @@ export async function getErrorGroups({ // sort buckets by last occurrence of error const sortByLatestOccurrence = sortField === 'latestOccurrenceAt'; - const projection = getErrorGroupsProjection({ - environment, - kuery, - serviceName, - start, - end, - }); - - const order = sortByLatestOccurrence - ? { - max_timestamp: sortDirection, - } + const maxTimestampAggKey = 'max_timestamp'; + const order: AggregationsTermsAggregationOrder = sortByLatestOccurrence + ? { [maxTimestampAggKey]: sortDirection } : { _count: sortDirection }; - const params = mergeProjection(projection, { + const params = { + apm: { + events: [ProcessorEvent.error as const], + }, body: { size: 0, + query: { + bool: { + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, aggs: { error_groups: { terms: { - ...projection.body.aggs.error_groups.terms, + field: ERROR_GROUP_ID, size: 500, order, }, @@ -83,19 +90,13 @@ export async function getErrorGroups({ }, }, ...(sortByLatestOccurrence - ? { - max_timestamp: { - max: { - field: '@timestamp', - }, - }, - } + ? { [maxTimestampAggKey]: { max: { field: '@timestamp' } } } : {}), }, }, }, }, - }); + }; const resp = await apmEventClient.search('get_error_groups', params); diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts index 62e23d19b00bd..54e10bd8adde0 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts @@ -9,7 +9,7 @@ import { Setup } from '../../helpers/setup_request'; import { getCPUChartData } from './shared/cpu'; import { getMemoryChartData } from './shared/memory'; -export async function getDefaultMetricsCharts({ +export function getDefaultMetricsCharts({ environment, kuery, serviceName, @@ -24,10 +24,8 @@ export async function getDefaultMetricsCharts({ start: number; end: number; }) { - const charts = await Promise.all([ + return Promise.all([ getCPUChartData({ environment, kuery, setup, serviceName, start, end }), getMemoryChartData({ environment, kuery, setup, serviceName, start, end }), ]); - - return { charts }; } diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts index 117b372d445d2..2d4cf2f70ab5f 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts @@ -11,17 +11,26 @@ import { isFiniteNumber } from '../../../../../../common/utils/is_finite_number' import { Setup } from '../../../../helpers/setup_request'; import { getMetricsDateHistogramParams } from '../../../../helpers/metrics'; import { ChartBase } from '../../../types'; -import { getMetricsProjection } from '../../../../../projections/metrics'; -import { mergeProjection } from '../../../../../projections/util/merge_projection'; + import { AGENT_NAME, LABEL_NAME, METRIC_JAVA_GC_COUNT, METRIC_JAVA_GC_TIME, + SERVICE_NAME, } from '../../../../../../common/elasticsearch_fieldnames'; import { getBucketSize } from '../../../../helpers/get_bucket_size'; import { getVizColorForIndex } from '../../../../../../common/viz_colors'; import { JAVA_AGENT_NAMES } from '../../../../../../common/agent_name'; +import { + environmentQuery, + serviceNodeNameQuery, +} from '../../../../../../common/utils/environment_query'; +import { + kqlQuery, + rangeQuery, +} from '../../../../../../../observability/server'; +import { ProcessorEvent } from '../../../../../../common/processor_event'; export async function fetchAndTransformGcMetrics({ environment, @@ -50,26 +59,24 @@ export async function fetchAndTransformGcMetrics({ const { bucketSize } = getBucketSize({ start, end }); - const projection = getMetricsProjection({ - environment, - kuery, - serviceName, - serviceNodeName, - start, - end, - }); - // GC rate and time are reported by the agents as monotonically // increasing counters, which means that we have to calculate // the delta in an es query. In the future agent might start // reporting deltas. - const params = mergeProjection(projection, { + const params = { + apm: { + events: [ProcessorEvent.metric], + }, body: { size: 0, query: { bool: { filter: [ - ...projection.body.query.bool.filter, + { term: { [SERVICE_NAME]: serviceName } }, + ...serviceNodeNameQuery(serviceNodeName), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), { exists: { field: fieldName } }, { terms: { [AGENT_NAME]: JAVA_AGENT_NAMES } }, ], @@ -114,7 +121,7 @@ export async function fetchAndTransformGcMetrics({ }, }, }, - }); + }; const response = await apmEventClient.search(operationName, params); diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts index 164777539c9d5..9039bb19ebb78 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts @@ -32,7 +32,7 @@ export function getJavaMetricsCharts({ start: number; end: number; }) { - return withApmSpan('get_java_system_metric_charts', async () => { + return withApmSpan('get_java_system_metric_charts', () => { const options = { environment, kuery, @@ -43,7 +43,7 @@ export function getJavaMetricsCharts({ end, }; - const charts = await Promise.all([ + return Promise.all([ getCPUChartData(options), getMemoryChartData(options), getHeapMemoryChart(options), @@ -52,7 +52,5 @@ export function getJavaMetricsCharts({ getGcRateChart(options), getGcTimeChart(options), ]); - - return { charts }; }); } diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts index 581a0782e4d72..0a24179ed9fc6 100644 --- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts @@ -5,15 +5,23 @@ * 2.0. */ -import { Overwrite, Unionize } from 'utility-types'; +import { Unionize } from 'utility-types'; +import { euiLightVars as theme } from '@kbn/ui-shared-deps-src/theme'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { getVizColorForIndex } from '../../../common/viz_colors'; import { AggregationOptionsByType } from '../../../../../../src/core/types/elasticsearch'; -import { getMetricsProjection } from '../../projections/metrics'; -import { mergeProjection } from '../../projections/util/merge_projection'; -import { APMEventESSearchRequest } from '../helpers/create_es_client/create_apm_event_client'; import { getMetricsDateHistogramParams } from '../helpers/metrics'; import { Setup } from '../helpers/setup_request'; -import { transformDataToMetricsChart } from './transform_metrics_chart'; import { ChartBase } from './types'; +import { + environmentQuery, + serviceNodeNameQuery, +} from '../../../common/utils/environment_query'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; +import { APMEventESSearchRequest } from '../helpers/create_es_client/create_apm_event_client'; +import { PromiseReturnType } from '../../../../observability/typings/common'; type MetricsAggregationMap = Unionize<{ min: AggregationOptionsByType['min']; @@ -24,31 +32,20 @@ type MetricsAggregationMap = Unionize<{ type MetricAggs = Record; -export type GenericMetricsRequest = Overwrite< - APMEventESSearchRequest, - { - body: { - aggs: { - timeseriesData: { - date_histogram: AggregationOptionsByType['date_histogram']; - aggs: MetricAggs; - }; - } & MetricAggs; - }; - } ->; - -interface Filter { - exists?: { - field: string; - }; - term?: { - [key: string]: string; - }; - terms?: { - [key: string]: string[]; +export type GenericMetricsRequest = APMEventESSearchRequest & { + body: { + aggs: { + timeseriesData: { + date_histogram: AggregationOptionsByType['date_histogram']; + aggs: MetricAggs; + }; + } & MetricAggs; }; -} +}; + +export type GenericMetricsChart = PromiseReturnType< + typeof fetchAndTransformMetrics +>; export async function fetchAndTransformMetrics({ environment, @@ -72,26 +69,27 @@ export async function fetchAndTransformMetrics({ end: number; chartBase: ChartBase; aggs: T; - additionalFilters?: Filter[]; + additionalFilters?: QueryDslQueryContainer[]; operationName: string; }) { const { apmEventClient, config } = setup; - const projection = getMetricsProjection({ - environment, - kuery, - serviceName, - serviceNodeName, - start, - end, - }); - - const params: GenericMetricsRequest = mergeProjection(projection, { + const params: GenericMetricsRequest = { + apm: { + events: [ProcessorEvent.metric], + }, body: { size: 0, query: { bool: { - filter: [...projection.body.query.bool.filter, ...additionalFilters], + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + ...serviceNodeNameQuery(serviceNodeName), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ...additionalFilters, + ], }, }, aggs: { @@ -106,9 +104,43 @@ export async function fetchAndTransformMetrics({ ...aggs, }, }, - }); + }; + + const { hits, aggregations } = await apmEventClient.search( + operationName, + params + ); + const timeseriesData = aggregations?.timeseriesData; - const response = await apmEventClient.search(operationName, params); + return { + title: chartBase.title, + key: chartBase.key, + yUnit: chartBase.yUnit, + series: + hits.total.value === 0 + ? [] + : Object.keys(chartBase.series).map((seriesKey, i) => { + // @ts-ignore + const overallValue = aggregations?.[seriesKey]?.value as number; - return transformDataToMetricsChart(response, chartBase); + return { + title: chartBase.series[seriesKey].title, + key: seriesKey, + type: chartBase.type, + color: + chartBase.series[seriesKey].color || + getVizColorForIndex(i, theme), + overallValue, + data: + timeseriesData?.buckets.map((bucket) => { + const { value } = bucket[seriesKey]; + const y = value === null || isNaN(value) ? null : value; + return { + x: bucket.key, + y, + }; + }) || [], + }; + }), + }; } diff --git a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts index 29991034f7c5e..611bb8196032c 100644 --- a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts +++ b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts @@ -8,12 +8,8 @@ import { Setup } from '../helpers/setup_request'; import { getJavaMetricsCharts } from './by_agent/java'; import { getDefaultMetricsCharts } from './by_agent/default'; -import { GenericMetricsChart } from './transform_metrics_chart'; import { isJavaAgentName } from '../../../common/agent_name'; - -export interface MetricsChartsByAgentAPIResponse { - charts: GenericMetricsChart[]; -} +import { GenericMetricsChart } from './fetch_and_transform_metrics'; export async function getMetricsChartDataByAgent({ environment, @@ -33,7 +29,7 @@ export async function getMetricsChartDataByAgent({ agentName: string; start: number; end: number; -}): Promise { +}): Promise { if (isJavaAgentName(agentName)) { return getJavaMetricsCharts({ environment, diff --git a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts deleted file mode 100644 index 107e2d5774816..0000000000000 --- a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 { transformDataToMetricsChart } from './transform_metrics_chart'; -import { ChartType, YUnit } from '../../../typings/timeseries'; - -test('transformDataToMetricsChart should transform an ES result into a chart object', () => { - const response = { - hits: { total: { value: 5000 } }, - aggregations: { - a: { value: 1000 }, - b: { value: 1000 }, - c: { value: 1000 }, - timeseriesData: { - buckets: [ - { - a: { value: 10 }, - b: { value: 10 }, - c: { value: 10 }, - key: 1, - doc_count: 0, - }, - { - a: { value: 20 }, - b: { value: 20 }, - c: { value: 20 }, - key: 2, - doc_count: 0, - }, - { - a: { value: 30 }, - b: { value: 30 }, - c: { value: 30 }, - key: 3, - doc_count: 0, - }, - ], - }, - }, - } as any; - - const chartBase = { - title: 'Test Chart Title', - type: 'linemark' as ChartType, - key: 'test_chart_key', - yUnit: 'number' as YUnit, - series: { - a: { title: 'Series A', color: 'red' }, - b: { title: 'Series B', color: 'blue' }, - c: { title: 'Series C', color: 'green' }, - }, - }; - - const chart = transformDataToMetricsChart(response, chartBase); - - expect(chart).toMatchInlineSnapshot(` -Object { - "key": "test_chart_key", - "series": Array [ - Object { - "color": "red", - "data": Array [ - Object { - "x": 1, - "y": 10, - }, - Object { - "x": 2, - "y": 20, - }, - Object { - "x": 3, - "y": 30, - }, - ], - "key": "a", - "overallValue": 1000, - "title": "Series A", - "type": "linemark", - }, - Object { - "color": "blue", - "data": Array [ - Object { - "x": 1, - "y": 10, - }, - Object { - "x": 2, - "y": 20, - }, - Object { - "x": 3, - "y": 30, - }, - ], - "key": "b", - "overallValue": 1000, - "title": "Series B", - "type": "linemark", - }, - Object { - "color": "green", - "data": Array [ - Object { - "x": 1, - "y": 10, - }, - Object { - "x": 2, - "y": 20, - }, - Object { - "x": 3, - "y": 30, - }, - ], - "key": "c", - "overallValue": 1000, - "title": "Series C", - "type": "linemark", - }, - ], - "title": "Test Chart Title", - "yUnit": "number", -} -`); -}); diff --git a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts deleted file mode 100644 index fea853af93b84..0000000000000 --- a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 { euiLightVars as theme } from '@kbn/ui-shared-deps-src/theme'; -import { ESSearchResponse } from '../../../../../../src/core/types/elasticsearch'; -import { getVizColorForIndex } from '../../../common/viz_colors'; -import { GenericMetricsRequest } from './fetch_and_transform_metrics'; -import { ChartBase } from './types'; - -export type GenericMetricsChart = ReturnType< - typeof transformDataToMetricsChart ->; - -export function transformDataToMetricsChart( - result: ESSearchResponse, - chartBase: ChartBase -) { - const { aggregations } = result; - const timeseriesData = aggregations?.timeseriesData; - - return { - title: chartBase.title, - key: chartBase.key, - yUnit: chartBase.yUnit, - series: - result.hits.total.value > 0 - ? Object.keys(chartBase.series).map((seriesKey, i) => { - const overallValue = aggregations?.[seriesKey]?.value; - - return { - title: chartBase.series[seriesKey].title, - key: seriesKey, - type: chartBase.type, - color: - chartBase.series[seriesKey].color || - getVizColorForIndex(i, theme), - overallValue, - data: - timeseriesData?.buckets.map((bucket) => { - const { value } = bucket[seriesKey]; - const y = value === null || isNaN(value) ? null : value; - return { - x: bucket.key, - y, - }; - }) || [], - }; - }) - : [], - }; -} diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts index aaf55413d9774..9f94bdd9275c0 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts @@ -156,7 +156,6 @@ async function getServicesData(options: IEnvOptions) { export type ConnectionsResponse = PromiseReturnType; export type ServicesResponse = PromiseReturnType; -export type ServiceMapAPIResponse = PromiseReturnType; export function getServiceMap(options: IEnvOptions) { return withApmSpan('get_service_map', async () => { diff --git a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap index 1b72fc8867570..e0591a90b1c19 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap @@ -35,11 +35,6 @@ Object { "service.name": "foo", }, }, - Object { - "term": Object { - "service.node.name": "bar", - }, - }, Object { "range": Object { "@timestamp": Object { @@ -49,6 +44,11 @@ Object { }, }, }, + Object { + "term": Object { + "service.node.name": "bar", + }, + }, ], }, }, @@ -92,6 +92,15 @@ Object { "service.name": "foo", }, }, + Object { + "range": Object { + "@timestamp": Object { + "format": "epoch_millis", + "gte": 0, + "lte": 50000, + }, + }, + }, Object { "bool": Object { "must_not": Array [ @@ -103,15 +112,6 @@ Object { ], }, }, - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, ], }, }, @@ -191,6 +191,7 @@ Object { ], }, }, + "size": 0, }, } `; diff --git a/x-pack/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts index 0ae7274c3b33f..541fbb79c9e50 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/index.ts +++ b/x-pack/plugins/apm/server/lib/service_nodes/index.ts @@ -14,8 +14,13 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; import { asMutableArray } from '../../../common/utils/as_mutable_array'; -import { getServiceNodesProjection } from '../../projections/service_nodes'; -import { mergeProjection } from '../../projections/util/merge_projection'; +import { + SERVICE_NAME, + SERVICE_NODE_NAME, +} from '../../../common/elasticsearch_fieldnames'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { Setup } from '../helpers/setup_request'; const getServiceNodes = async ({ @@ -35,20 +40,26 @@ const getServiceNodes = async ({ }) => { const { apmEventClient } = setup; - const projection = getServiceNodesProjection({ - kuery, - serviceName, - environment, - start, - end, - }); - - const params = mergeProjection(projection, { + const params = { + apm: { + events: [ProcessorEvent.metric], + }, body: { + size: 0, + query: { + bool: { + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, aggs: { nodes: { terms: { - ...projection.body.aggs.nodes.terms, + field: SERVICE_NODE_NAME, size: 10000, missing: SERVICE_NODE_NAME_MISSING, }, @@ -57,7 +68,7 @@ const getServiceNodes = async ({ top_metrics: { metrics: asMutableArray([{ field: HOST_NAME }] as const), sort: { - '@timestamp': 'desc', + '@timestamp': 'desc' as const, }, }, }, @@ -85,7 +96,7 @@ const getServiceNodes = async ({ }, }, }, - }); + }; const response = await apmEventClient.search('get_service_nodes', params); diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts index ef52c4b0f4927..ab0fa91529917 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts @@ -11,8 +11,16 @@ import { CONTAINER_ID, } from '../../../common/elasticsearch_fieldnames'; import { NOT_AVAILABLE_LABEL } from '../../../common/i18n'; -import { mergeProjection } from '../../projections/util/merge_projection'; -import { getServiceNodesProjection } from '../../projections/service_nodes'; +import { + SERVICE_NAME, + SERVICE_NODE_NAME, +} from '../../../common/elasticsearch_fieldnames'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { + environmentQuery, + serviceNodeNameQuery, +} from '../../../common/utils/environment_query'; import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; export async function getServiceNodeMetadata({ @@ -32,39 +40,48 @@ export async function getServiceNodeMetadata({ }) { const { apmEventClient } = setup; - const query = mergeProjection( - getServiceNodesProjection({ - kuery, - serviceName, - serviceNodeName, - environment: ENVIRONMENT_ALL.value, - start, - end, - }), - { - body: { - size: 0, - aggs: { - host: { - terms: { - field: HOST_NAME, - size: 1, - }, + const params = { + apm: { + events: [ProcessorEvent.metric], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + ...rangeQuery(start, end), + ...environmentQuery(ENVIRONMENT_ALL.value), + ...kqlQuery(kuery), + ...serviceNodeNameQuery(serviceNodeName), + ], + }, + }, + aggs: { + nodes: { + terms: { + field: SERVICE_NODE_NAME, }, - containerId: { - terms: { - field: CONTAINER_ID, - size: 1, - }, + }, + host: { + terms: { + field: HOST_NAME, + size: 1, + }, + }, + containerId: { + terms: { + field: CONTAINER_ID, + size: 1, }, }, }, - } - ); + }, + }; const response = await apmEventClient.search( 'get_service_node_metadata', - query + params ); return { diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts index dadb29d156e0b..a22c1d35dc663 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts @@ -8,14 +8,9 @@ import { withApmSpan } from '../../../../utils/with_apm_span'; import { getAllEnvironments } from '../../../environments/get_all_environments'; import { Setup } from '../../../helpers/setup_request'; -import { PromiseReturnType } from '../../../../../../observability/typings/common'; import { getExistingEnvironmentsForService } from './get_existing_environments_for_service'; import { ALL_OPTION_VALUE } from '../../../../../common/agent_configuration/all_option'; -export type AgentConfigurationEnvironmentsAPIResponse = PromiseReturnType< - typeof getEnvironments ->; - export async function getEnvironments({ serviceName, setup, diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts index 06bd900872a20..fc5167159b98d 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts @@ -7,15 +7,10 @@ import { ProcessorEvent } from '../../../../common/processor_event'; import { Setup } from '../../helpers/setup_request'; -import { PromiseReturnType } from '../../../../../observability/typings/common'; import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames'; import { ALL_OPTION_VALUE } from '../../../../common/agent_configuration/all_option'; import { getProcessorEventForTransactions } from '../../helpers/transactions'; -export type AgentConfigurationServicesAPIResponse = PromiseReturnType< - typeof getServiceNames ->; - export async function getServiceNames({ setup, searchAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/projections/errors.ts b/x-pack/plugins/apm/server/projections/errors.ts deleted file mode 100644 index b256428143400..0000000000000 --- a/x-pack/plugins/apm/server/projections/errors.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { - SERVICE_NAME, - ERROR_GROUP_ID, -} from '../../common/elasticsearch_fieldnames'; -import { rangeQuery, kqlQuery } from '../../../observability/server'; -import { environmentQuery } from '../../common/utils/environment_query'; -import { ProcessorEvent } from '../../common/processor_event'; - -export function getErrorGroupsProjection({ - environment, - kuery, - serviceName, - start, - end, -}: { - environment: string; - kuery: string; - serviceName: string; - start: number; - end: number; -}) { - return { - apm: { - events: [ProcessorEvent.error as const], - }, - body: { - query: { - bool: { - filter: [ - { term: { [SERVICE_NAME]: serviceName } }, - ...rangeQuery(start, end), - ...environmentQuery(environment), - ...kqlQuery(kuery), - ], - }, - }, - aggs: { - error_groups: { - terms: { - field: ERROR_GROUP_ID, - }, - }, - }, - }, - }; -} diff --git a/x-pack/plugins/apm/server/projections/metrics.ts b/x-pack/plugins/apm/server/projections/metrics.ts deleted file mode 100644 index 417281f2de487..0000000000000 --- a/x-pack/plugins/apm/server/projections/metrics.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { - SERVICE_NAME, - SERVICE_NODE_NAME, -} from '../../common/elasticsearch_fieldnames'; -import { rangeQuery, kqlQuery } from '../../../observability/server'; -import { environmentQuery } from '../../common/utils/environment_query'; -import { SERVICE_NODE_NAME_MISSING } from '../../common/service_nodes'; -import { ProcessorEvent } from '../../common/processor_event'; - -function getServiceNodeNameFilters(serviceNodeName?: string) { - if (!serviceNodeName) { - return []; - } - - if (serviceNodeName === SERVICE_NODE_NAME_MISSING) { - return [{ bool: { must_not: [{ exists: { field: SERVICE_NODE_NAME } }] } }]; - } - - return [{ term: { [SERVICE_NODE_NAME]: serviceNodeName } }]; -} - -export function getMetricsProjection({ - environment, - kuery, - serviceName, - serviceNodeName, - start, - end, -}: { - environment: string; - kuery: string; - serviceName: string; - serviceNodeName?: string; - start: number; - end: number; -}) { - const filter = [ - { term: { [SERVICE_NAME]: serviceName } }, - ...getServiceNodeNameFilters(serviceNodeName), - ...rangeQuery(start, end), - ...environmentQuery(environment), - ...kqlQuery(kuery), - ] as QueryDslQueryContainer[]; - - return { - apm: { - events: [ProcessorEvent.metric], - }, - body: { - query: { - bool: { - filter, - }, - }, - }, - }; -} diff --git a/x-pack/plugins/apm/server/projections/service_nodes.ts b/x-pack/plugins/apm/server/projections/service_nodes.ts deleted file mode 100644 index 5d97af0ad9860..0000000000000 --- a/x-pack/plugins/apm/server/projections/service_nodes.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 { SERVICE_NODE_NAME } from '../../common/elasticsearch_fieldnames'; -import { mergeProjection } from './util/merge_projection'; -import { getMetricsProjection } from './metrics'; - -export function getServiceNodesProjection({ - serviceName, - serviceNodeName, - environment, - kuery, - start, - end, -}: { - serviceName: string; - serviceNodeName?: string; - environment: string; - kuery: string; - start: number; - end: number; -}) { - return mergeProjection( - getMetricsProjection({ - serviceName, - serviceNodeName, - environment, - kuery, - start, - end, - }), - { - body: { - aggs: { - nodes: { - terms: { - field: SERVICE_NODE_NAME, - }, - }, - }, - }, - } - ); -} diff --git a/x-pack/plugins/apm/server/routes/metrics.ts b/x-pack/plugins/apm/server/routes/metrics.ts index 8b6b16a26f1d8..d1796431a86af 100644 --- a/x-pack/plugins/apm/server/routes/metrics.ts +++ b/x-pack/plugins/apm/server/routes/metrics.ts @@ -37,7 +37,8 @@ const metricsChartsRoute = createApmServerRoute({ const { serviceName } = params.path; const { agentName, environment, kuery, serviceNodeName, start, end } = params.query; - return await getMetricsChartDataByAgent({ + + const charts = await getMetricsChartDataByAgent({ environment, kuery, setup, @@ -47,6 +48,8 @@ const metricsChartsRoute = createApmServerRoute({ start, end, }); + + return { charts }; }, }); diff --git a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.spec.ts b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.spec.ts index 74af2c2dba008..d70ee347d4c2d 100644 --- a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.spec.ts +++ b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.spec.ts @@ -7,35 +7,39 @@ import expect from '@kbn/expect'; import { first } from 'lodash'; -import { MetricsChartsByAgentAPIResponse } from '../../../../plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent'; -import { GenericMetricsChart } from '../../../../plugins/apm/server/lib/metrics/transform_metrics_chart'; +import { GenericMetricsChart } from '../../../../plugins/apm/server/lib/metrics/fetch_and_transform_metrics'; +import { SupertestReturnType } from '../../common/apm_api_supertest'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -interface ChartResponse { - body: MetricsChartsByAgentAPIResponse; - status: number; -} +type ChartResponse = SupertestReturnType<'GET /internal/apm/services/{serviceName}/metrics/charts'>; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); - const supertest = getService('legacySupertestAsApmReadUser'); + const apmApiClient = getService('apmApiClient'); registry.when( 'Metrics charts when data is loaded', { config: 'basic', archives: ['metrics_8.0.0'] }, () => { describe('for opbeans-node', () => { - const start = encodeURIComponent('2020-09-08T14:50:00.000Z'); - const end = encodeURIComponent('2020-09-08T14:55:00.000Z'); - const agentName = 'nodejs'; - describe('returns metrics data', () => { let chartsResponse: ChartResponse; before(async () => { - chartsResponse = await supertest.get( - `/internal/apm/services/opbeans-node/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&kuery=&environment=ENVIRONMENT_ALL` - ); + chartsResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/metrics/charts', + params: { + path: { serviceName: 'opbeans-node' }, + query: { + start: '2020-09-08T14:50:00.000Z', + end: '2020-09-08T14:55:00.000Z', + agentName: 'nodejs', + environment: 'ENVIRONMENT_ALL', + kuery: ``, + }, + }, + }); }); + it('contains CPU usage and System memory usage chart data', async () => { expect(chartsResponse.status).to.be(200); expectSnapshot(chartsResponse.body.charts.map((chart) => chart.title)).toMatchInline(` @@ -112,17 +116,22 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('for opbeans-java', () => { - const agentName = 'java'; - describe('returns metrics data', () => { - const start = encodeURIComponent('2020-09-08T14:55:30.000Z'); - const end = encodeURIComponent('2020-09-08T15:00:00.000Z'); - let chartsResponse: ChartResponse; before(async () => { - chartsResponse = await supertest.get( - `/internal/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&environment=ENVIRONMENT_ALL&kuery=` - ); + chartsResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/metrics/charts', + params: { + path: { serviceName: 'opbeans-java' }, + query: { + start: '2020-09-08T14:55:30.000Z', + end: '2020-09-08T15:00:00.000Z', + agentName: 'java', + environment: 'ENVIRONMENT_ALL', + kuery: ``, + }, + }, + }); }); it('has correct chart data', async () => { @@ -406,12 +415,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { // 9223372036854771712 = memory limit for a c-group when no memory limit is specified it('calculates system memory usage using system total field when cgroup limit is equal to 9223372036854771712', async () => { - const start = encodeURIComponent('2020-09-08T15:00:30.000Z'); - const end = encodeURIComponent('2020-09-08T15:05:00.000Z'); - - const chartsResponse: ChartResponse = await supertest.get( - `/internal/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&environment=ENVIRONMENT_ALL&kuery=` - ); + const chartsResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/metrics/charts', + params: { + path: { serviceName: 'opbeans-java' }, + query: { + start: '2020-09-08T15:00:30.000Z', + end: '2020-09-08T15:05:00.000Z', + agentName: 'java', + environment: 'ENVIRONMENT_ALL', + kuery: ``, + }, + }, + }); const systemMemoryUsageChart = chartsResponse.body.charts.find( ({ key }) => key === 'memory_usage_chart'