[APM] Remove projections (#118327) (#118675)

* Delete errors projection

* Remove `getMetricsProjection`

* Remove `getServiceNodesProjection`

* Fix tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Søren Louv-Jansen <soren.louv@elastic.co>
This commit is contained in:
Kibana Machine 2021-11-16 08:16:34 -05:00 committed by GitHub
parent 24f8717345
commit 0365722184
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 292 additions and 553 deletions

View file

@ -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 } }];
}

View file

@ -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<number | string>;
end: Maybe<number | string>;
chart: GenericMetricsChart;
chart: MetricChart;
fetchStatus: FETCH_STATUS;
}

View file

@ -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: [],
};

View file

@ -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);

View file

@ -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 };
}

View file

@ -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);

View file

@ -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 };
});
}

View file

@ -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<string, MetricsAggregationMap>;
export type GenericMetricsRequest = Overwrite<
APMEventESSearchRequest,
{
body: {
aggs: {
timeseriesData: {
date_histogram: AggregationOptionsByType['date_histogram'];
aggs: MetricAggs;
};
} & MetricAggs;
};
}
>;
export type GenericMetricsRequest = 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 GenericMetricsChart = PromiseReturnType<
typeof fetchAndTransformMetrics
>;
export async function fetchAndTransformMetrics<T extends MetricAggs>({
environment,
@ -72,26 +69,27 @@ export async function fetchAndTransformMetrics<T extends MetricAggs>({
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<T extends MetricAggs>({
...aggs,
},
},
});
};
const response = await apmEventClient.search(operationName, params);
const { hits, aggregations } = await apmEventClient.search(
operationName,
params
);
const timeseriesData = aggregations?.timeseriesData;
return transformDataToMetricsChart(response, chartBase);
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 {
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,
};
}) || [],
};
}),
};
}

View file

@ -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<MetricsChartsByAgentAPIResponse> {
}): Promise<GenericMetricsChart[]> {
if (isJavaAgentName(agentName)) {
return getJavaMetricsCharts({
environment,

View file

@ -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",
}
`);
});

View file

@ -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<unknown, GenericMetricsRequest>,
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,
};
}) || [],
};
})
: [],
};
}

View file

@ -156,7 +156,6 @@ async function getServicesData(options: IEnvOptions) {
export type ConnectionsResponse = PromiseReturnType<typeof getConnectionData>;
export type ServicesResponse = PromiseReturnType<typeof getServicesData>;
export type ServiceMapAPIResponse = PromiseReturnType<typeof getServiceMap>;
export function getServiceMap(options: IEnvOptions) {
return withApmSpan('get_service_map', async () => {

View file

@ -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,
},
}
`;

View file

@ -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);

View file

@ -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 {

View file

@ -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,

View file

@ -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,

View file

@ -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,
},
},
},
},
};
}

View file

@ -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,
},
},
},
};
}

View file

@ -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,
},
},
},
},
}
);
}

View file

@ -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 };
},
});

View file

@ -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'