Instances latency distribution chart (#88546)
* Instances latency distribution chart Create an instances row component that does the data fetching for the instances table and the distribution chart. Use the same data for both the chart and the table. Tooltips and selection are disabled on the chart. * import fix * rename ServiceOverviewInstancesRow to ServiceOverviewInstancesChartAndTable * Updates based on feedback * remove stuff * hasdata
This commit is contained in:
parent
7297cc3b1d
commit
5b315858a3
|
@ -19,7 +19,7 @@ import { SearchBar } from '../../shared/search_bar';
|
||||||
import { UserExperienceCallout } from '../transaction_overview/user_experience_callout';
|
import { UserExperienceCallout } from '../transaction_overview/user_experience_callout';
|
||||||
import { ServiceOverviewDependenciesTable } from './service_overview_dependencies_table';
|
import { ServiceOverviewDependenciesTable } from './service_overview_dependencies_table';
|
||||||
import { ServiceOverviewErrorsTable } from './service_overview_errors_table';
|
import { ServiceOverviewErrorsTable } from './service_overview_errors_table';
|
||||||
import { ServiceOverviewInstancesTable } from './service_overview_instances_table';
|
import { ServiceOverviewInstancesChartAndTable } from './service_overview_instances_chart_and_table';
|
||||||
import { ServiceOverviewThroughputChart } from './service_overview_throughput_chart';
|
import { ServiceOverviewThroughputChart } from './service_overview_throughput_chart';
|
||||||
import { ServiceOverviewTransactionsTable } from './service_overview_transactions_table';
|
import { ServiceOverviewTransactionsTable } from './service_overview_transactions_table';
|
||||||
import { useShouldUseMobileLayout } from './use_should_use_mobile_layout';
|
import { useShouldUseMobileLayout } from './use_should_use_mobile_layout';
|
||||||
|
@ -131,9 +131,16 @@ export function ServiceOverview({
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
{!isRumAgent && (
|
{!isRumAgent && (
|
||||||
<EuiFlexItem>
|
<EuiFlexItem>
|
||||||
<EuiPanel>
|
<EuiFlexGroup
|
||||||
<ServiceOverviewInstancesTable serviceName={serviceName} />
|
direction={rowDirection}
|
||||||
</EuiPanel>
|
gutterSize="s"
|
||||||
|
responsive={false}
|
||||||
|
>
|
||||||
|
<ServiceOverviewInstancesChartAndTable
|
||||||
|
chartHeight={chartHeight}
|
||||||
|
serviceName={serviceName}
|
||||||
|
/>
|
||||||
|
</EuiFlexGroup>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
)}
|
)}
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { EuiFlexItem, EuiPanel } from '@elastic/eui';
|
||||||
|
import React from 'react';
|
||||||
|
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
|
||||||
|
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
|
||||||
|
import { useFetcher } from '../../../hooks/use_fetcher';
|
||||||
|
import { callApmApi } from '../../../services/rest/createCallApmApi';
|
||||||
|
import { InstancesLatencyDistributionChart } from '../../shared/charts/instances_latency_distribution_chart';
|
||||||
|
import { ServiceOverviewInstancesTable } from './service_overview_instances_table';
|
||||||
|
|
||||||
|
interface ServiceOverviewInstancesChartAndTableProps {
|
||||||
|
chartHeight: number;
|
||||||
|
serviceName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ServiceOverviewInstancesChartAndTable({
|
||||||
|
chartHeight,
|
||||||
|
serviceName,
|
||||||
|
}: ServiceOverviewInstancesChartAndTableProps) {
|
||||||
|
const { transactionType } = useApmServiceContext();
|
||||||
|
|
||||||
|
const {
|
||||||
|
urlParams: { start, end },
|
||||||
|
uiFilters,
|
||||||
|
} = useUrlParams();
|
||||||
|
|
||||||
|
const { data = [], status } = useFetcher(() => {
|
||||||
|
if (!start || !end || !transactionType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callApmApi({
|
||||||
|
endpoint:
|
||||||
|
'GET /api/apm/services/{serviceName}/service_overview_instances',
|
||||||
|
params: {
|
||||||
|
path: {
|
||||||
|
serviceName,
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
transactionType,
|
||||||
|
uiFilters: JSON.stringify(uiFilters),
|
||||||
|
numBuckets: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [start, end, serviceName, transactionType, uiFilters]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EuiFlexItem grow={3}>
|
||||||
|
<InstancesLatencyDistributionChart
|
||||||
|
height={chartHeight}
|
||||||
|
items={data}
|
||||||
|
status={status}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem grow={7}>
|
||||||
|
<EuiPanel>
|
||||||
|
<ServiceOverviewInstancesTable
|
||||||
|
items={data}
|
||||||
|
serviceName={serviceName}
|
||||||
|
status={status}
|
||||||
|
/>
|
||||||
|
</EuiPanel>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -4,52 +4,51 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EuiFlexItem } from '@elastic/eui';
|
import {
|
||||||
import { EuiInMemoryTable } from '@elastic/eui';
|
EuiBasicTableColumn,
|
||||||
import { EuiTitle } from '@elastic/eui';
|
EuiFlexGroup,
|
||||||
import { EuiBasicTableColumn } from '@elastic/eui';
|
EuiFlexItem,
|
||||||
import { EuiFlexGroup } from '@elastic/eui';
|
EuiInMemoryTable,
|
||||||
|
EuiTitle,
|
||||||
|
} from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ValuesType } from 'utility-types';
|
import { ValuesType } from 'utility-types';
|
||||||
import { isJavaAgentName } from '../../../../../common/agent_name';
|
import { isJavaAgentName } from '../../../../../common/agent_name';
|
||||||
import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../common/i18n';
|
import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../common/i18n';
|
||||||
import { SERVICE_NODE_NAME_MISSING } from '../../../../../common/service_nodes';
|
import { SERVICE_NODE_NAME_MISSING } from '../../../../../common/service_nodes';
|
||||||
import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context';
|
|
||||||
import {
|
import {
|
||||||
asMillisecondDuration,
|
asMillisecondDuration,
|
||||||
asPercent,
|
asPercent,
|
||||||
asTransactionRate,
|
asTransactionRate,
|
||||||
} from '../../../../../common/utils/formatters';
|
} from '../../../../../common/utils/formatters';
|
||||||
import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher';
|
import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context';
|
||||||
import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
|
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
|
||||||
import {
|
import { APIReturnType } from '../../../../services/rest/createCallApmApi';
|
||||||
APIReturnType,
|
|
||||||
callApmApi,
|
|
||||||
} from '../../../../services/rest/createCallApmApi';
|
|
||||||
import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip';
|
|
||||||
import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper';
|
|
||||||
import { SparkPlot } from '../../../shared/charts/spark_plot';
|
|
||||||
import { px, unit } from '../../../../style/variables';
|
import { px, unit } from '../../../../style/variables';
|
||||||
import { ServiceOverviewTableContainer } from '../service_overview_table_container';
|
import { SparkPlot } from '../../../shared/charts/spark_plot';
|
||||||
import { ServiceNodeMetricOverviewLink } from '../../../shared/Links/apm/ServiceNodeMetricOverviewLink';
|
|
||||||
import { MetricOverviewLink } from '../../../shared/Links/apm/MetricOverviewLink';
|
import { MetricOverviewLink } from '../../../shared/Links/apm/MetricOverviewLink';
|
||||||
|
import { ServiceNodeMetricOverviewLink } from '../../../shared/Links/apm/ServiceNodeMetricOverviewLink';
|
||||||
|
import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper';
|
||||||
|
import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip';
|
||||||
|
import { ServiceOverviewTableContainer } from '../service_overview_table_container';
|
||||||
|
|
||||||
type ServiceInstanceItem = ValuesType<
|
type ServiceInstanceItem = ValuesType<
|
||||||
APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances'>
|
APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances'>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
items?: ServiceInstanceItem[];
|
||||||
serviceName: string;
|
serviceName: string;
|
||||||
|
status: FETCH_STATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ServiceOverviewInstancesTable({ serviceName }: Props) {
|
export function ServiceOverviewInstancesTable({
|
||||||
const { agentName, transactionType } = useApmServiceContext();
|
items = [],
|
||||||
|
serviceName,
|
||||||
const {
|
status,
|
||||||
urlParams: { start, end },
|
}: Props) {
|
||||||
uiFilters,
|
const { agentName } = useApmServiceContext();
|
||||||
} = useUrlParams();
|
|
||||||
|
|
||||||
const columns: Array<EuiBasicTableColumn<ServiceInstanceItem>> = [
|
const columns: Array<EuiBasicTableColumn<ServiceInstanceItem>> = [
|
||||||
{
|
{
|
||||||
|
@ -197,31 +196,8 @@ export function ServiceOverviewInstancesTable({ serviceName }: Props) {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const { data = [], status } = useFetcher(() => {
|
|
||||||
if (!start || !end || !transactionType) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return callApmApi({
|
|
||||||
endpoint:
|
|
||||||
'GET /api/apm/services/{serviceName}/service_overview_instances',
|
|
||||||
params: {
|
|
||||||
path: {
|
|
||||||
serviceName,
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
transactionType,
|
|
||||||
uiFilters: JSON.stringify(uiFilters),
|
|
||||||
numBuckets: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [start, end, serviceName, transactionType, uiFilters]);
|
|
||||||
|
|
||||||
// need top-level sortable fields for the managed table
|
// need top-level sortable fields for the managed table
|
||||||
const items = data.map((item) => ({
|
const tableItems = items.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
latencyValue: item.latency?.value ?? 0,
|
latencyValue: item.latency?.value ?? 0,
|
||||||
throughputValue: item.throughput?.value ?? 0,
|
throughputValue: item.throughput?.value ?? 0,
|
||||||
|
@ -250,7 +226,7 @@ export function ServiceOverviewInstancesTable({ serviceName }: Props) {
|
||||||
>
|
>
|
||||||
<EuiInMemoryTable
|
<EuiInMemoryTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
items={items}
|
items={tableItems}
|
||||||
allowNeutralSort={false}
|
allowNeutralSort={false}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
pagination={{
|
pagination={{
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Axis,
|
||||||
|
BubbleSeries,
|
||||||
|
Chart,
|
||||||
|
Position,
|
||||||
|
ScaleType,
|
||||||
|
Settings,
|
||||||
|
} from '@elastic/charts';
|
||||||
|
import { EuiPanel, EuiTitle } from '@elastic/eui';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import React from 'react';
|
||||||
|
import { useChartTheme } from '../../../../../../observability/public';
|
||||||
|
import {
|
||||||
|
asTransactionRate,
|
||||||
|
getDurationFormatter,
|
||||||
|
} from '../../../../../common/utils/formatters';
|
||||||
|
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
|
||||||
|
import { useTheme } from '../../../../hooks/use_theme';
|
||||||
|
import { APIReturnType } from '../../../../services/rest/createCallApmApi';
|
||||||
|
import { ChartContainer } from '../chart_container';
|
||||||
|
import { getResponseTimeTickFormatter } from '../transaction_charts/helper';
|
||||||
|
|
||||||
|
interface InstancesLatencyDistributionChartProps {
|
||||||
|
height: number;
|
||||||
|
items?: APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances'>;
|
||||||
|
status: FETCH_STATUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InstancesLatencyDistributionChart({
|
||||||
|
height,
|
||||||
|
items = [],
|
||||||
|
status,
|
||||||
|
}: InstancesLatencyDistributionChartProps) {
|
||||||
|
const hasData = items.length > 0;
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const chartTheme = {
|
||||||
|
...useChartTheme(),
|
||||||
|
bubbleSeriesStyle: {
|
||||||
|
point: {
|
||||||
|
strokeWidth: 0,
|
||||||
|
fill: theme.eui.euiColorVis1,
|
||||||
|
radius: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const maxLatency = Math.max(...items.map((item) => item.latency?.value ?? 0));
|
||||||
|
const latencyFormatter = getDurationFormatter(maxLatency);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiPanel>
|
||||||
|
<EuiTitle size="xs">
|
||||||
|
<h2>
|
||||||
|
{i18n.translate('xpack.apm.instancesLatencyDistributionChartTitle', {
|
||||||
|
defaultMessage: 'Instances latency distribution',
|
||||||
|
})}
|
||||||
|
</h2>
|
||||||
|
</EuiTitle>
|
||||||
|
<ChartContainer hasData={hasData} height={height} status={status}>
|
||||||
|
<Chart id="instances-latency-distribution">
|
||||||
|
<Settings
|
||||||
|
legendPosition={Position.Bottom}
|
||||||
|
tooltip="none"
|
||||||
|
showLegend
|
||||||
|
theme={chartTheme}
|
||||||
|
/>
|
||||||
|
<BubbleSeries
|
||||||
|
color={theme.eui.euiColorVis1}
|
||||||
|
data={items}
|
||||||
|
id={i18n.translate(
|
||||||
|
'xpack.apm.instancesLatencyDistributionChartLegend',
|
||||||
|
{ defaultMessage: 'Instances' }
|
||||||
|
)}
|
||||||
|
xAccessor={(item) => item.throughput?.value}
|
||||||
|
xScaleType={ScaleType.Linear}
|
||||||
|
yAccessors={[(item) => item.latency?.value]}
|
||||||
|
yScaleType={ScaleType.Linear}
|
||||||
|
/>
|
||||||
|
<Axis
|
||||||
|
id="x-axis"
|
||||||
|
labelFormat={asTransactionRate}
|
||||||
|
position={Position.Bottom}
|
||||||
|
/>
|
||||||
|
<Axis
|
||||||
|
id="y-axis"
|
||||||
|
labelFormat={getResponseTimeTickFormatter(latencyFormatter)}
|
||||||
|
position={Position.Left}
|
||||||
|
ticks={3}
|
||||||
|
/>
|
||||||
|
</Chart>
|
||||||
|
</ChartContainer>
|
||||||
|
</EuiPanel>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue