Skip to content

Commit

Permalink
[APM] Correlations polish (#85116)
Browse files Browse the repository at this point in the history
Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
sorenlouv and kibanamachine authored Dec 15, 2020
1 parent 845f716 commit 20638a6
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import {
} from '@elastic/charts';
import React, { useState } from 'react';
import { useParams } from 'react-router-dom';
import { EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import {
EuiTitle,
EuiFlexGroup,
EuiFlexItem,
EuiComboBox,
EuiAccordion,
} from '@elastic/eui';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';
import {
Expand All @@ -35,12 +41,26 @@ type SignificantTerm = NonNullable<
CorrelationsApiResponse['significantTerms']
>[0];

const initialFieldNames = [
'transaction.name',
'user.username',
'user.id',
'host.ip',
'user_agent.name',
'kubernetes.pod.uuid',
'kubernetes.pod.name',
'url.domain',
'container.id',
'service.node.name',
].map((label) => ({ label }));

export function ErrorCorrelations() {
const [
selectedSignificantTerm,
setSelectedSignificantTerm,
] = useState<SignificantTerm | null>(null);

const [fieldNames, setFieldNames] = useState(initialFieldNames);
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
const { transactionName, transactionType, start, end } = urlParams;
Expand All @@ -57,13 +77,20 @@ export function ErrorCorrelations() {
start,
end,
uiFilters: JSON.stringify(uiFilters),
fieldNames:
'transaction.name,user.username,user.id,host.ip,user_agent.name,kubernetes.pod.uuid,kubernetes.pod.name,url.domain,container.id,service.node.name',
fieldNames: fieldNames.map((field) => field.label).join(','),
},
},
});
}
}, [serviceName, start, end, transactionName, transactionType, uiFilters]);
}, [
serviceName,
start,
end,
transactionName,
transactionType,
uiFilters,
fieldNames,
]);

return (
<>
Expand All @@ -72,14 +99,30 @@ export function ErrorCorrelations() {
<EuiTitle size="s">
<h4>Error rate over time</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem>
<ErrorTimeseriesChart
data={data}
status={status}
selectedSignificantTerm={selectedSignificantTerm}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiAccordion id="accordion" buttonContent="Customize">
<EuiComboBox
fullWidth={true}
placeholder="Select or create options"
selectedOptions={fieldNames}
onChange={setFieldNames}
onCreateOption={(term) =>
setFieldNames((names) => [...names, { label: term }])
}
/>
</EuiAccordion>
</EuiFlexItem>
<EuiFlexItem>
<SignificantTermsTable
cardinalityColumnName="# of failed transactions"
significantTerms={data?.significantTerms}
status={status}
setSelectedSignificantTerm={setSelectedSignificantTerm}
Expand All @@ -103,7 +146,7 @@ function ErrorTimeseriesChart({

return (
<ChartContainer height={200} hasData={!!data} status={status}>
<Chart size={{ height: px(200), width: px(600) }}>
<Chart size={{ height: px(200), width: '100%' }}>
<Settings showLegend legendPosition={Position.Bottom} />

<Axis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@
import {
ScaleType,
Chart,
LineSeries,
Axis,
CurveType,
BarSeries,
Position,
timeFormatter,
Settings,
} from '@elastic/charts';
import React, { useState } from 'react';
import { useParams } from 'react-router-dom';
import { EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import {
EuiTitle,
EuiFlexGroup,
EuiFlexItem,
EuiComboBox,
EuiAccordion,
EuiFormRow,
EuiFieldNumber,
} from '@elastic/eui';
import { getDurationFormatter } from '../../../../common/utils/formatters';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';
Expand All @@ -36,12 +41,26 @@ type SignificantTerm = NonNullable<
CorrelationsApiResponse['significantTerms']
>[0];

const initialFieldNames = [
'user.username',
'user.id',
'host.ip',
'user_agent.name',
'kubernetes.pod.uuid',
'kubernetes.pod.name',
'url.domain',
'container.id',
'service.node.name',
].map((label) => ({ label }));

export function LatencyCorrelations() {
const [
selectedSignificantTerm,
setSelectedSignificantTerm,
] = useState<SignificantTerm | null>(null);

const [fieldNames, setFieldNames] = useState(initialFieldNames);
const [durationPercentile, setDurationPercentile] = useState('50');
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
const { transactionName, transactionType, start, end } = urlParams;
Expand All @@ -58,30 +77,28 @@ export function LatencyCorrelations() {
start,
end,
uiFilters: JSON.stringify(uiFilters),
durationPercentile: '50',
fieldNames:
'user.username,user.id,host.ip,user_agent.name,kubernetes.pod.uuid,kubernetes.pod.name,url.domain,container.id,service.node.name',
durationPercentile,
fieldNames: fieldNames.map((field) => field.label).join(','),
},
},
});
}
}, [serviceName, start, end, transactionName, transactionType, uiFilters]);
}, [
serviceName,
start,
end,
transactionName,
transactionType,
uiFilters,
durationPercentile,
fieldNames,
]);

