signoz/frontend/src/hooks/useLogsData.ts
Shaheer Kochai 16140991be
refactor: update logs explorer pagination logic (#7010)
* refactor: pagination changes in query range custom hook

* refactor: handle pagination changes in k8s logs and host logs

* refactor: logs panel component pagination changes

* fix: handle resetting offset on changing page size

* fix: optimize context log rendering and prevent duplicate logs

* chore: revert pagination handling changes for logs context view

* chore: remove unused function and prop

* refactor: handle the updated pagination logic in k8s entity events

* refactor: refactor queryPayload generation in logs pagination custom hook

* fix: remove handling for last log timestamp being sent with query_range

* chore: remove the unnecessary last log related changes from LogsExplorerViews

* Revert "fix: optimize context log rendering and prevent duplicate logs"

This reverts commit ad9ef188651e5106cbc84fe40fc36061c2b9fd40.

* fix: prevent recalculating the start/end timestamps while fetching next/prev pages

* chore: logs explorer pagination tests

* fix: rewrite test and mock scroll

* chore: use real timers to detect the start/end timestamps mismatch + enhance tests

* refactor: extract filters and order by into a separate function

* chore: add tests for logs context pagination

* chore: write tests for host logs pagination

* chore: overall improvements to host logs tests

* chore: k8s entity logs pagination tests

* chore: reset capturedQueryRangePayloads in beforeEach

* chore: dashboard logs panel component pagination tests

* fix: fix the breaking logs pagination test by change /v3 to /v4

* chore: remove the unused prop from HostMetricsLogs
2025-05-22 09:42:51 +00:00

195 lines
4.7 KiB
TypeScript

import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { QueryParams } from 'constants/query';
import {
initialQueryBuilderFormValues,
PANEL_TYPES,
} from 'constants/queryBuilder';
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
import { getPaginationQueryDataV2 } from 'lib/newQueryBuilder/getPaginationQueryData';
import { useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { ILog } from 'types/api/logs/log';
import {
IBuilderQuery,
OrderByPayload,
Query,
TagFilter,
} from 'types/api/queryBuilder/queryBuilderData';
import { QueryDataV3 } from 'types/api/widgets/getQuery';
import { GlobalReducer } from 'types/reducer/globalTime';
import { useCopyLogLink } from './logs/useCopyLogLink';
import { useGetExplorerQueryRange } from './queryBuilder/useGetExplorerQueryRange';
import useUrlQueryData from './useUrlQueryData';
export const useLogsData = ({
result,
panelType,
stagedQuery,
}: {
result: QueryDataV3[] | undefined;
panelType: PANEL_TYPES;
stagedQuery: Query | null;
}): {
logs: ILog[];
handleEndReached: (index: number) => void;
isFetching: boolean;
} => {
const [logs, setLogs] = useState<ILog[]>([]);
const [page, setPage] = useState<number>(1);
const [requestData, setRequestData] = useState<Query | null>(null);
const [shouldLoadMoreLogs, setShouldLoadMoreLogs] = useState<boolean>(false);
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const { queryData: pageSize } = useUrlQueryData(
QueryParams.pageSize,
DEFAULT_PER_PAGE_VALUE,
);
const listQuery = useMemo(() => {
if (!stagedQuery || stagedQuery?.builder?.queryData?.length < 1) return null;
return stagedQuery.builder?.queryData.find((item) => !item.disabled) || null;
}, [stagedQuery]);
const isLimit: boolean = useMemo(() => {
if (!listQuery) return false;
if (!listQuery.limit) return false;
return logs.length >= listQuery.limit;
}, [logs.length, listQuery]);
const orderByTimestamp: OrderByPayload | null = useMemo(() => {
const timestampOrderBy = listQuery?.orderBy.find(
(item) => item.columnName === 'timestamp',
);
return timestampOrderBy || null;
}, [listQuery]);
useEffect(() => {
if (panelType !== PANEL_TYPES.LIST) return;
const currentData = result || [];
if (currentData.length > 0 && currentData[0].list) {
const currentLogs: ILog[] = currentData[0].list.map((item) => ({
...item.data,
timestamp: item.timestamp,
}));
const newLogs = [...currentLogs];
setLogs(newLogs);
} else {
setLogs([]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [result]);
const getRequestData = (
query: Query | null,
params: {
page: number;
log: ILog | null;
pageSize: number;
filters: TagFilter;
},
): Query | null => {
if (!query) return null;
const paginateData = getPaginationQueryDataV2({
page: params.page,
pageSize: params.pageSize,
});
const queryData: IBuilderQuery[] =
query.builder.queryData.length > 1
? query.builder.queryData
: [
{
...(listQuery || initialQueryBuilderFormValues),
...paginateData,
},
];
const data: Query = {
...query,
builder: {
...query.builder,
queryData,
},
};
return data;
};
const { activeLogId } = useCopyLogLink();
const { data, isFetching } = useGetExplorerQueryRange(
requestData,
panelType,
DEFAULT_ENTITY_VERSION,
{
keepPreviousData: true,
enabled: !isLimit && !!requestData,
},
{
...(activeLogId &&
!logs.length && {
start: minTime,
end: maxTime,
}),
},
shouldLoadMoreLogs,
);
useEffect(() => {
const currentData = data?.payload?.data?.newResult?.data?.result || [];
if (currentData.length > 0 && currentData[0].list) {
const currentLogs: ILog[] = currentData[0].list.map((item) => ({
...item.data,
timestamp: item.timestamp,
}));
const newLogs = [...logs, ...currentLogs];
setLogs(newLogs);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data]);
const handleEndReached = (index: number): void => {
if (!listQuery) return;
if (isLimit) return;
if (logs.length < pageSize) return;
const { limit, filters } = listQuery;
const lastLog = logs[index];
const nextLogsLength = logs.length + pageSize;
const nextPageSize =
limit && nextLogsLength >= limit ? limit - logs.length : pageSize;
if (!stagedQuery) return;
const newRequestData = getRequestData(stagedQuery, {
filters,
page: page + 1,
log: orderByTimestamp ? lastLog : null,
pageSize: nextPageSize,
});
setPage((prevPage) => prevPage + 1);
setRequestData(newRequestData);
setShouldLoadMoreLogs(true);
};
return { logs, handleEndReached, isFetching };
};