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:
Nathan L Smith 2021-01-20 10:07:13 -06:00 committed by GitHub
parent 7297cc3b1d
commit 5b315858a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 212 additions and 53 deletions

View file

@ -19,7 +19,7 @@ import { SearchBar } from '../../shared/search_bar';
import { UserExperienceCallout } from '../transaction_overview/user_experience_callout';
import { ServiceOverviewDependenciesTable } from './service_overview_dependencies_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 { ServiceOverviewTransactionsTable } from './service_overview_transactions_table';
import { useShouldUseMobileLayout } from './use_should_use_mobile_layout';
@ -131,9 +131,16 @@ export function ServiceOverview({
</EuiFlexItem>
{!isRumAgent && (
<EuiFlexItem>
<EuiPanel>
<ServiceOverviewInstancesTable serviceName={serviceName} />
</EuiPanel>
<EuiFlexGroup
direction={rowDirection}
gutterSize="s"
responsive={false}
>
<ServiceOverviewInstancesChartAndTable
chartHeight={chartHeight}
serviceName={serviceName}
/>
</EuiFlexGroup>
</EuiFlexItem>
)}
</EuiFlexGroup>

View file

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

View file

@ -4,52 +4,51 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiFlexItem } from '@elastic/eui';
import { EuiInMemoryTable } from '@elastic/eui';
import { EuiTitle } from '@elastic/eui';
import { EuiBasicTableColumn } from '@elastic/eui';
import { EuiFlexGroup } from '@elastic/eui';
import {
EuiBasicTableColumn,
EuiFlexGroup,
EuiFlexItem,
EuiInMemoryTable,
EuiTitle,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { ValuesType } from 'utility-types';
import { isJavaAgentName } from '../../../../../common/agent_name';
import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../common/i18n';
import { SERVICE_NODE_NAME_MISSING } from '../../../../../common/service_nodes';
import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context';
import {
asMillisecondDuration,
asPercent,
asTransactionRate,
} from '../../../../../common/utils/formatters';
import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher';
import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
import {
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 { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context';
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
import { APIReturnType } from '../../../../services/rest/createCallApmApi';
import { px, unit } from '../../../../style/variables';
import { ServiceOverviewTableContainer } from '../service_overview_table_container';
import { ServiceNodeMetricOverviewLink } from '../../../shared/Links/apm/ServiceNodeMetricOverviewLink';
import { SparkPlot } from '../../../shared/charts/spark_plot';
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<
APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances'>
>;
interface Props {
items?: ServiceInstanceItem[];
serviceName: string;
status: FETCH_STATUS;
}
export function ServiceOverviewInstancesTable({ serviceName }: Props) {
const { agentName, transactionType } = useApmServiceContext();
const {
urlParams: { start, end },
uiFilters,
} = useUrlParams();
export function ServiceOverviewInstancesTable({
items = [],
serviceName,
status,
}: Props) {
const { agentName } = useApmServiceContext();
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
const items = data.map((item) => ({
const tableItems = items.map((item) => ({
...item,
latencyValue: item.latency?.value ?? 0,
throughputValue: item.throughput?.value ?? 0,
@ -250,7 +226,7 @@ export function ServiceOverviewInstancesTable({ serviceName }: Props) {
>
<EuiInMemoryTable
columns={columns}
items={items}
items={tableItems}
allowNeutralSort={false}
loading={isLoading}
pagination={{

View file

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