return (
<>
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiFlexGroup direction="row">
<EuiFlexItem>
<EuiTitle size="s">
<h4>Average latency over time</h4>
</EuiTitle>
<LatencyTimeseriesChart
data={data}
status={status}
selectedSignificantTerm={selectedSignificantTerm}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="s">
<h4>Latency distribution</h4>
Expand All @@ -94,8 +111,42 @@ export function LatencyCorrelations() {
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiAccordion id="accordion" buttonContent="Customize">
<EuiFlexGroup>
<EuiFlexItem grow={1}>
<EuiFormRow label="Threshold">
<EuiFieldNumber
value={durationPercentile}
onChange={(e) =>
setDurationPercentile(e.currentTarget.value)
}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={4}>
<EuiFormRow
fullWidth={true}
label="Field"
helpText="Fields to analyse for significant terms"
>
<EuiComboBox
fullWidth={true}
placeholder="Select or create options"
selectedOptions={fieldNames}
onChange={setFieldNames}
onCreateOption={(term) => {
setFieldNames((names) => [...names, { label: term }]);
}}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</EuiAccordion>
</EuiFlexItem>
<EuiFlexItem>
<SignificantTermsTable
cardinalityColumnName="# of slow transactions"
significantTerms={data?.significantTerms}
status={status}
setSelectedSignificantTerm={setSelectedSignificantTerm}
Expand All @@ -106,20 +157,6 @@ export function LatencyCorrelations() {
);
}

function getTimeseriesYMax(data?: CorrelationsApiResponse) {
if (!data?.overall) {
return 0;
}

const yValues = [
...data.overall.timeseries.map((p) => p.y ?? 0),
...data.significantTerms.flatMap((term) =>
term.timeseries.map((p) => p.y ?? 0)
),
];
return Math.max(...yValues);
}

function getDistributionYMax(data?: CorrelationsApiResponse) {
if (!data?.overall) {
return 0;
Expand All @@ -134,65 +171,6 @@ function getDistributionYMax(data?: CorrelationsApiResponse) {
return Math.max(...yValues);
}

function LatencyTimeseriesChart({
data,
selectedSignificantTerm,
status,
}: {
data?: CorrelationsApiResponse;
selectedSignificantTerm: SignificantTerm | null;
status: FETCH_STATUS;
}) {
const dateFormatter = timeFormatter('HH:mm:ss');

const yMax = getTimeseriesYMax(data);
const durationFormatter = getDurationFormatter(yMax);

return (
<ChartContainer height={200} hasData={!!data} status={status}>
<Chart>
<Settings showLegend legendPosition={Position.Bottom} />

<Axis
id="bottom"
position={Position.Bottom}
showOverlappingTicks
tickFormat={dateFormatter}
/>
<Axis
id="left"
position={Position.Left}
domain={{ min: 0, max: yMax }}
tickFormat={(d) => durationFormatter(d).formatted}
/>

<LineSeries
id="Overall latency"
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor={'x'}
yAccessors={['y']}
data={data?.overall?.timeseries || []}
curve={CurveType.CURVE_MONOTONE_X}
/>

{selectedSignificantTerm !== null ? (
<LineSeries
id="Latency for selected term"
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor={'x'}
yAccessors={['y']}
color="red"
data={selectedSignificantTerm.timeseries}
curve={CurveType.CURVE_MONOTONE_X}
/>
) : null}
</Chart>
</ChartContainer>
);
}

function LatencyDistributionChart({
data,
selectedSignificantTerm,
Expand Down
Loading

0 comments on commit 20638a6

Please sign in to comment.