Skip to content

Commit f213bbb

Browse files
sthobisTackAdam
andauthored
Traces - Add traces unique count based on cardinality aggregation (#2435)
* Add traces unique count based on cardinality aggregation Signed-off-by: Thobi Sinaga <[email protected]> * Remove root spans count query on traces table Signed-off-by: Thobi Sinaga <[email protected]> * Update traces table warning message Signed-off-by: Thobi Sinaga <[email protected]> * Update warning message wordings Match data_prepper warning message to custom_data_prepper Fix datagrid pagination/empty state on traces mode Signed-off-by: Thobi Sinaga <[email protected]> * Remove redundant row count from warning text Signed-off-by: Thobi Sinaga <[email protected]> * Keep traces related props optional on custom datagrid Signed-off-by: Thobi Sinaga <[email protected]> --------- Signed-off-by: Thobi Sinaga <[email protected]> Co-authored-by: Adam Tackett <[email protected]>
1 parent b39e708 commit f213bbb

File tree

9 files changed

+109
-118
lines changed

9 files changed

+109
-118
lines changed

public/components/trace_analytics/components/common/constants.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export const NANOS_TO_MS = 1e6;
88

99
export const MILI_TO_SEC = 1000;
1010

11+
export const MAX_DISPLAY_ROWS = 10000;
12+
1113
export const pieChartColors = [
1214
'#7492e7',
1315
'#c33d69',

public/components/trace_analytics/components/common/shared_components/component_helper_functions.tsx

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@
55

66
import { useEffect } from 'react';
77

8-
// Injects the size warning and Load buttons into the corners of EUI Data Grid
8+
// Injects Size warning and Load more button into the corners of EUI Data Grid
9+
// 1. When rowCount > maxDisplayRows, show warning (some data is hidden)
10+
// 2. When rowCount < totalCount, show warning (more data to load), and Load more button when isLastPage is true
911
export const useInjectElementsIntoGrid = (
1012
rowCount: number,
1113
maxDisplayRows: number,
12-
tracesTableMode: string,
14+
totalCount: number,
1315
loadMoreHandler?: () => void,
14-
maxTraces?: number,
1516
isLastPage?: boolean
1617
) => {
1718
useEffect(() => {
1819
setTimeout(() => {
19-
const toolbar = document.querySelector<HTMLElement>('.euiDataGrid__controls');
20-
21-
const shouldShowWarning =
22-
(tracesTableMode === 'traces' && maxTraces != null && maxTraces < rowCount) ||
23-
(tracesTableMode !== 'traces' && rowCount > maxDisplayRows);
20+
const moreToShow = rowCount > maxDisplayRows;
21+
const moreToLoad = rowCount < totalCount;
22+
const shouldShowWarning = moreToShow || moreToLoad;
2423

24+
const toolbar = document.querySelector<HTMLElement>('.euiDataGrid__controls');
2525
if (toolbar) {
2626
const existingWarning = toolbar.querySelector('.trace-table-warning');
2727
if (existingWarning) {
@@ -36,14 +36,9 @@ export const useInjectElementsIntoGrid = (
3636
const warningDiv = document.createElement('div');
3737
warningDiv.className = 'trace-table-warning';
3838

39-
const strongElement = document.createElement('strong');
40-
strongElement.textContent =
41-
tracesTableMode === 'traces' ? `${maxTraces ?? maxDisplayRows}` : `${maxDisplayRows}`;
42-
4339
const textSpan = document.createElement('span');
44-
textSpan.appendChild(strongElement);
45-
textSpan.appendChild(document.createTextNode(' results shown out of '));
46-
textSpan.appendChild(document.createTextNode(` ${rowCount}`));
40+
const totalCountText = totalCount > maxDisplayRows ? `${maxDisplayRows}+` : totalCount;
41+
textSpan.appendChild(document.createTextNode(`Results out of ${totalCountText}`));
4742

4843
warningDiv.appendChild(textSpan);
4944

@@ -52,37 +47,32 @@ export const useInjectElementsIntoGrid = (
5247
}
5348

5449
const pagination = document.querySelector<HTMLElement>('.euiDataGrid__pagination');
55-
const existingLoadMoreButton = pagination?.querySelector('.trace-table-load-more');
50+
if (pagination) {
51+
pagination.style.display = 'flex';
52+
pagination.style.alignItems = 'center';
53+
pagination.style.justifyContent = 'space-between';
5654

57-
if (tracesTableMode !== 'traces' || !pagination || !loadMoreHandler || !isLastPage) {
55+
const existingLoadMoreButton = pagination.querySelector('.trace-table-load-more');
5856
if (existingLoadMoreButton) {
5957
existingLoadMoreButton.remove();
6058
}
61-
return;
62-
}
63-
64-
pagination.style.display = 'flex';
65-
pagination.style.alignItems = 'center';
66-
pagination.style.justifyContent = 'space-between';
67-
68-
let loadMoreButton = pagination.querySelector<HTMLElement>('.trace-table-load-more');
69-
if (!loadMoreButton) {
70-
loadMoreButton = document.createElement('button');
71-
loadMoreButton.className = 'trace-table-load-more euiButtonEmpty euiButtonEmpty--text';
72-
loadMoreButton.style.marginLeft = '12px';
73-
loadMoreButton.innerText = '... Load more';
74-
75-
loadMoreButton.onclick = () => loadMoreHandler();
76-
77-
const paginationList = pagination.querySelector('.euiPagination__list');
7859

79-
if (paginationList) {
80-
const listItem = document.createElement('li');
81-
listItem.className = 'euiPagination__item trace-table-load-more';
82-
listItem.appendChild(loadMoreButton);
83-
paginationList.appendChild(listItem);
60+
if (moreToLoad && loadMoreHandler && isLastPage) {
61+
const loadMoreButton = document.createElement('button');
62+
loadMoreButton.className = 'trace-table-load-more euiButtonEmpty euiButtonEmpty--text';
63+
loadMoreButton.style.marginLeft = '12px';
64+
loadMoreButton.innerText = '... Load more';
65+
loadMoreButton.onclick = () => loadMoreHandler();
66+
67+
const paginationList = pagination.querySelector('.euiPagination__list');
68+
if (paginationList) {
69+
const listItem = document.createElement('li');
70+
listItem.className = 'euiPagination__item trace-table-load-more';
71+
listItem.appendChild(loadMoreButton);
72+
paginationList.appendChild(listItem);
73+
}
8474
}
8575
}
8676
}, 100);
87-
}, [rowCount, tracesTableMode, loadMoreHandler, maxTraces, maxDisplayRows, isLastPage]);
77+
}, [rowCount, maxDisplayRows, totalCount, loadMoreHandler, isLastPage]);
8878
};

public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
EuiTextColor,
2323
} from '@elastic/eui';
2424
import { i18n } from '@osd/i18n';
25-
import React, { useEffect, useMemo, useState } from 'react';
25+
import React, { useMemo, useState } from 'react';
2626
import { NoMatchMessage } from '../helper_functions';
2727
import {
2828
TRACE_TABLE_OPTIONS,
@@ -31,8 +31,8 @@ import {
3131
} from '../../../../../../common/constants/trace_analytics';
3232
import { useInjectElementsIntoGrid } from './component_helper_functions';
3333
import { uiSettingsService } from '../../../../../../common/utils';
34+
import { MAX_DISPLAY_ROWS } from '../constants';
3435

35-
const MAX_DISPLAY_ROWS = 10000;
3636
interface FullScreenWrapperProps {
3737
children: React.ReactNode;
3838
onClose: () => void;
@@ -92,9 +92,22 @@ interface RenderCustomDataGridParams {
9292
tracesTableMode?: string;
9393
setTracesTableMode?: (mode: string) => void;
9494
maxTraces?: number;
95-
setMaxTraces?: (max: number) => void;
95+
setMaxTraces?: React.Dispatch<React.SetStateAction<number>>;
96+
uniqueTraces?: number;
9697
}
9798

99+
/**
100+
* A custom data grid component that supports two distinct modes of operation:
101+
* 1. All spans mode (all_spans, root_spans, entry_spans): Displays all rows at once, limited to MAX_DISPLAY_ROWS
102+
* 2. Traces mode: Implements lazy loading by initially limiting rows to maxTraces, with ability to load more
103+
* traces incrementally up to MAX_DISPLAY_ROWS
104+
*
105+
* Key properties:
106+
* - tracesTableMode: Current mode of the table ('traces', 'all_spans', 'root_spans', or 'entry_spans')
107+
* - rowCount: Total number of hits returned from the query for all_spans, root_spans, entry_spans modes (0 if traces mode)
108+
* - uniqueTraces: Total number of unique traces returned from the query when in traces mode (0 if all_spans, root_spans, or entry_spans mode)
109+
* - maxTraces: Current number of traces is being displayed in the grid (can be increased to load more traces)
110+
*/
98111
export const RenderCustomDataGrid: React.FC<RenderCustomDataGridParams> = ({
99112
columns,
100113
renderCellValue,
@@ -110,8 +123,9 @@ export const RenderCustomDataGrid: React.FC<RenderCustomDataGridParams> = ({
110123
isTableDataLoading,
111124
tracesTableMode,
112125
setTracesTableMode,
113-
maxTraces,
126+
maxTraces = 0,
114127
setMaxTraces,
128+
uniqueTraces = 0,
115129
}) => {
116130
const defaultVisibleColumns = useMemo(() => {
117131
return columns
@@ -126,8 +140,11 @@ export const RenderCustomDataGrid: React.FC<RenderCustomDataGridParams> = ({
126140
const [isFullScreen, setIsFullScreen] = useState(fullScreen);
127141
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
128142

129-
const displayedRowCount =
130-
tracesTableMode === 'traces' ? maxTraces : Math.min(rowCount, MAX_DISPLAY_ROWS);
143+
const isTracesMode = tracesTableMode === 'traces';
144+
const isEmpty = isTracesMode ? uniqueTraces === 0 : rowCount === 0;
145+
const displayedRowCount = isTracesMode
146+
? Math.min(maxTraces, uniqueTraces, MAX_DISPLAY_ROWS)
147+
: Math.min(rowCount, MAX_DISPLAY_ROWS);
131148

132149
const isDarkMode = uiSettingsService.get('theme:darkMode');
133150

@@ -137,39 +154,25 @@ export const RenderCustomDataGrid: React.FC<RenderCustomDataGridParams> = ({
137154
)
138155
: [];
139156

140-
const isLastPage =
141-
tracesTableMode === 'traces' && pagination?.pageSize && maxTraces != null
142-
? (pagination.pageIndex + 1) * pagination.pageSize >= maxTraces &&
143-
rowCount > maxTraces &&
144-
maxTraces < MAX_DISPLAY_ROWS
145-
: false;
146-
147-
// Used for when trace count less than default maxTraces
148-
useEffect(() => {
149-
if (
150-
tracesTableMode === 'traces' &&
151-
rowCount > 0 &&
152-
rowCount < maxTraces &&
153-
maxTraces !== rowCount
154-
) {
155-
setMaxTraces(rowCount);
156-
}
157-
}, [rowCount, maxTraces, tracesTableMode]);
158-
159-
const incrementTraceCount = () => {
160-
const nextIncrement = Math.min(500, rowCount - maxTraces);
161-
if (nextIncrement > 0 && maxTraces < MAX_DISPLAY_ROWS) {
162-
setMaxTraces((prevMax) => Math.min(prevMax + nextIncrement, MAX_DISPLAY_ROWS));
163-
}
164-
};
157+
const isTracesModeLastPage = Boolean(
158+
pagination?.pageSize &&
159+
(pagination.pageIndex + 1) * pagination.pageSize >= maxTraces &&
160+
uniqueTraces > maxTraces &&
161+
maxTraces < MAX_DISPLAY_ROWS
162+
);
165163

166164
useInjectElementsIntoGrid(
167-
rowCount,
165+
displayedRowCount,
168166
MAX_DISPLAY_ROWS,
169-
tracesTableMode ?? '',
170-
incrementTraceCount,
171-
maxTraces,
172-
isLastPage
167+
isTracesMode ? uniqueTraces : rowCount,
168+
isTracesMode
169+
? () => {
170+
setMaxTraces?.((prevMax: number) =>
171+
Math.min(prevMax + 500, uniqueTraces, MAX_DISPLAY_ROWS)
172+
);
173+
}
174+
: undefined,
175+
isTracesMode ? isTracesModeLastPage : false
173176
);
174177

175178
const disableInteractions = useMemo(() => isFullScreen, [isFullScreen]);
@@ -287,7 +290,7 @@ export const RenderCustomDataGrid: React.FC<RenderCustomDataGridParams> = ({
287290
.join(' ')}
288291
style={{
289292
position: 'relative',
290-
minHeight: isTableDataLoading && rowCount === 0 ? '100px' : undefined,
293+
minHeight: isTableDataLoading && isEmpty ? '100px' : undefined,
291294
}}
292295
>
293296
<EuiDataGrid
@@ -325,7 +328,7 @@ export const RenderCustomDataGrid: React.FC<RenderCustomDataGridParams> = ({
325328
)}
326329
</div>
327330
</FullScreenWrapper>
328-
{rowCount === 0 && <NoMatchMessage size={noMatchMessageSize} />}
331+
{isEmpty && <NoMatchMessage size={noMatchMessageSize} />}
329332
</>
330333
);
331334
};

public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces.test.tsx.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,7 @@ exports[`Traces component renders empty traces page 1`] = `
14761476
mode="data_prepper"
14771477
page="traces"
14781478
refresh={[Function]}
1479+
uniqueTraces={0}
14791480
>
14801481
<EuiPanel>
14811482
<div
@@ -3935,6 +3936,7 @@ exports[`Traces component renders jaeger traces page 1`] = `
39353936
mode="jaeger"
39363937
page="traces"
39373938
refresh={[Function]}
3939+
uniqueTraces={0}
39383940
>
39393941
<EuiPanel>
39403942
<div
@@ -6223,6 +6225,7 @@ exports[`Traces component renders traces page 1`] = `
62236225
mode="data_prepper"
62246226
page="traces"
62256227
refresh={[Function]}
6228+
uniqueTraces={0}
62266229
>
62276230
<EuiPanel>
62286231
<div

public/components/trace_analytics/components/traces/traces_content.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export function TracesContent(props: TracesProps) {
7272
const [includeMetrics, setIncludeMetrics] = useState(false);
7373
const isNavGroupEnabled = coreRefs?.chrome?.navGroup.getNavGroupEnabled();
7474
const [maxTraces, setMaxTraces] = useState(500);
75+
const [uniqueTraces, setUniqueTraces] = useState(0);
7576
const [sortingColumns, setSortingColumns] = useState<
7677
Array<{ id: string; direction: 'desc' | 'asc' }>
7778
>([]);
@@ -312,8 +313,7 @@ export function TracesContent(props: TracesProps) {
312313
maxTraces,
313314
props.dataSourceMDSId[0].id,
314315
sort,
315-
isUnderOneHour,
316-
setTotalHits
316+
isUnderOneHour
317317
).finally(() => setIsTraceTableLoading(false));
318318
};
319319

@@ -380,7 +380,7 @@ export function TracesContent(props: TracesProps) {
380380
props.dataSourceMDSId[0].id,
381381
newSort,
382382
isUnderOneHour,
383-
setTotalHits
383+
setUniqueTraces
384384
);
385385
tracesRequest.finally(() => setIsTraceTableLoading(false));
386386

@@ -404,7 +404,8 @@ export function TracesContent(props: TracesProps) {
404404
maxTraces,
405405
props.dataSourceMDSId[0].id,
406406
sort,
407-
isUnderOneHour
407+
isUnderOneHour,
408+
setUniqueTraces
408409
).finally(() => setIsTraceTableLoading(false));
409410
}
410411
};
@@ -476,6 +477,7 @@ export function TracesContent(props: TracesProps) {
476477
onSort={onSort}
477478
maxTraces={maxTraces}
478479
setMaxTraces={setMaxTraces}
480+
uniqueTraces={uniqueTraces}
479481
/>
480482
) : (
481483
<TracesTable
@@ -488,6 +490,7 @@ export function TracesContent(props: TracesProps) {
488490
jaegerIndicesExist={jaegerIndicesExist}
489491
dataPrepperIndicesExist={dataPrepperIndicesExist}
490492
page={page}
493+
uniqueTraces={uniqueTraces}
491494
/>
492495
)}
493496

public/components/trace_analytics/components/traces/traces_custom_indices_table.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ interface TracesLandingTableProps {
3535
onSort: (columns: Array<{ id: string; direction: 'desc' | 'asc' }>) => void;
3636
maxTraces: number;
3737
setMaxTraces: React.Dispatch<React.SetStateAction<number>>;
38+
uniqueTraces: number;
3839
}
3940

4041
export function TracesCustomIndicesTable(props: TracesLandingTableProps) {
@@ -151,6 +152,7 @@ export function TracesCustomIndicesTable(props: TracesLandingTableProps) {
151152
setTracesTableMode={(mode) => props.setTracesTableMode(mode as TraceQueryMode)}
152153
maxTraces={props.maxTraces}
153154
setMaxTraces={props.setMaxTraces}
155+
uniqueTraces={props.uniqueTraces}
154156
/>
155157
) : (
156158
<NoMatchMessage size="xl" mode={props.mode} />

public/components/trace_analytics/components/traces/traces_table.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
NoMatchMessage,
3131
PanelTitle,
3232
} from '../common/helper_functions';
33+
import { MAX_DISPLAY_ROWS } from '../common/constants';
3334

3435
interface TracesTableProps {
3536
items: any[];
@@ -41,16 +42,22 @@ interface TracesTableProps {
4142
jaegerIndicesExist: boolean;
4243
dataPrepperIndicesExist: boolean;
4344
page?: 'traces' | 'app';
45+
uniqueTraces: number;
4446
}
4547

4648
export function TracesTable(props: TracesTableProps) {
47-
const { items, refresh, mode, loading, getTraceViewUri, openTraceFlyout } = props;
48-
const renderTitleBar = (totalItems?: number) => {
49+
const { items, refresh, mode, loading, getTraceViewUri, openTraceFlyout, uniqueTraces } = props;
50+
const renderTitleBar = (rowCount: number, totalCount: number) => {
51+
const totalCountText = totalCount > MAX_DISPLAY_ROWS ? `${MAX_DISPLAY_ROWS}+` : totalCount;
52+
4953
return (
5054
<EuiFlexGroup alignItems="center" gutterSize="s">
5155
<EuiFlexItem grow={10}>
52-
<PanelTitle title="Traces" totalItems={totalItems} />
56+
<PanelTitle title="Traces" totalItems={rowCount} />
5357
</EuiFlexItem>
58+
{totalCount > rowCount && (
59+
<span className="trace-table-warning">{`Results out of ${totalCountText}`}</span>
60+
)}
5461
</EuiFlexGroup>
5562
);
5663
};
@@ -228,7 +235,10 @@ export function TracesTable(props: TracesTableProps) {
228235
}
229236
}, [items]);
230237

231-
const titleBar = useMemo(() => renderTitleBar(items?.length), [items]);
238+
const titleBar = useMemo(() => renderTitleBar(items?.length, uniqueTraces), [
239+
items,
240+
uniqueTraces,
241+
]);
232242

233243
const [sorting, setSorting] = useState<{ sort: PropertySort }>({
234244
sort: {

0 commit comments

Comments
 (0)