Skip to content

Commit 04127b7

Browse files
authored
feat(explore): Add pagination to explore traces (#79628)
This adds pagination buttons to the traces list in explore. Note the traces are not perfectly chronologically sorted.
1 parent d517605 commit 04127b7

File tree

3 files changed

+109
-63
lines changed

3 files changed

+109
-63
lines changed

static/app/views/explore/hooks/useTraces.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ interface TraceResults {
5555
}
5656

5757
interface UseTracesOptions {
58+
cursor?: string;
5859
dataset?: DiscoverDatasets;
5960
datetime?: PageFilters['datetime'];
6061
enabled?: boolean;
@@ -64,6 +65,7 @@ interface UseTracesOptions {
6465
}
6566

6667
export function useTraces({
68+
cursor,
6769
dataset,
6870
datetime,
6971
enabled,
@@ -86,6 +88,7 @@ export function useTraces({
8688
query,
8789
sort, // only has an effect when `dataset` is `EAPSpans`
8890
per_page: limit,
91+
cursor,
8992
breakdownSlices: BREAKDOWN_SLICES,
9093
},
9194
};

static/app/views/explore/tables/index.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Fragment, useCallback, useState} from 'react';
1+
import {Fragment, useCallback, useMemo} from 'react';
22
import styled from '@emotion/styled';
33

44
import {openModal} from 'sentry/actionCreators/modal';
@@ -7,6 +7,9 @@ import {TabList, Tabs} from 'sentry/components/tabs';
77
import {IconTable} from 'sentry/icons/iconTable';
88
import {t} from 'sentry/locale';
99
import {space} from 'sentry/styles/space';
10+
import {decodeScalar} from 'sentry/utils/queryString';
11+
import {useLocation} from 'sentry/utils/useLocation';
12+
import {useNavigate} from 'sentry/utils/useNavigate';
1013
import {useResultMode} from 'sentry/views/explore/hooks/useResultsMode';
1114
import {useSampleFields} from 'sentry/views/explore/hooks/useSampleFields';
1215

@@ -22,6 +25,35 @@ enum Tab {
2225
TRACE = 'trace',
2326
}
2427

28+
function useTab(): [Tab, (tab: Tab) => void] {
29+
const location = useLocation();
30+
const navigate = useNavigate();
31+
32+
const tab = useMemo(() => {
33+
const rawTab = decodeScalar(location.query.table);
34+
if (rawTab === 'trace') {
35+
return Tab.TRACE;
36+
}
37+
return Tab.SPAN;
38+
}, [location.query.table]);
39+
40+
const setTab = useCallback(
41+
(newTab: Tab) => {
42+
navigate({
43+
...location,
44+
query: {
45+
...location.query,
46+
table: newTab,
47+
cursor: undefined,
48+
},
49+
});
50+
},
51+
[location, navigate]
52+
);
53+
54+
return [tab, setTab];
55+
}
56+
2557
interface ExploreTablesProps {}
2658

2759
export function ExploreTables({}: ExploreTablesProps) {
@@ -40,7 +72,8 @@ function ExploreAggregatesTable() {
4072
}
4173

4274
function ExploreSamplesTable() {
43-
const [tab, setTab] = useState(Tab.SPAN);
75+
const [tab, setTab] = useTab();
76+
4477
const [fields, setFields] = useSampleFields();
4578
const numberTags = useSpanTags('number');
4679
const stringTags = useSpanTags('string');

static/app/views/explore/tables/tracesTable/index.tsx

Lines changed: 71 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Count from 'sentry/components/count';
77
import EmptyStateWarning, {EmptyStreamWrapper} from 'sentry/components/emptyStateWarning';
88
import ExternalLink from 'sentry/components/links/externalLink';
99
import LoadingIndicator from 'sentry/components/loadingIndicator';
10+
import Pagination from 'sentry/components/pagination';
1011
import PerformanceDuration from 'sentry/components/performanceDuration';
1112
import {DEFAULT_PER_PAGE, SPAN_PROPS_DOCS_URL} from 'sentry/constants';
1213
import {IconChevron} from 'sentry/icons/iconChevron';
@@ -15,6 +16,7 @@ import {t, tct} from 'sentry/locale';
1516
import {space} from 'sentry/styles/space';
1617
import {defined} from 'sentry/utils';
1718
import {trackAnalytics} from 'sentry/utils/analytics';
19+
import {decodeScalar} from 'sentry/utils/queryString';
1820
import {useLocation} from 'sentry/utils/useLocation';
1921
import useOrganization from 'sentry/utils/useOrganization';
2022
import usePageFilters from 'sentry/utils/usePageFilters';
@@ -45,11 +47,16 @@ import {
4547
export function TracesTable() {
4648
const [dataset] = useDataset();
4749
const [query] = useUserQuery();
48-
const {data, isPending, isError} = useTraces({
50+
51+
const location = useLocation();
52+
const cursor = decodeScalar(location.query.cursor);
53+
54+
const {data, isPending, isError, getResponseHeader} = useTraces({
4955
dataset,
5056
query,
5157
limit: DEFAULT_PER_PAGE,
5258
sort: '-timestamp',
59+
cursor,
5360
});
5461

5562
const showErrorState = useMemo(() => {
@@ -61,66 +68,69 @@ export function TracesTable() {
6168
}, [data, isPending, showErrorState]);
6269

6370
return (
64-
<StyledPanel>
65-
<TracePanelContent>
66-
<StyledPanelHeader align="left" lightText>
67-
{t('Trace ID')}
68-
</StyledPanelHeader>
69-
<StyledPanelHeader align="left" lightText>
70-
{t('Trace Root')}
71-
</StyledPanelHeader>
72-
<StyledPanelHeader align="right" lightText>
73-
{!query ? t('Total Spans') : t('Matching Spans')}
74-
</StyledPanelHeader>
75-
<StyledPanelHeader align="left" lightText>
76-
{t('Timeline')}
77-
</StyledPanelHeader>
78-
<StyledPanelHeader align="right" lightText>
79-
{t('Duration')}
80-
</StyledPanelHeader>
81-
<StyledPanelHeader align="right" lightText>
82-
{t('Timestamp')}
83-
</StyledPanelHeader>
84-
{isPending && (
85-
<StyledPanelItem span={6} overflow>
86-
<LoadingIndicator />
87-
</StyledPanelItem>
88-
)}
89-
{showErrorState && ( // TODO: need an error state
90-
<StyledPanelItem span={7} overflow>
91-
<EmptyStreamWrapper>
92-
<IconWarning color="gray300" size="lg" />
93-
</EmptyStreamWrapper>
94-
</StyledPanelItem>
95-
)}
96-
{showEmptyState && (
97-
<StyledPanelItem span={7} overflow>
98-
<EmptyStateWarning withIcon>
99-
<EmptyStateText size="fontSizeExtraLarge">
100-
{t('No trace results found')}
101-
</EmptyStateText>
102-
<EmptyStateText size="fontSizeMedium">
103-
{tct('Try adjusting your filters or refer to [docSearchProps].', {
104-
docSearchProps: (
105-
<ExternalLink href={SPAN_PROPS_DOCS_URL}>
106-
{t('docs for search properties')}
107-
</ExternalLink>
108-
),
109-
})}
110-
</EmptyStateText>
111-
</EmptyStateWarning>
112-
</StyledPanelItem>
113-
)}
114-
{data?.data?.map((trace, i) => (
115-
<TraceRow
116-
key={trace.trace}
117-
trace={trace}
118-
defaultExpanded={query && i === 0}
119-
query={query}
120-
/>
121-
))}
122-
</TracePanelContent>
123-
</StyledPanel>
71+
<Fragment>
72+
<StyledPanel>
73+
<TracePanelContent>
74+
<StyledPanelHeader align="left" lightText>
75+
{t('Trace ID')}
76+
</StyledPanelHeader>
77+
<StyledPanelHeader align="left" lightText>
78+
{t('Trace Root')}
79+
</StyledPanelHeader>
80+
<StyledPanelHeader align="right" lightText>
81+
{!query ? t('Total Spans') : t('Matching Spans')}
82+
</StyledPanelHeader>
83+
<StyledPanelHeader align="left" lightText>
84+
{t('Timeline')}
85+
</StyledPanelHeader>
86+
<StyledPanelHeader align="right" lightText>
87+
{t('Duration')}
88+
</StyledPanelHeader>
89+
<StyledPanelHeader align="right" lightText>
90+
{t('Timestamp')}
91+
</StyledPanelHeader>
92+
{isPending && (
93+
<StyledPanelItem span={6} overflow>
94+
<LoadingIndicator />
95+
</StyledPanelItem>
96+
)}
97+
{showErrorState && ( // TODO: need an error state
98+
<StyledPanelItem span={7} overflow>
99+
<EmptyStreamWrapper>
100+
<IconWarning color="gray300" size="lg" />
101+
</EmptyStreamWrapper>
102+
</StyledPanelItem>
103+
)}
104+
{showEmptyState && (
105+
<StyledPanelItem span={7} overflow>
106+
<EmptyStateWarning withIcon>
107+
<EmptyStateText size="fontSizeExtraLarge">
108+
{t('No trace results found')}
109+
</EmptyStateText>
110+
<EmptyStateText size="fontSizeMedium">
111+
{tct('Try adjusting your filters or refer to [docSearchProps].', {
112+
docSearchProps: (
113+
<ExternalLink href={SPAN_PROPS_DOCS_URL}>
114+
{t('docs for search properties')}
115+
</ExternalLink>
116+
),
117+
})}
118+
</EmptyStateText>
119+
</EmptyStateWarning>
120+
</StyledPanelItem>
121+
)}
122+
{data?.data?.map((trace, i) => (
123+
<TraceRow
124+
key={trace.trace}
125+
trace={trace}
126+
defaultExpanded={query && i === 0}
127+
query={query}
128+
/>
129+
))}
130+
</TracePanelContent>
131+
</StyledPanel>
132+
<Pagination pageLinks={getResponseHeader?.('Link')} />
133+
</Fragment>
124134
);
125135
}
126136

0 commit comments

Comments
 (0)