[APM] Add throughput, error rate charts to backend detail page (#107379)
This commit is contained in:
parent
66b951c07c
commit
cee5bc6f41
|
@ -46,11 +46,11 @@ export function BackendDetailDependenciesTable() {
|
|||
path: {
|
||||
backendName,
|
||||
},
|
||||
query: { start, end, environment, numBuckets: 20, offset },
|
||||
query: { start, end, environment, numBuckets: 20, offset, kuery },
|
||||
},
|
||||
});
|
||||
},
|
||||
[start, end, environment, offset, backendName]
|
||||
[start, end, environment, offset, backendName, kuery]
|
||||
);
|
||||
|
||||
const dependencies =
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { asPercent } from '../../../../common/utils/formatters';
|
||||
import { useApmBackendContext } from '../../../context/apm_backend/use_apm_backend_context';
|
||||
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
|
||||
import { useComparison } from '../../../hooks/use_comparison';
|
||||
import { useFetcher } from '../../../hooks/use_fetcher';
|
||||
import { useTimeRange } from '../../../hooks/use_time_range';
|
||||
import { Coordinate, TimeSeries } from '../../../../typings/timeseries';
|
||||
import { TimeseriesChart } from '../../shared/charts/timeseries_chart';
|
||||
import { useTheme } from '../../../hooks/use_theme';
|
||||
|
||||
function yLabelFormat(y?: number | null) {
|
||||
return asPercent(y || 0, 1);
|
||||
}
|
||||
|
||||
export function BackendErrorRateChart({ height }: { height: number }) {
|
||||
const { backendName } = useApmBackendContext();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const { start, end } = useTimeRange();
|
||||
|
||||
const {
|
||||
urlParams: { kuery, environment },
|
||||
} = useUrlParams();
|
||||
|
||||
const { offset, comparisonChartTheme } = useComparison();
|
||||
|
||||
const { data, status } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (!start || !end) {
|
||||
return;
|
||||
}
|
||||
|
||||
return callApmApi({
|
||||
endpoint: 'GET /api/apm/backends/{backendName}/charts/error_rate',
|
||||
params: {
|
||||
path: {
|
||||
backendName,
|
||||
},
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
offset,
|
||||
kuery,
|
||||
environment,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
[backendName, start, end, offset, kuery, environment]
|
||||
);
|
||||
|
||||
const timeseries = useMemo(() => {
|
||||
const specs: Array<TimeSeries<Coordinate>> = [];
|
||||
|
||||
if (data?.currentTimeseries) {
|
||||
specs.push({
|
||||
data: data.currentTimeseries,
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis7,
|
||||
title: i18n.translate('xpack.apm.backendErrorRateChart.chartTitle', {
|
||||
defaultMessage: 'Error rate',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (data?.comparisonTimeseries) {
|
||||
specs.push({
|
||||
data: data.comparisonTimeseries,
|
||||
type: 'area',
|
||||
color: theme.eui.euiColorMediumShade,
|
||||
title: i18n.translate(
|
||||
'xpack.apm.backendErrorRateChart.previousPeriodLabel',
|
||||
{ defaultMessage: 'Previous period' }
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return specs;
|
||||
}, [data, theme.eui.euiColorVis7, theme.eui.euiColorMediumShade]);
|
||||
|
||||
return (
|
||||
<TimeseriesChart
|
||||
height={height}
|
||||
fetchStatus={status}
|
||||
id="errorRateChart"
|
||||
customTheme={comparisonChartTheme}
|
||||
timeseries={timeseries}
|
||||
yLabelFormat={yLabelFormat}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -65,7 +65,7 @@ export function BackendLatencyChart({ height }: { height: number }) {
|
|||
specs.push({
|
||||
data: data.currentTimeseries,
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis0,
|
||||
color: theme.eui.euiColorVis1,
|
||||
title: i18n.translate('xpack.apm.backendLatencyChart.chartTitle', {
|
||||
defaultMessage: 'Latency',
|
||||
}),
|
||||
|
@ -85,7 +85,7 @@ export function BackendLatencyChart({ height }: { height: number }) {
|
|||
}
|
||||
|
||||
return specs;
|
||||
}, [data, theme.eui.euiColorVis0, theme.eui.euiColorMediumShade]);
|
||||
}, [data, theme.eui.euiColorVis1, theme.eui.euiColorMediumShade]);
|
||||
|
||||
const maxY = getMaxY(timeseries);
|
||||
const latencyFormatter = getDurationFormatter(maxY);
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { asTransactionRate } from '../../../../common/utils/formatters';
|
||||
import { useApmBackendContext } from '../../../context/apm_backend/use_apm_backend_context';
|
||||
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
|
||||
import { useComparison } from '../../../hooks/use_comparison';
|
||||
import { useFetcher } from '../../../hooks/use_fetcher';
|
||||
import { useTimeRange } from '../../../hooks/use_time_range';
|
||||
import { Coordinate, TimeSeries } from '../../../../typings/timeseries';
|
||||
import { TimeseriesChart } from '../../shared/charts/timeseries_chart';
|
||||
import { useTheme } from '../../../hooks/use_theme';
|
||||
|
||||
export function BackendThroughputChart({ height }: { height: number }) {
|
||||
const { backendName } = useApmBackendContext();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const { start, end } = useTimeRange();
|
||||
|
||||
const {
|
||||
urlParams: { kuery, environment },
|
||||
} = useUrlParams();
|
||||
|
||||
const { offset, comparisonChartTheme } = useComparison();
|
||||
|
||||
const { data, status } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (!start || !end) {
|
||||
return;
|
||||
}
|
||||
|
||||
return callApmApi({
|
||||
endpoint: 'GET /api/apm/backends/{backendName}/charts/throughput',
|
||||
params: {
|
||||
path: {
|
||||
backendName,
|
||||
},
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
offset,
|
||||
kuery,
|
||||
environment,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
[backendName, start, end, offset, kuery, environment]
|
||||
);
|
||||
|
||||
const timeseries = useMemo(() => {
|
||||
const specs: Array<TimeSeries<Coordinate>> = [];
|
||||
|
||||
if (data?.currentTimeseries) {
|
||||
specs.push({
|
||||
data: data.currentTimeseries,
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis0,
|
||||
title: i18n.translate('xpack.apm.backendThroughputChart.chartTitle', {
|
||||
defaultMessage: 'Throughput',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (data?.comparisonTimeseries) {
|
||||
specs.push({
|
||||
data: data.comparisonTimeseries,
|
||||
type: 'area',
|
||||
color: theme.eui.euiColorMediumShade,
|
||||
title: i18n.translate(
|
||||
'xpack.apm.backendThroughputChart.previousPeriodLabel',
|
||||
{ defaultMessage: 'Previous period' }
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return specs;
|
||||
}, [data, theme.eui.euiColorVis0, theme.eui.euiColorMediumShade]);
|
||||
|
||||
return (
|
||||
<TimeseriesChart
|
||||
height={height}
|
||||
fetchStatus={status}
|
||||
id="throughputChart"
|
||||
customTheme={comparisonChartTheme}
|
||||
timeseries={timeseries}
|
||||
yLabelFormat={asTransactionRate}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -9,16 +9,20 @@ import { EuiPanel } from '@elastic/eui';
|
|||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ApmBackendContextProvider } from '../../../context/apm_backend/apm_backend_context';
|
||||
import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb';
|
||||
import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context';
|
||||
import { useApmParams } from '../../../hooks/use_apm_params';
|
||||
import { useApmRouter } from '../../../hooks/use_apm_router';
|
||||
import { ApmMainTemplate } from '../../routing/templates/apm_main_template';
|
||||
import { SearchBar } from '../../shared/search_bar';
|
||||
import { BackendLatencyChart } from './backend_latency_chart';
|
||||
import { BackendInventoryTitle } from '../../routing/home';
|
||||
import { BackendDetailDependenciesTable } from './backend_detail_dependencies_table';
|
||||
import { BackendThroughputChart } from './backend_throughput_chart';
|
||||
import { BackendErrorRateChart } from './backend_error_rate_chart';
|
||||
import { BackendDetailTemplate } from '../../routing/templates/backend_detail_template';
|
||||
|
||||
export function BackendDetailOverview() {
|
||||
const {
|
||||
|
@ -43,21 +47,55 @@ export function BackendDetailOverview() {
|
|||
]);
|
||||
|
||||
return (
|
||||
<ApmMainTemplate pageTitle={backendName}>
|
||||
<ApmBackendContextProvider>
|
||||
<ApmBackendContextProvider>
|
||||
<BackendDetailTemplate title={backendName}>
|
||||
<SearchBar showTimeComparison />
|
||||
<ChartPointerEventContextProvider>
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
<EuiFlexGroup direction="row" gutterSize="s">
|
||||
<EuiFlexItem>
|
||||
<EuiPanel hasBorder={true}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate(
|
||||
'xpack.apm.backendDetailLatencyChartTitle',
|
||||
{ defaultMessage: 'Latency' }
|
||||
)}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<BackendLatencyChart height={200} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel hasBorder={true}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate(
|
||||
'xpack.apm.backendDetailThroughputChartTitle',
|
||||
{ defaultMessage: 'Throughput' }
|
||||
)}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<BackendThroughputChart height={200} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel hasBorder={true}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate(
|
||||
'xpack.apm.backendDetailErrorRateChartTitle',
|
||||
{ defaultMessage: 'Error rate' }
|
||||
)}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<BackendErrorRateChart height={200} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</ChartPointerEventContextProvider>
|
||||
<EuiSpacer size="m" />
|
||||
<BackendDetailDependenciesTable />
|
||||
</ApmBackendContextProvider>
|
||||
</ApmMainTemplate>
|
||||
</BackendDetailTemplate>
|
||||
</ApmBackendContextProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { useApmBackendContext } from '../../../context/apm_backend/use_apm_backend_context';
|
||||
import { ApmMainTemplate } from './apm_main_template';
|
||||
import { SpanIcon } from '../../shared/span_icon';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function BackendDetailTemplate({ title, children }: Props) {
|
||||
const {
|
||||
backendName,
|
||||
metadata: { data },
|
||||
} = useApmBackendContext();
|
||||
|
||||
const metadata = data?.metadata;
|
||||
|
||||
return (
|
||||
<ApmMainTemplate
|
||||
pageHeader={{
|
||||
pageTitle: (
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="l">
|
||||
<h1>{backendName}</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<SpanIcon
|
||||
type={metadata?.spanType}
|
||||
subtype={metadata?.spanSubtype}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ApmMainTemplate>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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 { EventOutcome } from '../../../common/event_outcome';
|
||||
import {
|
||||
EVENT_OUTCOME,
|
||||
SPAN_DESTINATION_SERVICE_RESOURCE,
|
||||
} from '../../../common/elasticsearch_fieldnames';
|
||||
import { environmentQuery } from '../../../common/utils/environment_query';
|
||||
import { kqlQuery, rangeQuery } from '../../../../observability/server';
|
||||
import { ProcessorEvent } from '../../../common/processor_event';
|
||||
import { Setup } from '../helpers/setup_request';
|
||||
import { getMetricsDateHistogramParams } from '../helpers/metrics';
|
||||
import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms';
|
||||
|
||||
export async function getErrorRateChartsForBackend({
|
||||
backendName,
|
||||
setup,
|
||||
start,
|
||||
end,
|
||||
environment,
|
||||
kuery,
|
||||
offset,
|
||||
}: {
|
||||
backendName: string;
|
||||
setup: Setup;
|
||||
start: number;
|
||||
end: number;
|
||||
environment?: string;
|
||||
kuery?: string;
|
||||
offset?: string;
|
||||
}) {
|
||||
const { apmEventClient } = setup;
|
||||
|
||||
const { offsetInMs, startWithOffset, endWithOffset } = getOffsetInMs({
|
||||
start,
|
||||
end,
|
||||
offset,
|
||||
});
|
||||
|
||||
const response = await apmEventClient.search('get_error_rate_for_backend', {
|
||||
apm: {
|
||||
events: [ProcessorEvent.metric],
|
||||
},
|
||||
body: {
|
||||
size: 0,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...environmentQuery(environment),
|
||||
...kqlQuery(kuery),
|
||||
...rangeQuery(startWithOffset, endWithOffset),
|
||||
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } },
|
||||
{
|
||||
terms: {
|
||||
[EVENT_OUTCOME]: [EventOutcome.success, EventOutcome.failure],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
timeseries: {
|
||||
date_histogram: getMetricsDateHistogramParams({
|
||||
start: startWithOffset,
|
||||
end: endWithOffset,
|
||||
metricsInterval: 60,
|
||||
}),
|
||||
aggs: {
|
||||
failures: {
|
||||
filter: {
|
||||
term: {
|
||||
[EVENT_OUTCOME]: EventOutcome.failure,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
response.aggregations?.timeseries.buckets.map((bucket) => {
|
||||
const totalCount = bucket.doc_count;
|
||||
const failureCount = bucket.failures.doc_count;
|
||||
|
||||
return {
|
||||
x: bucket.key + offsetInMs,
|
||||
y: failureCount / totalCount,
|
||||
};
|
||||
}) ?? []
|
||||
);
|
||||
}
|
|
@ -51,7 +51,7 @@ export async function getMetadataForBackend({
|
|||
const sample = maybe(sampleResponse.hits.hits[0])?._source;
|
||||
|
||||
return {
|
||||
'span.type': sample?.span.type,
|
||||
'span.subtype': sample?.span.subtype,
|
||||
spanType: sample?.span.type,
|
||||
spanSubtype: sample?.span.subtype,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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 { SPAN_DESTINATION_SERVICE_RESOURCE } from '../../../common/elasticsearch_fieldnames';
|
||||
import { environmentQuery } from '../../../common/utils/environment_query';
|
||||
import { kqlQuery, rangeQuery } from '../../../../observability/server';
|
||||
import { ProcessorEvent } from '../../../common/processor_event';
|
||||
import { Setup } from '../helpers/setup_request';
|
||||
import { getMetricsDateHistogramParams } from '../helpers/metrics';
|
||||
import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms';
|
||||
|
||||
export async function getThroughputChartsForBackend({
|
||||
backendName,
|
||||
setup,
|
||||
start,
|
||||
end,
|
||||
environment,
|
||||
kuery,
|
||||
offset,
|
||||
}: {
|
||||
backendName: string;
|
||||
setup: Setup;
|
||||
start: number;
|
||||
end: number;
|
||||
environment?: string;
|
||||
kuery?: string;
|
||||
offset?: string;
|
||||
}) {
|
||||
const { apmEventClient } = setup;
|
||||
|
||||
const { offsetInMs, startWithOffset, endWithOffset } = getOffsetInMs({
|
||||
start,
|
||||
end,
|
||||
offset,
|
||||
});
|
||||
|
||||
const response = await apmEventClient.search('get_throughput_for_backend', {
|
||||
apm: {
|
||||
events: [ProcessorEvent.metric],
|
||||
},
|
||||
body: {
|
||||
size: 0,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...environmentQuery(environment),
|
||||
...kqlQuery(kuery),
|
||||
...rangeQuery(startWithOffset, endWithOffset),
|
||||
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } },
|
||||
],
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
timeseries: {
|
||||
date_histogram: getMetricsDateHistogramParams({
|
||||
start: startWithOffset,
|
||||
end: endWithOffset,
|
||||
metricsInterval: 60,
|
||||
}),
|
||||
aggs: {
|
||||
throughput: {
|
||||
rate: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
response.aggregations?.timeseries.buckets.map((bucket) => {
|
||||
return {
|
||||
x: bucket.key + offsetInMs,
|
||||
y: bucket.throughput.value,
|
||||
};
|
||||
}) ?? []
|
||||
);
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { kqlQuery } from '../../../../observability/server';
|
||||
import { SPAN_DESTINATION_SERVICE_RESOURCE } from '../../../common/elasticsearch_fieldnames';
|
||||
import { environmentQuery } from '../../../common/utils/environment_query';
|
||||
import { getConnectionStats } from '../connections/get_connection_stats';
|
||||
|
@ -17,6 +18,7 @@ export async function getUpstreamServicesForBackend({
|
|||
end,
|
||||
backendName,
|
||||
numBuckets,
|
||||
kuery,
|
||||
environment,
|
||||
offset,
|
||||
}: {
|
||||
|
@ -25,6 +27,7 @@ export async function getUpstreamServicesForBackend({
|
|||
end: number;
|
||||
backendName: string;
|
||||
numBuckets: number;
|
||||
kuery?: string;
|
||||
environment?: string;
|
||||
offset?: string;
|
||||
}) {
|
||||
|
@ -35,6 +38,7 @@ export async function getUpstreamServicesForBackend({
|
|||
filter: [
|
||||
{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } },
|
||||
...environmentQuery(environment),
|
||||
...kqlQuery(kuery),
|
||||
],
|
||||
collapseBy: 'upstream',
|
||||
numBuckets,
|
||||
|
|
|
@ -15,6 +15,8 @@ import { getMetadataForBackend } from '../lib/backends/get_metadata_for_backend'
|
|||
import { getLatencyChartsForBackend } from '../lib/backends/get_latency_charts_for_backend';
|
||||
import { getTopBackends } from '../lib/backends/get_top_backends';
|
||||
import { getUpstreamServicesForBackend } from '../lib/backends/get_upstream_services_for_backend';
|
||||
import { getThroughputChartsForBackend } from '../lib/backends/get_throughput_charts_for_backend';
|
||||
import { getErrorRateChartsForBackend } from '../lib/backends/get_error_rate_charts_for_backend';
|
||||
|
||||
const topBackendsRoute = createApmServerRoute({
|
||||
endpoint: 'GET /api/apm/backends/top_backends',
|
||||
|
@ -68,7 +70,7 @@ const upstreamServicesForBackendRoute = createApmServerRoute({
|
|||
query: t.intersection([rangeRt, t.type({ numBuckets: toNumberRt })]),
|
||||
}),
|
||||
t.partial({
|
||||
query: t.intersection([environmentRt, offsetRt]),
|
||||
query: t.intersection([environmentRt, offsetRt, kueryRt]),
|
||||
}),
|
||||
]),
|
||||
options: {
|
||||
|
@ -80,10 +82,18 @@ const upstreamServicesForBackendRoute = createApmServerRoute({
|
|||
const { start, end } = setup;
|
||||
const {
|
||||
path: { backendName },
|
||||
query: { environment, offset, numBuckets },
|
||||
query: { environment, offset, numBuckets, kuery },
|
||||
} = resources.params;
|
||||
|
||||
const opts = { backendName, setup, start, end, numBuckets, environment };
|
||||
const opts = {
|
||||
backendName,
|
||||
setup,
|
||||
start,
|
||||
end,
|
||||
numBuckets,
|
||||
environment,
|
||||
kuery,
|
||||
};
|
||||
|
||||
const [currentServices, previousServices] = await Promise.all([
|
||||
getUpstreamServicesForBackend(opts),
|
||||
|
@ -182,8 +192,100 @@ const backendLatencyChartsRoute = createApmServerRoute({
|
|||
},
|
||||
});
|
||||
|
||||
const backendThroughputChartsRoute = createApmServerRoute({
|
||||
endpoint: 'GET /api/apm/backends/{backendName}/charts/throughput',
|
||||
params: t.type({
|
||||
path: t.type({
|
||||
backendName: t.string,
|
||||
}),
|
||||
query: t.intersection([rangeRt, kueryRt, environmentRt, offsetRt]),
|
||||
}),
|
||||
options: {
|
||||
tags: ['access:apm'],
|
||||
},
|
||||
handler: async (resources) => {
|
||||
const setup = await setupRequest(resources);
|
||||
const { params } = resources;
|
||||
const { backendName } = params.path;
|
||||
const { kuery, environment, offset } = params.query;
|
||||
|
||||
const { start, end } = setup;
|
||||
|
||||
const [currentTimeseries, comparisonTimeseries] = await Promise.all([
|
||||
getThroughputChartsForBackend({
|
||||
backendName,
|
||||
setup,
|
||||
start,
|
||||
end,
|
||||
kuery,
|
||||
environment,
|
||||
}),
|
||||
offset
|
||||
? getThroughputChartsForBackend({
|
||||
backendName,
|
||||
setup,
|
||||
start,
|
||||
end,
|
||||
kuery,
|
||||
environment,
|
||||
offset,
|
||||
})
|
||||
: null,
|
||||
]);
|
||||
|
||||
return { currentTimeseries, comparisonTimeseries };
|
||||
},
|
||||
});
|
||||
|
||||
const backendErrorRateChartsRoute = createApmServerRoute({
|
||||
endpoint: 'GET /api/apm/backends/{backendName}/charts/error_rate',
|
||||
params: t.type({
|
||||
path: t.type({
|
||||
backendName: t.string,
|
||||
}),
|
||||
query: t.intersection([rangeRt, kueryRt, environmentRt, offsetRt]),
|
||||
}),
|
||||
options: {
|
||||
tags: ['access:apm'],
|
||||
},
|
||||
handler: async (resources) => {
|
||||
const setup = await setupRequest(resources);
|
||||
const { params } = resources;
|
||||
const { backendName } = params.path;
|
||||
const { kuery, environment, offset } = params.query;
|
||||
|
||||
const { start, end } = setup;
|
||||
|
||||
const [currentTimeseries, comparisonTimeseries] = await Promise.all([
|
||||
getErrorRateChartsForBackend({
|
||||
backendName,
|
||||
setup,
|
||||
start,
|
||||
end,
|
||||
kuery,
|
||||
environment,
|
||||
}),
|
||||
offset
|
||||
? getErrorRateChartsForBackend({
|
||||
backendName,
|
||||
setup,
|
||||
start,
|
||||
end,
|
||||
kuery,
|
||||
environment,
|
||||
offset,
|
||||
})
|
||||
: null,
|
||||
]);
|
||||
|
||||
return { currentTimeseries, comparisonTimeseries };
|
||||
},
|
||||
});
|
||||
|
||||
export const backendsRouteRepository = createApmServerRouteRepository()
|
||||
.add(topBackendsRoute)
|
||||
.add(upstreamServicesForBackendRoute)
|
||||
.add(backendMetadataRoute)
|
||||
.add(backendLatencyChartsRoute);
|
||||
.add(backendLatencyChartsRoute)
|
||||
.add(backendThroughputChartsRoute)
|
||||
.add(backendErrorRateChartsRoute);
|
||||
|
|
Loading…
Reference in a new issue