diff --git a/web/src/composables/useLogs.ts b/web/src/composables/useLogs.ts index b3f5886d0f..373213dc74 100644 --- a/web/src/composables/useLogs.ts +++ b/web/src/composables/useLogs.ts @@ -15,7 +15,7 @@ import { useQuasar } from "quasar"; import { useI18n } from "vue-i18n"; -import { reactive, onBeforeMount } from "vue"; +import { reactive, onBeforeMount, nextTick } from "vue"; import { useStore } from "vuex"; import { useRouter } from "vue-router"; import { cloneDeep } from "lodash-es"; @@ -285,7 +285,8 @@ const useLogs = () => { // in-progress and user select stream from dropdown in that case it loads data but it should wait for // additional details from the user like filter conditions and time range selection before load data // it should work in case of page refresh, navigate user from streams page or short url - let initialStreamSelected: boolean = searchObj.data.stream.selectedStream.length > 0; + let initialStreamSelected: boolean = + searchObj.data.stream.selectedStream.length > 0; await getStreamList(); await getFunctions(); @@ -355,7 +356,7 @@ const useLogs = () => { if ( Object.hasOwn(router.currentRoute.value.query, "type") && (router.currentRoute.value.query.type == "search_history_re_apply" || - router.currentRoute.value.query.type == "ai_chat_query") + router.currentRoute.value.query.type == "ai_chat_query") ) { delete router.currentRoute.value.query.type; } @@ -394,6 +395,10 @@ const useLogs = () => { if (queryParams.query) { searchObj.meta.sqlMode = queryParams.sql_mode == "true" ? true : false; + // Added nextTick() for a purpose, don't remove. + // If value of sqlMode is changed, watcher gets called which resets the editor and query value + // The editor value and query value assigned below, is overrided by the watcher + await nextTick(); searchObj.data.editorValue = b64DecodeUnicode(queryParams.query); searchObj.data.query = b64DecodeUnicode(queryParams.query); } @@ -513,7 +518,10 @@ const useLogs = () => { } }; - const reorderArrayByReference = (arr1: string[], arr2: string[]): string[] => { + const reorderArrayByReference = ( + arr1: string[], + arr2: string[], + ): string[] => { return [...arr1].sort((a, b) => { const indexA = arr2.indexOf(a); const indexB = arr2.indexOf(b); diff --git a/web/src/composables/useStreams.spec.ts b/web/src/composables/useStreams.spec.ts index 3d2a0e2ad7..48eb202c9e 100644 --- a/web/src/composables/useStreams.spec.ts +++ b/web/src/composables/useStreams.spec.ts @@ -636,14 +636,16 @@ describe("useStreams Composable", () => { expect(setStreamsCalls[0][1].streams.list).toEqual(existingStreams); }); - it("should handle addNewStreams with empty cache", () => { + it("should not seed cache when addNewStreams called with empty cache", () => { const newStreams = [{ name: "new-stream", stream_type: "logs" }]; - + mockStore.state.streams.logs = null; - + streamsInstance.addNewStreams("logs", newStreams); - - expect(mockStore.dispatch).toHaveBeenCalled(); + + // Should NOT dispatch — partial results must not seed the cache, + // otherwise a full fetch on the logs page would be skipped. + expect(mockStore.dispatch).not.toHaveBeenCalled(); }); }); diff --git a/web/src/composables/useStreams.ts b/web/src/composables/useStreams.ts index 6630081b70..d43a38cb75 100644 --- a/web/src/composables/useStreams.ts +++ b/web/src/composables/useStreams.ts @@ -784,6 +784,11 @@ const useStreams = () => { }; const addNewStreams = (streamType: string, streamList: any[]) => { + // Don't seed the cache with partial results — only merge into an + // already-populated cache. Otherwise a paginated fetch (e.g. from the + // streams management page) would prevent a full fetch on the logs page. + if (!streamsCache[streamType]?.value?.list) return; + // Check if stream exist in store, if not then add it const streamsToAdd = [...(streamsCache[streamType].value?.list || [])]; diff --git a/web/src/composables/useTraces.spec.ts b/web/src/composables/useTraces.spec.ts index b8bccf6b84..822b3fb4aa 100644 --- a/web/src/composables/useTraces.spec.ts +++ b/web/src/composables/useTraces.spec.ts @@ -46,6 +46,7 @@ vi.mock("vuex", () => ({ }, }, }, + dispatch: vi.fn(), })), })); @@ -486,9 +487,9 @@ describe("useTraces", () => { // navigateToLogs // ------------------------------------------------------------------------- describe("navigateToLogs", () => { - it("calls router.push with /logs path", () => { + it("calls router.push with /logs path", async () => { const { navigateToLogs } = useTraces(); - navigateToLogs({ + await navigateToLogs({ stream: "default", from: 1000, to: 2000, @@ -502,9 +503,9 @@ describe("useTraces", () => { ); }); - it("passes all query parameters to router.push", () => { + it("passes all query parameters to router.push", async () => { const { navigateToLogs } = useTraces(); - navigateToLogs({ + await navigateToLogs({ stream: "my-stream", from: 500, to: 1000, diff --git a/web/src/composables/useTraces.ts b/web/src/composables/useTraces.ts index 1f0791386d..6a574faef4 100644 --- a/web/src/composables/useTraces.ts +++ b/web/src/composables/useTraces.ts @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { reactive, computed } from "vue"; +import { reactive, computed, nextTick } from "vue"; import { b64EncodeStandard, b64EncodeUnicode, @@ -343,8 +343,8 @@ const useTraces = () => { return { stream: searchObj.data.traceDetails.selectedLogStreams.join(","), - from: span.startTimeMs * 1000 - 60000000, - to: span.endTimeMs * 1000 + 60000000, + from: Math.floor(span.start_time / 1000) - 60000000, + to: Math.ceil(span.end_time / 1000) + 60000000, refresh: 0, query, orgIdentifier: store.state.selectedOrganization.identifier, @@ -352,7 +352,9 @@ const useTraces = () => { }; // Function to navigate to logs with the provided query details - const navigateToLogs = (queryDetails: any) => { + const navigateToLogs = async (queryDetails: any) => { + store.dispatch("logs/setIsInitialized", false); + await nextTick(); router.push({ path: "/logs", query: { diff --git a/web/src/plugins/logs/Index.vue b/web/src/plugins/logs/Index.vue index 516617868d..bb652ab36a 100644 --- a/web/src/plugins/logs/Index.vue +++ b/web/src/plugins/logs/Index.vue @@ -1148,7 +1148,7 @@ export default defineComponent({ searchObj.meta.showHistogram = isHistogramEnabled(); - restoreUrlQueryParams(dashboardPanelData); + await restoreUrlQueryParams(dashboardPanelData); if ( store.state.zoConfig?.auto_query_enabled && @@ -1302,21 +1302,21 @@ export default defineComponent({ }; // Helper function for handling the trace explorer - function handleTraceExplorer(queryParams) { + async function handleTraceExplorer(queryParams) { searchObj.organizationIdentifier = queryParams.org_identifier; searchObj.data.stream.selectedStream.value = queryParams.stream; searchObj.data.stream.streamType = queryParams.stream_type; resetSearchObj(); resetStreamData(); - restoreUrlQueryParams(dashboardPanelData); + await restoreUrlQueryParams(dashboardPanelData); loadLogsData(); } // Helper function for handling the stream explorer - function handleStreamExplorer() { + async function handleStreamExplorer() { resetSearchObj(); resetStreamData(); - restoreUrlQueryParams(dashboardPanelData); + await restoreUrlQueryParams(dashboardPanelData); loadLogsData(); } diff --git a/web/src/plugins/logs/IndexList.vue b/web/src/plugins/logs/IndexList.vue index be04529041..695b20e945 100644 --- a/web/src/plugins/logs/IndexList.vue +++ b/web/src/plugins/logs/IndexList.vue @@ -24,7 +24,10 @@ along with this program. If not, see . style="max-width: 100%; overflow: hidden" > . data-cy="index-dropdown" :placeholder="placeHolderText" input-debounce="0" - popup-content-style="height: 400px" behavior="menu" borderless dense @@ -73,7 +75,7 @@ along with this program. If not, see .