diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
index f7720589359c..c6cc59876fe3 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
@@ -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({
{!isRumAgent && (
-
-
-
+
+
+
)}
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx
new file mode 100644
index 000000000000..f7c2891bb3e6
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx
@@ -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 (
+ <>
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx
index 1d0e1e50c148..8d84ad7878ec 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx
@@ -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> = [
{
@@ -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) {
>
;
+ 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 (
+
+
+
+ {i18n.translate('xpack.apm.instancesLatencyDistributionChartTitle', {
+ defaultMessage: 'Instances latency distribution',
+ })}
+
+
+
+
+
+ item.throughput?.value}
+ xScaleType={ScaleType.Linear}
+ yAccessors={[(item) => item.latency?.value]}
+ yScaleType={ScaleType.Linear}
+ />
+
+
+
+
+
+ );
+}