mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Instances latency distribution chart tooltips and axis fixes (#95577)
Fixes #88852
This commit is contained in:
parent
355c949463
commit
dfca5d440c
10 changed files with 586 additions and 39 deletions
|
@ -13,10 +13,3 @@ export const NOT_AVAILABLE_LABEL = i18n.translate(
|
|||
defaultMessage: 'N/A',
|
||||
}
|
||||
);
|
||||
|
||||
export const UNIDENTIFIED_SERVICE_NODES_LABEL = i18n.translate(
|
||||
'xpack.apm.serviceNodeNameMissing',
|
||||
{
|
||||
defaultMessage: '(Empty)',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -5,4 +5,19 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const SERVICE_NODE_NAME_MISSING = '_service_node_name_missing_';
|
||||
|
||||
const UNIDENTIFIED_SERVICE_NODES_LABEL = i18n.translate(
|
||||
'xpack.apm.serviceNodeNameMissing',
|
||||
{
|
||||
defaultMessage: '(Empty)',
|
||||
}
|
||||
);
|
||||
|
||||
export function getServiceNodeName(serviceNodeName?: string) {
|
||||
return serviceNodeName === SERVICE_NODE_NAME_MISSING || !serviceNodeName
|
||||
? UNIDENTIFIED_SERVICE_NODES_LABEL
|
||||
: serviceNodeName;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import React from 'react';
|
||||
import { Redirect, RouteComponentProps } from 'react-router-dom';
|
||||
import { ApmServiceContextProvider } from '../../../../context/apm_service/apm_service_context';
|
||||
import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../common/i18n';
|
||||
import { SERVICE_NODE_NAME_MISSING } from '../../../../../common/service_nodes';
|
||||
import { getServiceNodeName } from '../../../../../common/service_nodes';
|
||||
import { APMRouteDefinition } from '../../../../application/routes';
|
||||
import { toQuery } from '../../../shared/Links/url_helpers';
|
||||
import { ErrorGroupDetails } from '../../ErrorGroupDetails';
|
||||
|
@ -294,15 +293,7 @@ export const routes: APMRouteDefinition[] = [
|
|||
exact: true,
|
||||
path: '/services/:serviceName/nodes/:serviceNodeName/metrics',
|
||||
component: withApmServiceContext(ServiceNodeMetrics),
|
||||
breadcrumb: ({ match }) => {
|
||||
const { serviceNodeName } = match.params;
|
||||
|
||||
if (serviceNodeName === SERVICE_NODE_NAME_MISSING) {
|
||||
return UNIDENTIFIED_SERVICE_NODES_LABEL;
|
||||
}
|
||||
|
||||
return serviceNodeName || '';
|
||||
},
|
||||
breadcrumb: ({ match }) => getServiceNodeName(match.params.serviceNodeName),
|
||||
},
|
||||
{
|
||||
exact: true,
|
||||
|
|
|
@ -8,8 +8,10 @@ import { EuiFlexGroup, EuiPage, EuiPanel, EuiToolTip } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
|
||||
import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../common/i18n';
|
||||
import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes';
|
||||
import {
|
||||
getServiceNodeName,
|
||||
SERVICE_NODE_NAME_MISSING,
|
||||
} from '../../../../common/service_nodes';
|
||||
import {
|
||||
asDynamicBytes,
|
||||
asInteger,
|
||||
|
@ -83,7 +85,7 @@ function ServiceNodeOverview({ serviceName }: ServiceNodeOverviewProps) {
|
|||
const { displayedName, tooltip } =
|
||||
name === SERVICE_NODE_NAME_MISSING
|
||||
? {
|
||||
displayedName: UNIDENTIFIED_SERVICE_NODES_LABEL,
|
||||
displayedName: getServiceNodeName(name),
|
||||
tooltip: i18n.translate(
|
||||
'xpack.apm.jvmsTable.explainServiceNodeNameMissing',
|
||||
{
|
||||
|
|
|
@ -13,19 +13,13 @@ import { useApmServiceContext } from '../../../context/apm_service/use_apm_servi
|
|||
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
|
||||
import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';
|
||||
import { APIReturnType } from '../../../services/rest/createCallApmApi';
|
||||
import { InstancesLatencyDistributionChart } from '../../shared/charts/instances_latency_distribution_chart';
|
||||
import { getTimeRangeComparison } from '../../shared/time_comparison/get_time_range_comparison';
|
||||
import {
|
||||
ServiceOverviewInstancesTable,
|
||||
TableOptions,
|
||||
} from './service_overview_instances_table';
|
||||
|
||||
// We're hiding this chart until these issues are resolved in the 7.13 timeframe:
|
||||
//
|
||||
// * [[APM] Tooltips for instances latency distribution chart](https://github.com/elastic/kibana/issues/88852)
|
||||
// * [[APM] x-axis on the instance bubble chart is broken](https://github.com/elastic/kibana/issues/92631)
|
||||
//
|
||||
// import { InstancesLatencyDistributionChart } from '../../shared/charts/instances_latency_distribution_chart';
|
||||
|
||||
interface ServiceOverviewInstancesChartAndTableProps {
|
||||
chartHeight: number;
|
||||
serviceName: string;
|
||||
|
@ -215,13 +209,13 @@ export function ServiceOverviewInstancesChartAndTable({
|
|||
|
||||
return (
|
||||
<>
|
||||
{/* <EuiFlexItem grow={3}>
|
||||
<EuiFlexItem grow={3}>
|
||||
<InstancesLatencyDistributionChart
|
||||
height={chartHeight}
|
||||
items={data.items}
|
||||
status={status}
|
||||
items={primaryStatsItems}
|
||||
status={primaryStatsStatus}
|
||||
/>
|
||||
</EuiFlexItem> */}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={7}>
|
||||
<EuiPanel>
|
||||
<ServiceOverviewInstancesTable
|
||||
|
|
|
@ -10,8 +10,10 @@ import { i18n } from '@kbn/i18n';
|
|||
import React from 'react';
|
||||
import { LatencyAggregationType } from '../../../../../common/latency_aggregation_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 {
|
||||
getServiceNodeName,
|
||||
SERVICE_NODE_NAME_MISSING,
|
||||
} from '../../../../../common/service_nodes';
|
||||
import {
|
||||
asMillisecondDuration,
|
||||
asPercent,
|
||||
|
@ -52,9 +54,7 @@ export function getColumns({
|
|||
const { serviceNodeName } = item;
|
||||
const isMissingServiceNodeName =
|
||||
serviceNodeName === SERVICE_NODE_NAME_MISSING;
|
||||
const text = isMissingServiceNodeName
|
||||
? UNIDENTIFIED_SERVICE_NODES_LABEL
|
||||
: serviceNodeName;
|
||||
const text = getServiceNodeName(serviceNodeName);
|
||||
|
||||
const link = isJavaAgentName(agentName) ? (
|
||||
<ServiceNodeMetricOverviewLink
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* 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 { TooltipInfo } from '@elastic/charts';
|
||||
import React, { ComponentType } from 'react';
|
||||
import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
|
||||
import { getDurationFormatter } from '../../../../../common/utils/formatters';
|
||||
import { PrimaryStatsServiceInstanceItem } from '../../../app/service_overview/service_overview_instances_chart_and_table';
|
||||
import { CustomTooltip } from './custom_tooltip';
|
||||
|
||||
function getLatencyFormatter(props: TooltipInfo) {
|
||||
const maxLatency = Math.max(
|
||||
...props.values.map((value) => {
|
||||
const datum = (value.datum as unknown) as PrimaryStatsServiceInstanceItem;
|
||||
return datum.latency ?? 0;
|
||||
})
|
||||
);
|
||||
return getDurationFormatter(maxLatency);
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'shared/charts/InstancesLatencyDistributionChart/CustomTooltip',
|
||||
component: CustomTooltip,
|
||||
decorators: [
|
||||
(Story: ComponentType) => (
|
||||
<EuiThemeProvider>
|
||||
<Story />
|
||||
</EuiThemeProvider>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export function Example(props: TooltipInfo) {
|
||||
return (
|
||||
<CustomTooltip {...props} latencyFormatter={getLatencyFormatter(props)} />
|
||||
);
|
||||
}
|
||||
Example.args = {
|
||||
header: {
|
||||
seriesIdentifier: {
|
||||
key:
|
||||
'groupId{__global__}spec{Instances}yAccessor{(index:0)}splitAccessors{}',
|
||||
specId: 'Instances',
|
||||
yAccessor: '(index:0)',
|
||||
splitAccessors: {},
|
||||
seriesKeys: ['(index:0)'],
|
||||
},
|
||||
valueAccessor: 'y1',
|
||||
label: 'Instances',
|
||||
value: 9.473837632998105,
|
||||
formattedValue: '9.473837632998105',
|
||||
markValue: null,
|
||||
color: '#6092c0',
|
||||
isHighlighted: false,
|
||||
isVisible: true,
|
||||
datum: {
|
||||
serviceNodeName:
|
||||
'2f3221afa3f00d3bc07069d69efd5bd4c1607be6155a204551c8fe2e2b5dd750',
|
||||
errorRate: 0.03496503496503497,
|
||||
latency: 1057231.4125874126,
|
||||
throughput: 9.473837632998105,
|
||||
cpuUsage: 0.000033333333333333335,
|
||||
memoryUsage: 0.18701022939403547,
|
||||
},
|
||||
},
|
||||
values: [
|
||||
{
|
||||
seriesIdentifier: {
|
||||
key:
|
||||
'groupId{__global__}spec{Instances}yAccessor{(index:0)}splitAccessors{}',
|
||||
specId: 'Instances',
|
||||
},
|
||||
valueAccessor: 'y1',
|
||||
label: 'Instances',
|
||||
value: 1057231.4125874126,
|
||||
formattedValue: '1057231.4125874126',
|
||||
markValue: null,
|
||||
color: '#6092c0',
|
||||
isHighlighted: true,
|
||||
isVisible: true,
|
||||
datum: {
|
||||
serviceNodeName:
|
||||
'2f3221afa3f00d3bc07069d69efd5bd4c1607be6155a204551c8fe2e2b5dd750',
|
||||
errorRate: 0.03496503496503497,
|
||||
latency: 1057231.4125874126,
|
||||
throughput: 9.473837632998105,
|
||||
cpuUsage: 0.000033333333333333335,
|
||||
memoryUsage: 0.18701022939403547,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as TooltipInfo;
|
||||
|
||||
export function MultipleInstances(props: TooltipInfo) {
|
||||
return (
|
||||
<CustomTooltip {...props} latencyFormatter={getLatencyFormatter(props)} />
|
||||
);
|
||||
}
|
||||
MultipleInstances.args = {
|
||||
header: {
|
||||
seriesIdentifier: {
|
||||
key:
|
||||
'groupId{__global__}spec{Instances}yAccessor{(index:0)}splitAccessors{}',
|
||||
specId: 'Instances',
|
||||
yAccessor: '(index:0)',
|
||||
splitAccessors: {},
|
||||
seriesKeys: ['(index:0)'],
|
||||
},
|
||||
valueAccessor: 'y1',
|
||||
label: 'Instances',
|
||||
value: 9.606338858634443,
|
||||
formattedValue: '9.606338858634443',
|
||||
markValue: null,
|
||||
color: '#6092c0',
|
||||
isHighlighted: false,
|
||||
isVisible: true,
|
||||
datum: {
|
||||
serviceNodeName:
|
||||
'3b50ad269c45be69088905c4b355cc75ab94aaac1b35432bb752050438f4216f',
|
||||
errorRate: 0.006896551724137931,
|
||||
latency: 56465.53793103448,
|
||||
throughput: 9.606338858634443,
|
||||
cpuUsage: 0.0001,
|
||||
memoryUsage: 0.1872131360014741,
|
||||
},
|
||||
},
|
||||
values: [
|
||||
{
|
||||
seriesIdentifier: {
|
||||
key:
|
||||
'groupId{__global__}spec{Instances}yAccessor{(index:0)}splitAccessors{}',
|
||||
specId: 'Instances',
|
||||
},
|
||||
valueAccessor: 'y1',
|
||||
label: 'Instances',
|
||||
value: 56465.53793103448,
|
||||
formattedValue: '56465.53793103448',
|
||||
markValue: null,
|
||||
color: '#6092c0',
|
||||
isHighlighted: true,
|
||||
isVisible: true,
|
||||
datum: {
|
||||
serviceNodeName:
|
||||
'3b50ad269c45be69088905c4b355cc75ab94aaac1b35432bb752050438f4216f',
|
||||
errorRate: 0.006896551724137931,
|
||||
latency: 56465.53793103448,
|
||||
throughput: 9.606338858634443,
|
||||
cpuUsage: 0.0001,
|
||||
memoryUsage: 0.1872131360014741,
|
||||
},
|
||||
},
|
||||
{
|
||||
seriesIdentifier: {
|
||||
key:
|
||||
'groupId{__global__}spec{Instances}yAccessor{(index:0)}splitAccessors{}',
|
||||
specId: 'Instances',
|
||||
},
|
||||
valueAccessor: 'y1',
|
||||
label: 'Instances',
|
||||
value: 56465.53793103448,
|
||||
formattedValue: '56465.53793103448',
|
||||
markValue: null,
|
||||
color: '#6092c0',
|
||||
isHighlighted: true,
|
||||
isVisible: true,
|
||||
datum: {
|
||||
serviceNodeName:
|
||||
'3b50ad269c45be69088905c4b355cc75ab94aaac1b35432bb752050438f4216f (2)',
|
||||
errorRate: 0.006896551724137931,
|
||||
latency: 56465.53793103448,
|
||||
throughput: 9.606338858634443,
|
||||
cpuUsage: 0.0001,
|
||||
memoryUsage: 0.1872131360014741,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as TooltipInfo;
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* 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 { TooltipInfo } from '@elastic/charts';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { getServiceNodeName } from '../../../../../common/service_nodes';
|
||||
import {
|
||||
asTransactionRate,
|
||||
TimeFormatter,
|
||||
} from '../../../../../common/utils/formatters';
|
||||
import { useTheme } from '../../../../hooks/use_theme';
|
||||
import { PrimaryStatsServiceInstanceItem } from '../../../app/service_overview/service_overview_instances_chart_and_table';
|
||||
|
||||
const latencyLabel = i18n.translate(
|
||||
'xpack.apm.instancesLatencyDistributionChartTooltipLatencyLabel',
|
||||
{
|
||||
defaultMessage: 'Latency',
|
||||
}
|
||||
);
|
||||
|
||||
const throughputLabel = i18n.translate(
|
||||
'xpack.apm.instancesLatencyDistributionChartTooltipThroughputLabel',
|
||||
{
|
||||
defaultMessage: 'Throughput',
|
||||
}
|
||||
);
|
||||
|
||||
const clickToFilterDescription = i18n.translate(
|
||||
'xpack.apm.instancesLatencyDistributionChartTooltipClickToFilterDescription',
|
||||
{ defaultMessage: 'Click to filter by instance' }
|
||||
);
|
||||
|
||||
/**
|
||||
* Tooltip for a single instance
|
||||
*/
|
||||
function SingleInstanceCustomTooltip({
|
||||
latencyFormatter,
|
||||
values,
|
||||
}: {
|
||||
latencyFormatter: TimeFormatter;
|
||||
values: TooltipInfo['values'];
|
||||
}) {
|
||||
const value = values[0];
|
||||
const { color } = value;
|
||||
const datum = (value.datum as unknown) as PrimaryStatsServiceInstanceItem;
|
||||
const { latency, serviceNodeName, throughput } = datum;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="echTooltip__header">
|
||||
{getServiceNodeName(serviceNodeName)}
|
||||
</div>
|
||||
<div className="echTooltip__list">
|
||||
<div className="echTooltip__item">
|
||||
<div
|
||||
className="echTooltip__item--backgroundColor"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<div
|
||||
className="echTooltip__item--color"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
</div>
|
||||
<div className="echTooltip__item--container">
|
||||
<span className="echTooltip__label">{latencyLabel}</span>
|
||||
<span className="echTooltip__value">
|
||||
{latencyFormatter(latency).formatted}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="echTooltip__item">
|
||||
<div
|
||||
className="echTooltip__item--backgroundColor"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<div
|
||||
className="echTooltip__item--color"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
</div>
|
||||
<div className="echTooltip__item--container">
|
||||
<span className="echTooltip__label">{throughputLabel}</span>
|
||||
<span className="echTooltip__value">
|
||||
{asTransactionRate(throughput)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tooltip for a multiple instances
|
||||
*/
|
||||
function MultipleInstanceCustomTooltip({
|
||||
latencyFormatter,
|
||||
values,
|
||||
}: TooltipInfo & { latencyFormatter: TimeFormatter }) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="echTooltip__header">
|
||||
{i18n.translate(
|
||||
'xpack.apm.instancesLatencyDistributionChartTooltipInstancesTitle',
|
||||
{
|
||||
defaultMessage:
|
||||
'{instancesCount} {instancesCount, plural, one {instance} other {instances}}',
|
||||
values: { instancesCount: values.length },
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{values.map((value) => {
|
||||
const { color } = value;
|
||||
const datum = (value.datum as unknown) as PrimaryStatsServiceInstanceItem;
|
||||
const { latency, serviceNodeName, throughput } = datum;
|
||||
return (
|
||||
<div className="echTooltip__list" key={serviceNodeName}>
|
||||
<div className="echTooltip__item">
|
||||
<div
|
||||
className="echTooltip__item--backgroundColor"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<div
|
||||
className="echTooltip__item--color"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
</div>
|
||||
<div className="echTooltip__item--container">
|
||||
<span className="echTooltip__label">
|
||||
{getServiceNodeName(serviceNodeName)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="echTooltip__item">
|
||||
<div
|
||||
className="echTooltip__item--backgroundColor"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<div
|
||||
className="echTooltip__item--color"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="echTooltip__item--container"
|
||||
style={{ paddingLeft: theme.eui.paddingSizes.s }}
|
||||
>
|
||||
<span className="echTooltip__label">{latencyLabel}</span>
|
||||
<span className="echTooltip__value">
|
||||
{latencyFormatter(latency).formatted}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="echTooltip__item">
|
||||
<div
|
||||
className="echTooltip__item--backgroundColor"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<div
|
||||
className="echTooltip__item--color"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="echTooltip__item--container"
|
||||
style={{ paddingLeft: theme.eui.paddingSizes.s }}
|
||||
>
|
||||
<span className="echTooltip__label">{throughputLabel}</span>
|
||||
<span className="echTooltip__value">
|
||||
{asTransactionRate(throughput)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom tooltip for instances latency distribution chart.
|
||||
*
|
||||
* The styling provided here recreates that in the Elastic Charts tooltip: https://github.com/elastic/elastic-charts/blob/58e6b5fbf77f4471d2a9a41c45a61f79ebd89b65/src/components/tooltip/tooltip.tsx
|
||||
*
|
||||
* We probably won't need to do all of this once https://github.com/elastic/elastic-charts/issues/615 is completed.
|
||||
*/
|
||||
export function CustomTooltip(
|
||||
props: TooltipInfo & { latencyFormatter: TimeFormatter }
|
||||
) {
|
||||
const { values } = props;
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div className="echTooltip">
|
||||
{values.length > 1 ? (
|
||||
<MultipleInstanceCustomTooltip {...props} />
|
||||
) : (
|
||||
<SingleInstanceCustomTooltip {...props} />
|
||||
)}
|
||||
<div style={{ padding: theme.eui.paddingSizes.xs }}>
|
||||
<EuiIcon type="filter" /> {clickToFilterDescription}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -9,14 +9,21 @@ import {
|
|||
Axis,
|
||||
BubbleSeries,
|
||||
Chart,
|
||||
ElementClickListener,
|
||||
GeometryValue,
|
||||
Position,
|
||||
ScaleType,
|
||||
Settings,
|
||||
TooltipInfo,
|
||||
TooltipProps,
|
||||
TooltipType,
|
||||
} from '@elastic/charts';
|
||||
import { EuiPanel, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useChartTheme } from '../../../../../../observability/public';
|
||||
import { SERVICE_NODE_NAME } from '../../../../../common/elasticsearch_fieldnames';
|
||||
import {
|
||||
asTransactionRate,
|
||||
getDurationFormatter,
|
||||
|
@ -24,10 +31,12 @@ import {
|
|||
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
|
||||
import { useTheme } from '../../../../hooks/use_theme';
|
||||
import { PrimaryStatsServiceInstanceItem } from '../../../app/service_overview/service_overview_instances_chart_and_table';
|
||||
import * as urlHelpers from '../../Links/url_helpers';
|
||||
import { ChartContainer } from '../chart_container';
|
||||
import { getResponseTimeTickFormatter } from '../transaction_charts/helper';
|
||||
import { CustomTooltip } from './custom_tooltip';
|
||||
|
||||
interface InstancesLatencyDistributionChartProps {
|
||||
export interface InstancesLatencyDistributionChartProps {
|
||||
height: number;
|
||||
items?: PrimaryStatsServiceInstanceItem[];
|
||||
status: FETCH_STATUS;
|
||||
|
@ -38,6 +47,7 @@ export function InstancesLatencyDistributionChart({
|
|||
items = [],
|
||||
status,
|
||||
}: InstancesLatencyDistributionChartProps) {
|
||||
const history = useHistory();
|
||||
const hasData = items.length > 0;
|
||||
|
||||
const theme = useTheme();
|
||||
|
@ -51,6 +61,43 @@ export function InstancesLatencyDistributionChart({
|
|||
const maxLatency = Math.max(...items.map((item) => item.latency ?? 0));
|
||||
const latencyFormatter = getDurationFormatter(maxLatency);
|
||||
|
||||
const tooltip: TooltipProps = {
|
||||
type: TooltipType.Follow,
|
||||
snap: false,
|
||||
customTooltip: (props: TooltipInfo) => (
|
||||
<CustomTooltip {...props} latencyFormatter={latencyFormatter} />
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle click events on the items.
|
||||
*
|
||||
* Due to how we handle filtering by using the kuery bar, it's difficult to
|
||||
* modify existing queries. If you have an existing query in the bar, this will
|
||||
* wipe it out. This is ok for now, since we probably will be replacing this
|
||||
* interaction with something nicer in a future release.
|
||||
*
|
||||
* The event object has an array two items for each point, one of which has
|
||||
* the serviceNodeName, so we flatten the list and get the items we need to
|
||||
* form a query.
|
||||
*/
|
||||
const handleElementClick: ElementClickListener = (event) => {
|
||||
const serviceNodeNamesQuery = event
|
||||
.flat()
|
||||
.flatMap((value) => (value as GeometryValue).datum?.serviceNodeName)
|
||||
.filter((serviceNodeName) => !!serviceNodeName)
|
||||
.map((serviceNodeName) => `${SERVICE_NODE_NAME}:"${serviceNodeName}"`)
|
||||
.join(' OR ');
|
||||
|
||||
urlHelpers.push(history, { query: { kuery: serviceNodeNamesQuery } });
|
||||
};
|
||||
|
||||
// With a linear scale, if all the instances have similar throughput (or if
|
||||
// there's just a single instance) they'll show along the origin. Make sure
|
||||
// the x-axis domain is [0, maxThroughput].
|
||||
const maxThroughput = Math.max(...items.map((item) => item.throughput ?? 0));
|
||||
const xDomain = { min: 0, max: maxThroughput };
|
||||
|
||||
return (
|
||||
<EuiPanel>
|
||||
<EuiTitle size="xs">
|
||||
|
@ -64,9 +111,11 @@ export function InstancesLatencyDistributionChart({
|
|||
<Chart id="instances-latency-distribution">
|
||||
<Settings
|
||||
legendPosition={Position.Bottom}
|
||||
tooltip="none"
|
||||
onElementClick={handleElementClick}
|
||||
tooltip={tooltip}
|
||||
showLegend
|
||||
theme={chartTheme}
|
||||
xDomain={xDomain}
|
||||
/>
|
||||
<BubbleSeries
|
||||
color={theme.eui.euiColorVis1}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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, { ComponentType } from 'react';
|
||||
import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
|
||||
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
|
||||
import {
|
||||
InstancesLatencyDistributionChart,
|
||||
InstancesLatencyDistributionChartProps,
|
||||
} from './';
|
||||
|
||||
export default {
|
||||
title: 'shared/charts/InstancesLatencyDistributionChart',
|
||||
component: InstancesLatencyDistributionChart,
|
||||
decorators: [
|
||||
(Story: ComponentType) => (
|
||||
<EuiThemeProvider>
|
||||
<Story />
|
||||
</EuiThemeProvider>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export function Example({ items }: InstancesLatencyDistributionChartProps) {
|
||||
return (
|
||||
<InstancesLatencyDistributionChart
|
||||
height={300}
|
||||
items={items}
|
||||
status={FETCH_STATUS.SUCCESS}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Example.args = {
|
||||
items: [
|
||||
{
|
||||
serviceNodeName:
|
||||
'3f67bfc39c7891dc0c5657befb17bf58c19cf10f99472cf8df263c8e5bb1c766',
|
||||
latency: 15802930.92133213,
|
||||
throughput: 0.4019360641691481,
|
||||
},
|
||||
{
|
||||
serviceNodeName:
|
||||
'd52c64bea9327f3e960ac1cb63c1b7ea922e3cb3d76ab9b254e57a7cb2f760a0',
|
||||
latency: 8296442.578550679,
|
||||
throughput: 0.3932978392703585,
|
||||
},
|
||||
{
|
||||
serviceNodeName:
|
||||
'797e0a906ad342223468ca51b663e1af8bdeb40bab376c46c7f7fa2021349290',
|
||||
latency: 34842576.51204916,
|
||||
throughput: 0.3353931699532713,
|
||||
},
|
||||
{
|
||||
serviceNodeName:
|
||||
'21e1c648bd73434a8a1bf6e849817930e8b43eacf73a5c39c30520ee3b79d8c0',
|
||||
latency: 40713854.354498595,
|
||||
throughput: 0.32947224189485164,
|
||||
},
|
||||
{
|
||||
serviceNodeName:
|
||||
'a1c99c8675372af4c74bb01cc48e75989faa6f010a4ccb027df1c410dde0c72c',
|
||||
latency: 18565471.348388012,
|
||||
throughput: 0.3261219384041683,
|
||||
},
|
||||
{
|
||||
serviceNodeName: '_service_node_name_missing_',
|
||||
latency: 20065471.348388012,
|
||||
throughput: 0.3261219384041683,
|
||||
},
|
||||
],
|
||||
} as InstancesLatencyDistributionChartProps;
|
||||
|
||||
export function SimilarThroughputInstances({
|
||||
items,
|
||||
}: InstancesLatencyDistributionChartProps) {
|
||||
return (
|
||||
<InstancesLatencyDistributionChart
|
||||
height={300}
|
||||
items={items}
|
||||
status={FETCH_STATUS.SUCCESS}
|
||||
/>
|
||||
);
|
||||
}
|
||||
SimilarThroughputInstances.args = {
|
||||
items: [
|
||||
{
|
||||
serviceNodeName:
|
||||
'21e1c648bd73434a8a1bf6e849817930e8b43eacf73a5c39c30520ee3b79d8c0',
|
||||
latency: 40713854.354498595,
|
||||
throughput: 0.3261219384041683,
|
||||
},
|
||||
{
|
||||
serviceNodeName:
|
||||
'a1c99c8675372af4c74bb01cc48e75989faa6f010a4ccb027df1c410dde0c72c',
|
||||
latency: 18565471.348388012,
|
||||
throughput: 0.3261219384041683,
|
||||
},
|
||||
{
|
||||
serviceNodeName: '_service_node_name_missing_',
|
||||
latency: 20065471.348388012,
|
||||
throughput: 0.3261219384041683,
|
||||
},
|
||||
],
|
||||
} as InstancesLatencyDistributionChartProps;
|
Loading…
Add table
Add a link
Reference in a new issue