This is an automated email from the ASF dual-hosted git repository. wu-sheng pushed a commit to branch feat/metric-trace-drilldown in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
commit 8cc01e7d931b41b72d01bd693b6fe402b05239b6 Author: Wu Sheng <[email protected]> AuthorDate: Thu Jul 2 14:40:58 2026 +0800 fix(traces): drill window snaps to the clicked hour/day bucket The window centered on a reconstructed mid-bucket point and capped the half-window at 6h, so clicking a DAY bucket opened a 12h mid-day slice (08:35–20:35) instead of that day. Now MINUTE → a ±5min neighborhood, HOUR → the whole clicked hour, DAY → the whole clicked day (snapped to the local boundary and spanning one bucket). --- .../render/layer-dashboard/LayerDashboardsView.vue | 33 ++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue b/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue index 223706d..e96d354 100644 --- a/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue +++ b/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue @@ -474,16 +474,28 @@ function traceDrillMode(w: DashboardWidget): 'latency' | 'error' | null { return m === 'latency' || m === 'error' ? m : null; } // Half-window (ms) around the clicked bucket, scaled to step, capped at 6h. -const DRILL_STEP_MS: Record<string, number> = { MINUTE: 60_000, HOUR: 3_600_000, DAY: 86_400_000 }; -function drillHalfWindowMs(): number { - const step = DRILL_STEP_MS[timeRange.step] ?? 60_000; - return Math.min(Math.max(5 * 60_000, step / 2), 6 * 60 * 60_000); -} function drillCenterMs(dataIndex: number, len: number): number { const { startMs, endMs } = timeRange.range; if (len <= 1) return endMs; return Math.round(startMs + ((endMs - startMs) * dataIndex) / (len - 1)); } +// Trace window for the clicked bucket. MINUTE → a ±5min neighborhood; HOUR/DAY +// → the whole clicked hour/day (snapped to the local boundary) so the search +// matches the bucket the operator clicked, not a mid-bucket slice. `labelMs` is +// the bucket start, used for the popover's "around …" label. +function drillWindow(dataIndex: number, len: number): { fromMs: number; toMs: number; labelMs: number } { + const center = drillCenterMs(dataIndex, len); + if (timeRange.step === 'MINUTE') { + const half = 5 * 60_000; + return { fromMs: center - half, toMs: center + half, labelMs: center }; + } + const d = new Date(center); + d.setMinutes(0, 0, 0); + if (timeRange.step === 'DAY') d.setHours(0); + const fromMs = d.getTime(); + const spanMs = timeRange.step === 'DAY' ? 86_400_000 : 3_600_000; + return { fromMs, toMs: fromMs + spanMs, labelMs: fromMs }; +} // datetime-local wall-clock — must match the Traces tab's custom-range format. function toLocalInput(ms: number): string { const d = new Date(ms); @@ -507,15 +519,14 @@ function onDrillPoint( const mode = traceDrillMode(w); if (!mode) return; const len = resultsById.value.get(w.id)?.series?.[0]?.data.length ?? 0; - const center = drillCenterMs(p.dataIndex, len); - const half = drillHalfWindowMs(); + const win = drillWindow(p.dataIndex, len); const ms = Math.max(0, Math.round(p.value)); const query: Record<string, string> = { dMode: mode, - dFrom: toLocalInput(center - half), - dTo: toLocalInput(center + half), + dFrom: toLocalInput(win.fromMs), + dTo: toLocalInput(win.toMs), // Unique per click so a drill→drill navigation on the same tab re-fires. - dNonce: `${center}:${p.dataIndex}:${w.id}`, + dNonce: `${win.fromMs}:${p.dataIndex}:${w.id}`, }; if (mode === 'latency') query.dValue = String(ms); // service (id) seeds the fresh tab's selection; instance/endpoint go by name @@ -531,7 +542,7 @@ function onDrillPoint( title: w.title, meta: band + - t('around {t}', { t: bucketTimeLabel(timeRange.step, center) }) + + t('around {t}', { t: bucketTimeLabel(timeRange.step, win.labelMs) }) + (mode === 'latency' ? ` · ≥ ${ms} ms` : ''), label: mode === 'latency' ? t('View slow traces') : t('View error traces'), };
