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