Skip to content

Commit 4faac74

Browse files
authored
feat(ourlogs): Add manual refresh button and adjust infinite scroll (#97509)
### Summary This adds a manual refresh button for users to click to avoid having to leave the page on logs. This also fixes some problems with infinite scroll introduced after the auto-refresh changes, and maintains a longer time between requests when scrolling as it was too sensitive.
1 parent 80270b0 commit 4faac74

File tree

7 files changed

+70
-28
lines changed

7 files changed

+70
-28
lines changed

static/app/views/explore/contexts/logs/logsAutoRefreshContext.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,6 @@ export function useLogsAutoRefreshContinued() {
107107
return autoRefresh === 'enabled' && pausedAtAllowedToContinue(pausedAt);
108108
}
109109

110-
export function useAutorefreshEnabledOrWithinPauseWindow() {
111-
const {autoRefresh, pausedAt} = useLogsAutoRefresh();
112-
return (
113-
autoRefresh === 'enabled' ||
114-
(autoRefresh === 'paused' && withinPauseWindow(autoRefresh, pausedAt))
115-
);
116-
}
117-
118110
function withinPauseWindow(autoRefresh: AutoRefreshState, pausedAt: number | undefined) {
119111
return (
120112
(autoRefresh === 'paused' || autoRefresh === 'enabled') &&

static/app/views/explore/logs/constants.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,6 @@ export const LOGS_INSTRUCTIONS_URL =
7575
export const LOGS_FILTER_KEY_SECTIONS: FilterKeySection[] = [LOGS_FILTERS];
7676

7777
export const VIRTUAL_STREAMED_INTERVAL_MS = 250;
78+
export const MINIMUM_INFINITE_SCROLL_FETCH_COOLDOWN_MS = 1000;
7879

79-
export const LOGS_GRID_SCROLL_MIN_ITEM_THRESHOLD = 100; // Items from bottom of table to trigger table fetch.
80+
export const LOGS_GRID_SCROLL_MIN_ITEM_THRESHOLD = 50; // Items from bottom of table to trigger table fetch.

static/app/views/explore/logs/logsTab.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
1010
import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
1111
import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
1212
import {SearchQueryBuilderProvider} from 'sentry/components/searchQueryBuilder/context';
13-
import {IconChevron, IconTable} from 'sentry/icons';
13+
import {IconChevron, IconRefresh, IconTable} from 'sentry/icons';
1414
import {t} from 'sentry/locale';
1515
import {LogsAnalyticsPageSource} from 'sentry/utils/analytics/logsAnalyticsEvent';
1616
import {DiscoverDatasets} from 'sentry/utils/discover/types';
17+
import {type InfiniteData, useQueryClient} from 'sentry/utils/queryClient';
1718
import useOrganization from 'sentry/utils/useOrganization';
1819
import SchemaHintsList, {
1920
SchemaHintsSection,
@@ -63,7 +64,10 @@ import {
6364
import {LogsAggregateTable} from 'sentry/views/explore/logs/tables/logsAggregateTable';
6465
import {LogsInfiniteTable as LogsInfiniteTable} from 'sentry/views/explore/logs/tables/logsInfiniteTable';
6566
import {LogsTable} from 'sentry/views/explore/logs/tables/logsTable';
66-
import {OurLogKnownFieldKey} from 'sentry/views/explore/logs/types';
67+
import {
68+
OurLogKnownFieldKey,
69+
type OurLogsResponseItem,
70+
} from 'sentry/views/explore/logs/types';
6771
import {
6872
getIngestDelayFilterValue,
6973
getMaxIngestDelayTimestamp,
@@ -93,6 +97,7 @@ export function LogsTabContent({
9397
const fields = useLogsFields();
9498
const groupBy = useLogsGroupBy();
9599
const mode = useQueryParamsMode();
100+
const queryClient = useQueryClient();
96101
const sortBys = useLogsAggregateSortBys();
97102
const setMode = useSetQueryParamsMode();
98103
const setFields = useSetLogsFields();
@@ -187,6 +192,23 @@ export function LogsTabContent({
187192
return [];
188193
}, []);
189194

195+
const refreshTable = useCallback(async () => {
196+
queryClient.setQueryData(
197+
tableData.queryKey,
198+
(data: InfiniteData<OurLogsResponseItem[]>) => {
199+
if (data?.pages) {
200+
// We only want to keep the first page of data to avoid re-fetching multiple pages, since infinite query will otherwise fetch up to max pages (eg. 30) all at once.
201+
return {
202+
pages: data.pages.slice(0, 1),
203+
pageParams: data.pageParams.slice(0, 1),
204+
};
205+
}
206+
return data;
207+
}
208+
);
209+
await tableData.refetch();
210+
}, [tableData, queryClient]);
211+
190212
const openColumnEditor = useCallback(() => {
191213
openModal(
192214
modalProps => (
@@ -319,6 +341,12 @@ export function LogsTabContent({
319341
<Feature features="organizations:ourlogs-live-refresh">
320342
<AutorefreshToggle averageLogsPerSecond={averageLogsPerSecond} />
321343
</Feature>
344+
<Button
345+
onClick={refreshTable}
346+
icon={<IconRefresh />}
347+
size="sm"
348+
aria-label={t('Refresh')}
349+
/>
322350
<Button onClick={openColumnEditor} icon={<IconTable />} size="sm">
323351
{t('Edit Table')}
324352
</Button>

static/app/views/explore/logs/tables/logsInfiniteTable.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ import {
2323
TableStatus,
2424
useTableStyles,
2525
} from 'sentry/views/explore/components/table';
26-
import {
27-
useAutorefreshEnabledOrWithinPauseWindow,
28-
useLogsAutoRefreshEnabled,
29-
} from 'sentry/views/explore/contexts/logs/logsAutoRefreshContext';
26+
import {useLogsAutoRefreshEnabled} from 'sentry/views/explore/contexts/logs/logsAutoRefreshContext';
3027
import {useLogsPageData} from 'sentry/views/explore/contexts/logs/logsPageData';
3128
import {
3229
useLogsFields,
3330
useLogsSearch,
3431
useLogsSortBys,
3532
useSetLogsSortBys,
3633
} from 'sentry/views/explore/contexts/logs/logsPageParams';
37-
import {LOGS_INSTRUCTIONS_URL} from 'sentry/views/explore/logs/constants';
34+
import {
35+
LOGS_INSTRUCTIONS_URL,
36+
MINIMUM_INFINITE_SCROLL_FETCH_COOLDOWN_MS,
37+
} from 'sentry/views/explore/logs/constants';
3838
import {
3939
FirstTableHeadCell,
4040
FloatingBackToTopContainer,
@@ -90,6 +90,7 @@ export function LogsInfiniteTable({
9090
const search = useLogsSearch();
9191
const autoRefresh = useLogsAutoRefreshEnabled();
9292
const {infiniteLogsQueryResult} = useLogsPageData();
93+
const lastFetchTime = useRef<number | null>(null);
9394
const {
9495
isPending,
9596
isEmpty,
@@ -101,6 +102,7 @@ export function LogsInfiniteTable({
101102
isFetchingNextPage,
102103
isFetchingPreviousPage,
103104
lastPageLength,
105+
isRefetching,
104106
} = infiniteLogsQueryResult;
105107

106108
// Use filtered items if provided, otherwise use original data
@@ -113,10 +115,8 @@ export function LogsInfiniteTable({
113115
Record<string, number>
114116
>({});
115117
const [isFunctionScrolling, setIsFunctionScrolling] = useState(false);
116-
const isAutorefreshEnabledOrWithinPauseWindow =
117-
useAutorefreshEnabledOrWithinPauseWindow();
118-
const scrollFetchDisabled =
119-
isFunctionScrolling || !isAutorefreshEnabledOrWithinPauseWindow;
118+
const autorefreshEnabled = useLogsAutoRefreshEnabled();
119+
const scrollFetchDisabled = isFunctionScrolling || autorefreshEnabled;
120120

121121
const sharedHoverTimeoutRef = useRef<NodeJS.Timeout | null>(null);
122122
const {initialTableStyles, onResizeMouseDown} = useTableStyles(fields, tableRef, {
@@ -203,7 +203,13 @@ export function LogsInfiniteTable({
203203
lastItemIndex &&
204204
lastItemIndex >= data?.length - getDynamicLogsNextFetchThreshold(lastPageLength)
205205
) {
206-
fetchNextPage();
206+
if (
207+
lastFetchTime.current === null ||
208+
Date.now() - lastFetchTime.current > MINIMUM_INFINITE_SCROLL_FETCH_COOLDOWN_MS
209+
) {
210+
fetchNextPage();
211+
lastFetchTime.current = Date.now();
212+
}
207213
}
208214
}
209215
}, [
@@ -217,6 +223,7 @@ export function LogsInfiniteTable({
217223
scrollOffset,
218224
isFunctionScrolling,
219225
scrollFetchDisabled,
226+
lastFetchTime,
220227
]);
221228

222229
const handleExpand = useCallback((logItemId: string) => {
@@ -286,6 +293,7 @@ export function LogsInfiniteTable({
286293
{!autoRefresh && !isPending && isFetchingPreviousPage && (
287294
<HoveringRowLoadingRenderer position="top" />
288295
)}
296+
{isRefetching && <HoveringRowLoadingRenderer position="top" />}
289297
{virtualItems.map(virtualRow => {
290298
const dataRow = data?.[virtualRow.index];
291299

static/app/views/explore/logs/useLogsQuery.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,13 +280,16 @@ export function useLogsQuery({
280280

281281
return {
282282
isPending: queryResult.isPending,
283+
isRefetching: queryResult.isRefetching,
283284
isError: queryResult.isError,
284285
isLoading: queryResult.isLoading,
285286
queryResult,
286287
data: queryResult?.data?.data,
288+
refetch: queryResult.refetch,
287289
infiniteData: queryResult?.data?.data,
288290
error: queryResult.error,
289291
meta: queryResult?.data?.meta,
292+
queryKey,
290293
pageLinks: queryResult?.getResponseHeader?.('Link') ?? undefined,
291294
};
292295
}
@@ -552,7 +555,7 @@ export function useInfiniteLogsQuery({
552555
isFetching,
553556
isFetchingNextPage,
554557
isFetchingPreviousPage,
555-
isPending,
558+
refetch,
556559
} = queryResult;
557560

558561
useEffect(() => {
@@ -636,22 +639,29 @@ export function useInfiniteLogsQuery({
636639
const _fetchNextPage = useCallback(
637640
() =>
638641
hasNextPage && nextPageHasData
639-
? !isFetchingNextPage && !isError && fetchNextPage()
642+
? !isFetching && !isError && fetchNextPage()
640643
: Promise.resolve(),
641-
[hasNextPage, fetchNextPage, isFetchingNextPage, isError, nextPageHasData]
644+
[hasNextPage, fetchNextPage, isFetching, isError, nextPageHasData]
642645
);
643646

644647
return {
645648
error,
646649
isError,
647650
isFetching,
648-
isPending,
651+
isPending: queryResult.isPending,
649652
data: _data,
650653
meta: _meta,
651-
isEmpty: !isPending && !isError && _data.length === 0,
654+
isRefetching: queryResult.isRefetching,
655+
isEmpty:
656+
!queryResult.isPending &&
657+
!queryResult.isRefetching &&
658+
!isError &&
659+
_data.length === 0,
652660
fetchNextPage: _fetchNextPage,
653661
fetchPreviousPage: _fetchPreviousPage,
662+
refetch,
654663
hasNextPage,
664+
queryKey: queryKeyWithInfinite,
655665
hasPreviousPage,
656666
isFetchingNextPage,
657667
isFetchingPreviousPage,

static/app/views/explore/logs/useStreamingTimeseriesResult.spec.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ describe('useStreamingTimeseriesResult', () => {
9090
isEmpty: logFixtures.length === 0,
9191
fetchNextPage: jest.fn(),
9292
fetchPreviousPage: jest.fn(),
93+
isRefetching: false,
94+
refetch: jest.fn(),
95+
queryKey: ['logs', 'infinite', 'infinite'],
9396
hasNextPage: false,
9497
hasPreviousPage: false,
9598
isFetchingNextPage: false,

static/app/views/explore/logs/utils.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,8 @@ export function logsPickableDays(organization: Organization): PickableDays {
255255
}
256256

257257
export function getDynamicLogsNextFetchThreshold(lastPageLength: number) {
258-
if (lastPageLength * 0.75 > LOGS_GRID_SCROLL_MIN_ITEM_THRESHOLD) {
259-
return Math.floor(lastPageLength * 0.75); // Can be up to 750 on large pages.
258+
if (lastPageLength * 0.25 > LOGS_GRID_SCROLL_MIN_ITEM_THRESHOLD) {
259+
return Math.floor(lastPageLength * 0.25); // Can be up to 250 on large pages.
260260
}
261261
return LOGS_GRID_SCROLL_MIN_ITEM_THRESHOLD;
262262
}

0 commit comments

Comments
 (0)