From 976fced81ac7eb23b9d223cfca64da3ffb231842 Mon Sep 17 00:00:00 2001 From: Omkar Kesarkhane <127078473+omkarK06@users.noreply.github.com> Date: Fri, 12 Sep 2025 10:33:35 +0530 Subject: [PATCH] fix: UI support for uuid-v7 timestamp traceid's in traces (#8286) ### **PR Type** Bug fix, Enhancement ___ ### **Description** - Auto-adjust time range from trace bounds - Sync URL params with trace start/end - Expose method to update SearchBar datetime - Minor cleanup and inline documentation ___ ### Diagram Walkthrough ```mermaid flowchart LR query["Trace query result"] --> check["Has single hit with start/end?"] check -- "yes" --> compute["Compute seconds (floor/ceil)"] compute -- "Index.vue" --> updateSB["SearchBar.updateNewDateTime()"] compute -- "TraceDetails.vue" --> urlUpdate["Update URL from/to"] urlUpdate --> detailsFetch["Fetch trace details with new range"] ```

File Walkthrough

Relevant files
Enhancement
Index.vue
Auto-update time range after single trace query                   

web/src/plugins/traces/Index.vue
  • Detect single-trace queries with bounds.
  • Update SearchBar datetime via new helper.
  • Add updateNewDateTime wrapper with docs.
  • Remove redundant filter expansion call.
+25/-1   
SearchBar.vue
Expose API to programmatically set absolute datetime         

web/src/plugins/traces/SearchBar.vue
  • Add updateNewDateTime method to set absolute range.
  • Ensure datetime ref updates consistently.
  • Minor formatting and comments.
  • Export new method from setup.
+14/-0   
Bug fix
TraceDetails.vue
Sync URL and fetch window with trace time bounds                 

web/src/plugins/traces/TraceDetails.vue
  • Derive start/end from trace meta when available.
  • Normalize to seconds and update URL query.
  • Use updated range for details fetch.
  • Add updateUrlQueryParams helper with docs.
+31/-2   
___ --- web/src/plugins/traces/Index.vue | 40 ++++++++++++++++++++++- web/src/plugins/traces/SearchBar.vue | 14 +++++++++ web/src/plugins/traces/TraceDetails.vue | 42 +++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/web/src/plugins/traces/Index.vue b/web/src/plugins/traces/Index.vue index d02b20c63b..b477810a6a 100644 --- a/web/src/plugins/traces/Index.vue +++ b/web/src/plugins/traces/Index.vue @@ -664,6 +664,27 @@ async function getQueryData() { }) .then(async (res) => { searchObj.loading = false; + + if ( + filter && + filter.includes("trace_id") && + res.data.hits.length === 1 && + res.data.hits[0].start_time && + res.data.hits[0].end_time + ) { + const startTime = Math.floor(res.data.hits[0].start_time / 1000); + const endTime = Math.ceil(res.data.hits[0].end_time / 1000); + // If the trace is not in the current time range, update the time range + if ( + !( + startTime >= queryReq.query.start_time && + endTime <= queryReq.query.end_time + ) + ) { + updateNewDateTime(startTime, endTime); + } + } + const formattedHits = getTracesMetaData(res.data.hits); if (res.data.from > 0) { searchObj.data.queryResults.from = res.data.from; @@ -730,6 +751,24 @@ async function getQueryData() { } } +/** + * + * @param startTime - start time in microseconds + * @param endTime - end time in microseconds + */ +const updateNewDateTime = (startTime: number, endTime: number) => { + searchBarRef.value?.updateNewDateTime({ + startTime: startTime, + endTime: endTime, + }); + $q.notify({ + type: "positive", + message: + "The selected time range did not include this trace. The time range has been updated to match the trace’s timestamp.", + timeout: 5000, + }); +}; + const getTracesMetaData = (traces) => { if (!traces.length) return []; @@ -1158,7 +1197,6 @@ const searchData = () => { } runQueryFn(); - indexListRef.value.filterExpandedFieldValues(); if (config.isCloud == "true") { segment.track("Button Click", { diff --git a/web/src/plugins/traces/SearchBar.vue b/web/src/plugins/traces/SearchBar.vue index 38feee3797..443426e59c 100644 --- a/web/src/plugins/traces/SearchBar.vue +++ b/web/src/plugins/traces/SearchBar.vue @@ -317,6 +317,7 @@ export default defineComponent({ dateTimeRef.value.setDateType("absolute"); } } + searchObj.data.datetime = { startTime: value.startTime, endTime: value.endTime, @@ -407,6 +408,18 @@ export default defineComponent({ emit("shareLink"); }; + /** + * Update the date time in the date time component + * @param value - object containing start time and end time + * @param value.startTime - start time in microseconds + * @param value.endTime - end time in microseconds + */ + const updateNewDateTime = (value: object) => { + if (!value.startTime || !value.endTime) return; + dateTimeRef.value?.setAbsoluteTime(value.startTime, value.endTime); + dateTimeRef.value?.setDateType("absolute"); + }; + return { t, router, @@ -425,6 +438,7 @@ export default defineComponent({ dateTimeRef, resetFilters, shareLink, + updateNewDateTime, }; }, computed: { diff --git a/web/src/plugins/traces/TraceDetails.vue b/web/src/plugins/traces/TraceDetails.vue index 87031da933..5c7d0cf01f 100644 --- a/web/src/plugins/traces/TraceDetails.vue +++ b/web/src/plugins/traces/TraceDetails.vue @@ -706,11 +706,36 @@ export default defineComponent({ return; } searchObj.data.traceDetails.selectedTrace = trace; + + let startTime = Number(router.currentRoute.value.query.from); + let endTime = Number(router.currentRoute.value.query.to); + if ( + res.data.hits.length === 1 && + res.data.hits[0].start_time && + res.data.hits[0].end_time + ) { + startTime = Math.floor(res.data.hits[0].start_time / 1000); + endTime = Math.ceil(res.data.hits[0].end_time / 1000); + + // If the trace is not in the current time range, update the time range + if ( + !( + startTime >= Number(router.currentRoute.value.query.from) && + endTime <= Number(router.currentRoute.value.query.to) + ) + ) { + updateUrlQueryParams({ + from: startTime, + to: endTime, + }); + } + } + getTraceDetails({ stream: streamName, trace_id: trace.trace_id, - from: Number(router.currentRoute.value.query.from), - to: Number(router.currentRoute.value.query.to), + from: startTime, + to: endTime, }); }) .catch(() => { @@ -726,6 +751,19 @@ export default defineComponent({ } }; + /** + * Update the query parameters in the URL + * @param newParams - object containing new parameters + */ + const updateUrlQueryParams = (newParams: any) => { + router.replace({ + query: { + ...router.currentRoute.value.query, // keep existing params + ...newParams, // overwrite with new ones + }, + }); + }; + const getDefaultRequest = () => { return { query: {