Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[APM] Adding comparison to transactions table #91435

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ export function ServiceOverview({
return (
<AnnotationsContextProvider>
<ChartPointerEventContextProvider>
<SearchBar prepend={transactionTypeLabel} showCorrelations />
<SearchBar
prepend={transactionTypeLabel}
showCorrelations
showTimeComparison
/>
<EuiPage>
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { EuiBasicTableColumn } from '@elastic/eui';
import { EuiBasicTableColumn, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { ValuesType } from 'utility-types';
Expand Down Expand Up @@ -34,10 +34,12 @@ export function getColumns({
serviceName,
latencyAggregationType,
transactionGroupComparisonStatistics,
comparisonEnabled,
}: {
serviceName: string;
latencyAggregationType?: LatencyAggregationType;
transactionGroupComparisonStatistics?: TransactionGroupComparisonStatistics;
comparisonEnabled?: boolean;
}): Array<EuiBasicTableColumn<ServiceTransactionGroupItem>> {
return [
{
Expand Down Expand Up @@ -71,13 +73,18 @@ export function getColumns({
name: getLatencyColumnLabel(latencyAggregationType),
width: px(unit * 10),
render: (_, { latency, name }) => {
const timeseries =
transactionGroupComparisonStatistics?.[name]?.latency;
const currentTimeseries =
transactionGroupComparisonStatistics?.currentPeriod?.[name]?.latency;
const previousTimeseries =
transactionGroupComparisonStatistics?.previousPeriod?.[name]?.latency;
return (
<SparkPlot
color="euiColorVis1"
compact
series={timeseries}
series={currentTimeseries}
comparisonSeries={
comparisonEnabled ? previousTimeseries : undefined
}
valueLabel={asMillisecondDuration(latency)}
/>
);
Expand All @@ -92,13 +99,20 @@ export function getColumns({
),
width: px(unit * 10),
render: (_, { throughput, name }) => {
const timeseries =
transactionGroupComparisonStatistics?.[name]?.throughput;
const currentTimeseries =
transactionGroupComparisonStatistics?.currentPeriod?.[name]
?.throughput;
const previousTimeseries =
transactionGroupComparisonStatistics?.previousPeriod?.[name]
?.throughput;
return (
<SparkPlot
color="euiColorVis0"
compact
series={timeseries}
series={currentTimeseries}
comparisonSeries={
comparisonEnabled ? previousTimeseries : undefined
}
valueLabel={asTransactionRate(throughput)}
/>
);
Expand All @@ -113,13 +127,20 @@ export function getColumns({
),
width: px(unit * 8),
render: (_, { errorRate, name }) => {
const timeseries =
transactionGroupComparisonStatistics?.[name]?.errorRate;
const currentTimeseries =
transactionGroupComparisonStatistics?.currentPeriod?.[name]
?.errorRate;
const previousTimeseries =
transactionGroupComparisonStatistics?.previousPeriod?.[name]
?.errorRate;
return (
<SparkPlot
color="euiColorVis7"
compact
series={timeseries}
series={currentTimeseries}
comparisonSeries={
comparisonEnabled ? previousTimeseries : undefined
}
valueLabel={asPercent(errorRate, 1)}
/>
);
Expand All @@ -134,9 +155,22 @@ export function getColumns({
),
width: px(unit * 5),
render: (_, { name }) => {
const impact =
transactionGroupComparisonStatistics?.[name]?.impact ?? 0;
return <ImpactBar value={impact} size="m" />;
const currenIimpact =
transactionGroupComparisonStatistics?.currentPeriod?.[name]?.impact ??
0;
const previousIimpact =
transactionGroupComparisonStatistics?.previousPeriod?.[name]
?.impact ?? 0;
return (
<EuiFlexGroup gutterSize="xs" direction="column">
<EuiFlexItem>
<ImpactBar value={currenIimpact} size="m" />
</EuiFlexItem>
<EuiFlexItem>
<ImpactBar value={previousIimpact} size="s" color="subdued" />
</EuiFlexItem>
</EuiFlexGroup>
);
},
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useUrlParams } from '../../../../context/url_params_context/use_url_par
import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher';
import { TransactionOverviewLink } from '../../../shared/Links/apm/transaction_overview_link';
import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper';
import { getTimeRangeComparison } from '../../../shared/time_comparison/get_time_range_comparison';
import { ServiceOverviewTableContainer } from '../service_overview_table_container';
import { getColumns } from './get_columns';

Expand Down Expand Up @@ -54,12 +55,27 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
});

const { pageIndex, sort } = tableOptions;
const { direction, field } = sort;

const { transactionType } = useApmServiceContext();
const {
urlParams: { environment, kuery, start, end, latencyAggregationType },
urlParams: {
start,
end,
latencyAggregationType,
comparisonType,
comparisonEnabled,
environment,
kuery,
},
} = useUrlParams();

const { comparisonStart, comparisonEnd } = getTimeRangeComparison({
start,
end,
comparisonType,
});

const { data = INITIAL_STATE, status } = useFetcher(
(callApmApi) => {
if (!start || !end || !latencyAggregationType || !transactionType) {
Expand All @@ -81,11 +97,13 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
},
}).then((response) => {
return {
// Everytime the primary statistics is refetched, updates the requestId making the comparison API to be refetched.
requestId: uuid(),
...response,
};
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
environment,
kuery,
Expand All @@ -94,14 +112,18 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
end,
transactionType,
latencyAggregationType,
comparisonType,
pageIndex,
direction,
field,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be worth trying to figure out if we can move calculating the visible transaction groups into the useFetcher call as well, to see if we no longer need to disable react-hooks/exhaustive-deps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dgieselaar I can calculate the visible transaction groups inside useFetcher, but I'll still need the react-hooks/exhaustive-deps, because comparisonType needs to be listed on its dependencies, even though it is not used, to trigger the comparison API call when it's changed.

]
);

const { transactionGroups, requestId } = data;
const currentPageTransactionGroups = orderBy(
transactionGroups,
sort.field,
sort.direction
field,
direction
).slice(pageIndex * PAGE_SIZE, (pageIndex + 1) * PAGE_SIZE);

const transactionNames = JSON.stringify(
Expand All @@ -114,6 +136,7 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
} = useFetcher(
(callApmApi) => {
if (
status !== FETCH_STATUS.LOADING &&
currentPageTransactionGroups.length &&
start &&
end &&
Expand All @@ -134,21 +157,24 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
transactionType,
latencyAggregationType,
transactionNames,
comparisonStart,
comparisonEnd,
},
},
});
}
},
// only fetches statistics when requestId changes or transaction names changes
// only fetches comparison statistics when requestId is invalidated by primary statistics api call
// eslint-disable-next-line react-hooks/exhaustive-deps
[requestId, transactionNames],
[requestId],
{ preservePreviousData: false }
);

const columns = getColumns({
serviceName,
latencyAggregationType,
transactionGroupComparisonStatistics,
comparisonEnabled,
});

const isLoading =
Expand All @@ -162,13 +188,6 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
hidePerPageOptions: true,
};

const sorting = {
sort: {
field: sort.field,
direction: sort.direction,
},
};

return (
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem>
Expand Down Expand Up @@ -211,7 +230,7 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
items={currentPageTransactionGroups}
columns={columns}
pagination={pagination}
sorting={sorting}
sorting={{ sort: { field, direction } }}
onChange={(newTableOptions: {
page?: {
index: number;
Expand Down
12 changes: 4 additions & 8 deletions x-pack/plugins/apm/public/components/shared/ImpactBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,19 @@ import React from 'react';
// TODO: extend from EUI's EuiProgress prop interface
export interface ImpactBarProps extends Record<string, unknown> {
value: number;
size?: 'l' | 'm';
size?: 's' | 'l' | 'm';
max?: number;
color?: string;
}

export function ImpactBar({
value,
size = 'l',
max = 100,
color = 'primary',
...rest
}: ImpactBarProps) {
return (
<EuiProgress
size={size}
value={value}
max={max}
color="primary"
{...rest}
/>
<EuiProgress size={size} value={value} max={max} color={color} {...rest} />
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
AreaSeries,
Chart,
CurveType,
LineSeries,
ScaleType,
Settings,
} from '@elastic/charts';
Expand All @@ -20,8 +21,9 @@ import { Coordinate } from '../../../../../typings/timeseries';
import { useChartTheme } from '../../../../../../observability/public';
import { px, unit } from '../../../../style/variables';
import { useTheme } from '../../../../hooks/use_theme';
import { getComparisonChartTheme } from '../../time_comparison/get_time_range_comparison';

type Color =
export type Color =
| 'euiColorVis0'
| 'euiColorVis1'
| 'euiColorVis2'
Expand All @@ -33,19 +35,27 @@ type Color =
| 'euiColorVis8'
| 'euiColorVis9';

function isEmptyTimeseries(series: Coordinate[]) {
return series.every((point) => point.y === null);
}

export function SparkPlot({
color,
series,
comparisonSeries = [],
valueLabel,
compact,
}: {
color: Color;
series?: Coordinate[] | null;
valueLabel: React.ReactNode;
compact?: boolean;
comparisonSeries?: Coordinate[];
}) {
const theme = useTheme();
const defaultChartTheme = useChartTheme();
const comparisonChartTheme = getComparisonChartTheme(theme);
const hasComparisonSeries = !!comparisonSeries?.length;

const sparkplotChartTheme = merge({}, defaultChartTheme, {
chartMargins: { left: 0, right: 0, top: 0, bottom: 0 },
Expand All @@ -55,6 +65,7 @@ export function SparkPlot({
areaSeriesStyle: {
point: { opacity: 0 },
},
...(hasComparisonSeries ? comparisonChartTheme : {}),
});

const colorValue = theme.eui[color];
Expand All @@ -64,10 +75,12 @@ export function SparkPlot({
width: compact ? px(unit * 3) : px(unit * 4),
};

const SparklinesSeries = hasComparisonSeries ? LineSeries : AreaSeries;

return (
<EuiFlexGroup gutterSize="m" responsive={false}>
<EuiFlexItem grow={false}>
{!series || series.every((point) => point.y === null) ? (
{!series || isEmptyTimeseries(series) ? (
<div
style={{
...chartSize,
Expand All @@ -85,8 +98,8 @@ export function SparkPlot({
showLegend={false}
tooltip="none"
/>
<AreaSeries
id="area"
<SparklinesSeries
id="sparklinesSeries"
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor={'x'}
Expand All @@ -95,6 +108,18 @@ export function SparkPlot({
color={colorValue}
curve={CurveType.CURVE_MONOTONE_X}
/>
{hasComparisonSeries && (
<AreaSeries
id="comparisonSeries"
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor={'x'}
yAccessors={['y']}
data={comparisonSeries}
color={theme.eui.euiColorLightestShade}
curve={CurveType.CURVE_MONOTONE_X}
/>
)}
</Chart>
)}
</EuiFlexItem>
Expand Down
Loading