Skip to content

Commit b5fc746

Browse files
authored
feat(ourlogs): Add auto-refresh graph updates (#95489)
### Summary This updates the graph when autorefresh is on to also stream in log data. This is behind the feature flag alongside the auto-refresh. This adds a hook which takes in the table data (which is streaming with auto-refresh) and the timeseries and synthesizes new buckets based on the existing timeseries structure as more logs appear in the table. Explanation for how the timeseries streaming works is in the comments for hook function.
1 parent b4c922f commit b5fc746

File tree

9 files changed

+1039
-16
lines changed

9 files changed

+1039
-16
lines changed

static/app/utils/timeSeries/markDelayedData.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Given a timeseries and a delay in seconds, goes through the timeseries data, and marks each point as either delayed (data bucket ended before the delay threshold) or not
33
*/
44

5+
import {defined} from 'sentry/utils';
56
import type {TimeSeries} from 'sentry/views/dashboards/widgets/common/types';
67

78
export function markDelayedData(timeSeries: TimeSeries, delay: number): TimeSeries {
@@ -22,6 +23,10 @@ export function markDelayedData(timeSeries: TimeSeries, delay: number): TimeSeri
2223
const bucketEndTimestamp = new Date(datum.timestamp).getTime() + bucketSize;
2324
const delayed = bucketEndTimestamp >= ingestionDelayTimestamp;
2425

26+
if (defined(datum.incomplete)) {
27+
return datum;
28+
}
29+
2530
if (!delayed) {
2631
return datum;
2732
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,8 @@ export function useSetLogsAutoRefresh() {
547547
(autoRefresh: boolean) => {
548548
if (autoRefresh) {
549549
queryClient.removeQueries({queryKey});
550+
// Until we change our timeseries hooks to be build their query keys separately, we need to remove the query via the route.
551+
queryClient.removeQueries({queryKey: ['events-stats']});
550552
}
551553
setPageParams({autoRefresh});
552554
},

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

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import usePrevious from 'sentry/utils/usePrevious';
1010
import {useLogsPageData} from 'sentry/views/explore/contexts/logs/logsPageData';
1111
import {
1212
useLogsAutoRefresh,
13+
useLogsGroupBy,
1314
useLogsRefreshInterval,
1415
useLogsSortBys,
1516
useSetLogsAutoRefresh,
@@ -27,12 +28,20 @@ type DisableReason = 'sort' | 'timeout' | 'rateLimit' | 'error';
2728

2829
const SWITCH_DISABLE_REASONS: DisableReason[] = ['sort'];
2930

30-
export function AutorefreshToggle() {
31-
const {selection} = usePageFilters();
32-
const previousProjects = usePrevious(selection.projects);
31+
interface AutorefreshToggleProps {
32+
disabled?: boolean;
33+
}
34+
35+
export function AutorefreshToggle({
36+
disabled: externallyDisabled,
37+
}: AutorefreshToggleProps) {
3338
const checked = useLogsAutoRefresh();
3439
const setChecked = useSetLogsAutoRefresh();
3540
const sortBys = useLogsSortBys();
41+
const groupBy = useLogsGroupBy();
42+
const {selection} = usePageFilters();
43+
const previousSelection = usePrevious(selection);
44+
const previousGroupBy = usePrevious(groupBy);
3645
const refreshInterval = useLogsRefreshInterval();
3746
const {infiniteLogsQueryResult} = useLogsPageData();
3847
const {fetchPreviousPage, isError} = infiniteLogsQueryResult;
@@ -50,16 +59,25 @@ export function AutorefreshToggle() {
5059
const isRefreshRunningRef = useRef(false);
5160
const consecutivePagesWithMoreDataRef = useRef(0);
5261

53-
const statsPeriod = usePageFilters().selection.datetime.period;
62+
const statsPeriod = selection.datetime.period;
5463
const isDescendingTimeBasedSort = checkSortIsTimeBasedDescending(sortBys);
55-
const enabled = isDescendingTimeBasedSort && checked && defined(statsPeriod);
64+
const enabled =
65+
isDescendingTimeBasedSort && checked && defined(statsPeriod) && !externallyDisabled;
5666

57-
// Disable auto-refresh if the project changes
67+
// Disable auto-refresh if anything in the selection changes
5868
useEffect(() => {
59-
if (!isEqual(selection.projects, previousProjects)) {
69+
const selectionChanged = !isEqual(previousSelection, selection);
70+
if (selectionChanged) {
6071
setChecked(false);
6172
}
62-
}, [selection.projects, previousProjects, setChecked]);
73+
}, [selection, previousSelection, setChecked]);
74+
75+
// Disable auto-refresh if the group-by changes
76+
useEffect(() => {
77+
if (groupBy && groupBy !== previousGroupBy) {
78+
setChecked(false);
79+
}
80+
}, [groupBy, previousGroupBy, setChecked]);
6381

6482
// Reset disableReason when sort bys change
6583
useEffect(() => {
@@ -68,6 +86,13 @@ export function AutorefreshToggle() {
6886
}
6987
}, [sortBysString, previousSortBysString, isDescendingTimeBasedSort]);
7088

89+
// Disable auto-refresh when externally disabled
90+
useEffect(() => {
91+
if (externallyDisabled && checked) {
92+
setChecked(false);
93+
}
94+
}, [externallyDisabled, checked, setChecked]);
95+
7196
useEffect(() => {
7297
if (isError && enabled) {
7398
setDisableReason('error');
@@ -209,6 +234,9 @@ export function AutorefreshToggle() {
209234
}, [enabled, refreshInterval]);
210235

211236
const getTooltipMessage = (): string => {
237+
if (externallyDisabled) {
238+
return t('Auto-refresh is not available in the aggregates view.');
239+
}
212240
switch (disableReason) {
213241
case 'rateLimit':
214242
return t(
@@ -235,11 +263,14 @@ export function AutorefreshToggle() {
235263
<AutoRefreshLabel>
236264
<Tooltip
237265
title={getTooltipMessage()}
238-
disabled={disableReason === undefined}
266+
disabled={disableReason === undefined && !externallyDisabled}
239267
skipWrapper
240268
>
241269
<Switch
242-
disabled={disableReason && SWITCH_DISABLE_REASONS.includes(disableReason)}
270+
disabled={
271+
externallyDisabled ||
272+
(disableReason && SWITCH_DISABLE_REASONS.includes(disableReason))
273+
}
243274
checked={checked}
244275
onChange={() => {
245276
if (!checked) {

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
ChartIntervalUnspecifiedStrategy,
2222
useChartInterval,
2323
} from 'sentry/views/explore/hooks/useChartInterval';
24-
import {INGESTION_DELAY} from 'sentry/views/explore/settings';
2524
import {ChartType} from 'sentry/views/insights/common/components/chart';
2625
import type {useSortedTimeSeries} from 'sentry/views/insights/common/queries/useSortedTimeSeries';
2726

@@ -138,7 +137,7 @@ export function LogsGraph({timeseriesResult}: LogsGraphProps) {
138137
<TimeSeriesWidgetVisualization
139138
plottables={data.map(
140139
timeSeries =>
141-
new DataPlottableConstructor(markDelayedData(timeSeries, INGESTION_DELAY), {
140+
new DataPlottableConstructor(markDelayedData(timeSeries, 60), {
142141
stack: 'all',
143142
})
144143
)}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import {LogsAggregateTable} from 'sentry/views/explore/logs/tables/logsAggregate
6464
import {LogsInfiniteTable as LogsInfiniteTable} from 'sentry/views/explore/logs/tables/logsInfiniteTable';
6565
import {LogsTable} from 'sentry/views/explore/logs/tables/logsTable';
6666
import {usePersistentLogsPageParameters} from 'sentry/views/explore/logs/usePersistentLogsPageParameters';
67+
import {useStreamingTimeseriesResult} from 'sentry/views/explore/logs/useStreamingTimeseriesResult';
6768
import {ColumnEditorModal} from 'sentry/views/explore/tables/columnEditorModal';
6869
import {TraceItemDataset} from 'sentry/views/explore/types';
6970
import type {PickableDays} from 'sentry/views/explore/utils';
@@ -100,7 +101,7 @@ export function LogsTabContent({
100101
const [sidebarOpen, setSidebarOpen] = useState(
101102
!!((aggregateFunction && aggregateFunction !== 'count') || groupBy)
102103
);
103-
const timeseriesResult = useSortedTimeSeries(
104+
const _timeseriesResult = useSortedTimeSeries(
104105
{
105106
search: logsSearch,
106107
yAxis: [aggregate],
@@ -111,6 +112,7 @@ export function LogsTabContent({
111112
'explore.ourlogs.main-chart',
112113
DiscoverDatasets.OURLOGS
113114
);
115+
const timeseriesResult = useStreamingTimeseriesResult(tableData, _timeseriesResult);
114116

115117
const {attributes: stringAttributes, isLoading: stringAttributesLoading} =
116118
useTraceItemAttributes('string');
@@ -254,7 +256,7 @@ export function LogsTabContent({
254256
</Feature>
255257
<TableActionsContainer>
256258
<Feature features="organizations:ourlogs-live-refresh">
257-
<AutorefreshToggle />
259+
<AutorefreshToggle disabled={tableTab === 'aggregates'} />
258260
</Feature>
259261
<Button onClick={openColumnEditor} icon={<IconTable />} size="sm">
260262
{t('Edit Table')}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,15 @@ function useLogsQueryKey({limit, referrer}: {referrer: string; limit?: number})
197197
const {selection, isReady: pageFiltersReady} = usePageFilters();
198198
const location = useLocation();
199199
const projectIds = useLogsProjectIds();
200+
const groupBy = useLogsGroupBy();
200201

201202
const search = baseSearch ? _search.copy() : _search;
202203
if (baseSearch) {
203204
search.tokens.push(...baseSearch.tokens);
204205
}
205-
const fields = Array.from(new Set([...AlwaysPresentLogFields, ..._fields]));
206+
const fields = Array.from(
207+
new Set([...AlwaysPresentLogFields, ..._fields, ...(groupBy ? [groupBy] : [])])
208+
);
206209
const sorts = sortBys ?? [];
207210
const pageFilters = selection;
208211
const dataset = DiscoverDatasets.OURLOGS;
@@ -512,7 +515,7 @@ export function useInfiniteLogsQuery({
512515
initialPageParam,
513516
enabled: !disabled,
514517
staleTime: getStaleTimeForEventView(other.eventView),
515-
maxPages: 15,
518+
maxPages: 30, // This number * the refresh interval must be more seconds than 2 * the smallest time interval in the chart for streaming to work.
516519
});
517520

518521
const {

0 commit comments

Comments
 (0)