From 3cbb8f7e4a774073a760cff733f0772f1fe4fbb5 Mon Sep 17 00:00:00 2001 From: Makito Date: Wed, 18 Dec 2024 18:16:44 +0800 Subject: [PATCH] fix(tracing): incorrect span bar position and undefined access error --- .../components/waterfall/WaterfallSpanBar.vue | 108 +++--------------- .../components/waterfall/WaterfallView.vue | 24 ++-- packages/core/tracing/src/types/spans.ts | 5 - packages/core/tracing/src/utils/spans.ts | 6 - 4 files changed, 28 insertions(+), 115 deletions(-) diff --git a/packages/core/tracing/src/components/waterfall/WaterfallSpanBar.vue b/packages/core/tracing/src/components/waterfall/WaterfallSpanBar.vue index 0d5ce7f496..0104ebc9f5 100644 --- a/packages/core/tracing/src/components/waterfall/WaterfallSpanBar.vue +++ b/packages/core/tracing/src/components/waterfall/WaterfallSpanBar.vue @@ -7,8 +7,6 @@ import { computed, inject } from 'vue' import { WATERFALL_CONFIG, WATERFALL_LEGENDS, WATERFALL_SPAN_BAR_FADING_WIDTH, WaterfallLegendItemKind } from '../../constants' import type { SpanNode, WaterfallConfig } from '../../types' -const MIN_WIDTH = 1 - const config = inject(WATERFALL_CONFIG) if (!config) { throw new Error('WATERFALL_CONFIG is not provided') @@ -39,105 +37,30 @@ const barColor = computed(() => { return WATERFALL_LEGENDS[WaterfallLegendItemKind.KONG].color }) -/** - * Left position of the span bar in the whole trace, presented by a ratio where 0 marks the left-most - * position and 1 marks the right-most position. It is calculated by the following formula: - * - * ``` - * (span_start_time - root_start_time) / (total_duration - minimal_duration) - * ^ reserve space for spans that are too short - * ``` - * - * Note: The value is calculated without the zoom factor to reduce unnecessary recalculations. - */ -const barUnscaledLeftBaseRatio = computed(() => { +const barEdges = computed(() => { const spanStart = props.spanNode.span.startTimeUnixNano - const subtreeStart = config.root?.subtreeValues.startTimeUnixNano - const subtreeMin = config.root?.subtreeValues.minDurationNano - - if (spanStart === undefined || subtreeStart === undefined || subtreeMin === undefined) { - return 0 - } + const spanEnd = props.spanNode.span.endTimeUnixNano - const relativeStart = Number(spanStart - subtreeStart) - - return (relativeStart / (config.totalDurationNano - subtreeMin)) -}) + const traceStart = config.root?.subtreeValues.startTimeUnixNano + const traceEnd = config.root?.subtreeValues.endTimeUnixNano -/** - * Scaled left position of the span bar with the zoom factor applied. - */ -const barLeftBaseRatio = computed(() => barUnscaledLeftBaseRatio.value * config.zoom) - -/** - * Scaled viewport left position with the zoom factor applied. It is calculated by the following formula: - */ -const viewportLeftRatio = computed(() => config.viewport.left * config.zoom) - -/** - * Left position of the span bar in the whole trace. This is the raw left position that may overflow - * the viewport a lot, which may bring performance issues. - */ -const barUnclampedLeft = computed(() => { - if (!config.root) { - return 0 + if (spanStart === undefined || spanEnd === undefined || traceStart === undefined || traceEnd === undefined) { + return { left: 0, right: 0 } } - return `calc((100% - ${MIN_WIDTH}px) * ${barLeftBaseRatio.value} - 100% * ${viewportLeftRatio.value})` -}) - -/** - * Clamped left position of the span bar within the viewport with fading edges on both ends, to ensure - * the performance. It is clamped in the following range: - * - * ``` - * -fading_width <= position <= 100% + fading_width / 2 - * ``` - */ -const barLeft = computed(() => - `min(calc(100% + ${WATERFALL_SPAN_BAR_FADING_WIDTH} / 2), max(-${WATERFALL_SPAN_BAR_FADING_WIDTH}, ${barUnclampedLeft.value}))`, -) - -const barExtraWidthFactor = computed(() => { - const spanDuration = props.spanNode.durationNano - const subtreeMin = config.root?.subtreeValues.minDurationNano - - if (spanDuration === undefined || subtreeMin === undefined) { - return 0 - } + const left = (Number(spanStart - traceStart) / (config.totalDurationNano)) + const right = (Number(traceEnd - spanEnd) / (config.totalDurationNano)) - /** - * We will make minDuration as `MIN_WIDTH`. Therefore, we will calculate the "extra" width besides the - * `MIN_WIDTH`. We will present this extra width as a factor. - * - * e.g. - * - The total duration should take `MIN_WIDTH + (100% - MIN_WIDTH) * 1` which is `100%` (factor = 1) - * - The minimal duration should take `MIN_WIDTH + (100% - MIN_WIDTH) * 0` which is `MIN_WIDTH` (factor = 0) - * - * Therefore, the formula to calculate the factor for a span is: - * (span duration - minimal duration) / (total duration - minimal duration)` - */ - return (spanDuration - subtreeMin) / (config.totalDurationNano - subtreeMin) + return { left, right } }) -/** - * Clamped right position of the span bar within the viewport with fading edges on both ends, to ensure - * the performance. It is clamped in the following range: - * - * ``` - * -fading_width <= position <= 100% + fading_width / 2 - * ``` - */ -const barRight = computed(() => { - // The unclamped but scaled width of the span bar with zoom factor applied - const unclampedWidth = `calc((${MIN_WIDTH}px + (100% - ${MIN_WIDTH}px) * ${barExtraWidthFactor.value}) * ${config.zoom})` - - /** - * Right position of the span bar in the whole trace. This is the raw right position that may overflow - * the viewport a lot, which may bring performance issues. - */ - const unclampedRight = `calc(100% - ${barUnclampedLeft.value} - ${unclampedWidth})` +const barLeft = computed(() => { + const unclampedLeft = `calc((100% * ${config.zoom}) * (${barEdges.value.left} - ${config.viewport.left}))` + return `min(calc(100% + ${WATERFALL_SPAN_BAR_FADING_WIDTH} / 2), max(-${WATERFALL_SPAN_BAR_FADING_WIDTH}, ${unclampedLeft}))` +}) +const barRight = computed(() =>{ + const unclampedRight = `calc((100% * ${config.zoom}) * (${barEdges.value.right} - ${config.viewport.right}))` return `min(calc(100% + ${WATERFALL_SPAN_BAR_FADING_WIDTH} / 2), max(-${WATERFALL_SPAN_BAR_FADING_WIDTH}, ${unclampedRight}))` }) @@ -155,6 +78,7 @@ const barRight = computed(() => { content: ''; height: 100%; left: v-bind(barLeft); + min-width: 1px; position: absolute; right: v-bind(barRight); top: 0; diff --git a/packages/core/tracing/src/components/waterfall/WaterfallView.vue b/packages/core/tracing/src/components/waterfall/WaterfallView.vue index c65a3163f7..92f89b1649 100644 --- a/packages/core/tracing/src/components/waterfall/WaterfallView.vue +++ b/packages/core/tracing/src/components/waterfall/WaterfallView.vue @@ -17,9 +17,7 @@
-
+
@@ -145,9 +143,10 @@ const getRowsAreaRect = () => rowsAreaRef.value?.getBoundingClientRect() const getSBMRect = () => spanBarMeasurementRef.value?.getBoundingClientRect() const handleRowsAreaMove = (e: MouseEvent) => { - const rowsAreaRect = getRowsAreaRect()! - const sbmRect = getSBMRect()! - if (sbmRect.x < e.x && e.x <= sbmRect.x + sbmRect.width) { + const rowsAreaRect = getRowsAreaRect() + const sbmRect = getSBMRect() + + if (rowsAreaRect && sbmRect && sbmRect.x < e.x && e.x <= sbmRect.x + sbmRect.width) { rowsAreaGuideX.value = e.x - (rowsAreaRect.x ?? 0) } else { rowsAreaGuideX.value = undefined @@ -169,11 +168,12 @@ watch(() => config.selectedSpan, (span) => { const handleWheel = (e: WheelEvent) => { e.preventDefault() - const sbmRect = getSBMRect()! - if (e.x < sbmRect.x) { - if (rootRef.value) { - rootRef.value.scrollBy(0, e.deltaY) - } + const sbmRect = getSBMRect() + if (!sbmRect) { + // in case + return + } else if (e.x < sbmRect.x) { + rootRef.value?.scrollBy(0, e.deltaY) return } @@ -287,7 +287,7 @@ const handleWheel = (e: WheelEvent) => { } .waterfall-rows { - cursor: v-bind('rowsAreaGuideX !== undefined ? "crosshair" : "crosshair"'); + cursor: crosshair; font-family: $kui-font-family-code; overflow: hidden; position: relative; diff --git a/packages/core/tracing/src/types/spans.ts b/packages/core/tracing/src/types/spans.ts index 2bde42ceee..22acf3e78c 100644 --- a/packages/core/tracing/src/types/spans.ts +++ b/packages/core/tracing/src/types/spans.ts @@ -47,11 +47,6 @@ export interface SpanNode { * The latest end time among all nodes in the subtree */ endTimeUnixNano?: bigint - /** - * The minimum duration among all nodes in the subtree. - * Default to the duration of the span itself. - */ - minDurationNano?: number } } diff --git a/packages/core/tracing/src/utils/spans.ts b/packages/core/tracing/src/utils/spans.ts index ce3d497790..7989801648 100644 --- a/packages/core/tracing/src/utils/spans.ts +++ b/packages/core/tracing/src/utils/spans.ts @@ -62,7 +62,6 @@ export const buildSpanTrees = (spans: Span[]): SpanNode[] => { subtreeValues: { startTimeUnixNano, endTimeUnixNano, - minDurationNano: durationNano, }, } node.span.attributes?.sort((a, b) => a.key.localeCompare(b.key)) @@ -76,11 +75,6 @@ export const buildSpanTrees = (spans: Span[]): SpanNode[] => { const parent = nodes.get(node.span.parentSpanId!)! parent.children.push(node) // Update subtree values when necessary - if (node.subtreeValues.minDurationNano !== undefined - && (parent.subtreeValues.minDurationNano === undefined - || node.subtreeValues.minDurationNano < parent.subtreeValues.minDurationNano)) { - parent.subtreeValues.minDurationNano = node.subtreeValues.minDurationNano - } if (node.subtreeValues.startTimeUnixNano !== undefined && (parent.subtreeValues.startTimeUnixNano === undefined || node.subtreeValues.startTimeUnixNano < parent.subtreeValues.startTimeUnixNano)) {