Skip to content

Commit

Permalink
fix(tracing): bar calculation and start and end time in span attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
sumimakito committed Dec 11, 2024
1 parent b7e1944 commit c84d4cf
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 155 deletions.
29 changes: 17 additions & 12 deletions packages/core/tracing/src/components/trace-viewer/SpanAttribute.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
:item="{
type: itemType,
key: keyValue.key,
label: keyValue.key,
label: label || keyValue.key,
value: formattedValue,
}"
>
Expand All @@ -30,7 +30,7 @@
import { ConfigCardItem, ConfigurationSchemaType, EntityLink, type EntityLinkData } from '@kong-ui-public/entities-shared'
import { computed, inject, onWatcherCleanup, shallowRef, watch } from 'vue'
import composables from '../../composables'
import { SPAN_ATTRIBUTE_VALUE_UNKNOWN, SPAN_ATTRIBUTES, TRACE_VIEWER_CONFIG } from '../../constants'
import { SPAN_ATTRIBUTE_VALUE_UNKNOWN, SpanAttributeKeys, TRACE_VIEWER_CONFIG } from '../../constants'
import type { EntityRequest, IKeyValue, Span, TraceViewerConfig } from '../../types'
import { getPhaseAndPlugin, unwrapAnyValue } from '../../utils'
Expand All @@ -41,6 +41,11 @@ const { i18n: { t } } = composables.useI18n()
const props = defineProps<{
span: Span
keyValue: IKeyValue
/**
* Label to show for the attribute.
* If omitted, the key will be used as the default label.
*/
label?: string
}>()
const config = inject<TraceViewerConfig>(TRACE_VIEWER_CONFIG)
Expand Down Expand Up @@ -69,13 +74,13 @@ const formattedValue = computed(() => {
// A map of keys of attributes whose values are IDs of entities (services, routes, consumers, etc.)
// to their corresponding entities
const ATTRIBUTE_KEY_TO_ENTITY = {
[SPAN_ATTRIBUTES.KONG_SERVICE_ID.name]: 'services',
[SPAN_ATTRIBUTES.KONG_ROUTE_ID.name]: 'routes',
[SPAN_ATTRIBUTES.KONG_CONSUMER_ID.name]: 'consumers',
[SPAN_ATTRIBUTES.KONG_PLUGIN_ID.name]: 'plugins',
[SPAN_ATTRIBUTES.KONG_TARGET_ID.name]: 'targets',
[SPAN_ATTRIBUTES.KONG_UPSTREAM_ID.name]: 'upstreams',
const ATTRIBUTE_KEY_TO_ENTITY: Record<string, string> = {
[SpanAttributeKeys.KONG_SERVICE_ID]: 'services',
[SpanAttributeKeys.KONG_ROUTE_ID]: 'routes',
[SpanAttributeKeys.KONG_CONSUMER_ID]: 'consumers',
[SpanAttributeKeys.KONG_PLUGIN_ID]: 'plugins',
[SpanAttributeKeys.KONG_TARGET_ID]: 'targets',
[SpanAttributeKeys.KONG_UPSTREAM_ID]: 'upstreams',
}
// Let's only make the attributes listed above copyable, for now.
Expand Down Expand Up @@ -105,7 +110,7 @@ const entityRequest = computed(() => {
}
switch (props.keyValue.key) {
case SPAN_ATTRIBUTES.KONG_PLUGIN_ID.name: {
case SpanAttributeKeys.KONG_PLUGIN_ID: {
// We will need to parse the plugin name from the span name
request.plugin = getPhaseAndPlugin(props.span.name)?.[1]
if (!request.plugin) {
Expand All @@ -115,8 +120,8 @@ const entityRequest = computed(() => {
}
break
}
case SPAN_ATTRIBUTES.KONG_TARGET_ID.name: {
const upstreamIdAttrValue = props.span.attributes?.find((attr) => attr.key === SPAN_ATTRIBUTES.KONG_UPSTREAM_ID.name)?.value
case SpanAttributeKeys.KONG_TARGET_ID: {
const upstreamIdAttrValue = props.span.attributes?.find((attr) => attr.key === SpanAttributeKeys.KONG_UPSTREAM_ID)?.value
const upstreamId = upstreamIdAttrValue && unwrapAnyValue<string>(upstreamIdAttrValue)
if (!upstreamId) {
console.warn(`Failed to look up the upstream ID for the upstream target in the span "${props.span.name}"`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
</div>

<div class="attributes">
<SpanAttribute
v-for="attributes in internalAttributes"
:key="attributes.key"
:key-value="attributes"
:label="attributes.label"
:span="span"
/>

<SpanAttribute
v-for="keyValue in span.attributes"
:key="keyValue.key"
Expand All @@ -19,14 +27,35 @@
</template>

<script setup lang="ts">
import { computed } from 'vue'
import composables from '../../composables'
import { WATERFALL_ROW_PADDING_X } from '../../constants'
import type { Span } from '../../types'
import { SpanAttributeKeys, WATERFALL_ROW_PADDING_X } from '../../constants'
import type { IKeyValue, Span } from '../../types'
import { formatNanoDateTimeString } from '../../utils'
import SpanAttribute from './SpanAttribute.vue'
const { i18n: { t } } = composables.useI18n()
defineProps<{ span: Span }>()
const props = defineProps<{ span: Span }>()
const internalAttributes = computed<(IKeyValue & { label?: string })[]>(() => {
return [
{
key: SpanAttributeKeys._INTERNAL_START_TIME,
value: {
stringValue: formatNanoDateTimeString(BigInt(props.span.startTimeUnixNano)),
},
label: t('span_attributes.labels.start_time'),
},
{
key: SpanAttributeKeys._INTERNAL_END_TIME,
value: {
stringValue: formatNanoDateTimeString(BigInt(props.span.endTimeUnixNano)),
},
label: t('span_attributes.labels.end_time'),
},
]
})
</script>

<style lang="scss" scoped>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { computed, inject } from 'vue'
import { WATERFALL_CONFIG, WATERFALL_LEGENDS, WaterfallLegendItemKind } from '../../constants'
import type { SpanNode, WaterfallConfig } from '../../types'
const MIN_WIDTH = 1
const config = inject<WaterfallConfig>(WATERFALL_CONFIG)
if (!config) {
throw new Error('WATERFALL_CONFIG is not provided')
Expand Down Expand Up @@ -38,13 +40,33 @@ const barColor = computed(() => {
})
// RESERVED: Only used when zooming is enabled
const relativeStartTimeNano = computed(() => Number(BigInt(props.spanNode.span.startTimeUnixNano) - config.startTimeUnixNano))
const relativeStartTimeNano = computed(() =>
Number(BigInt(props.spanNode.span.startTimeUnixNano) - (config.root?.subtreeValues.startTimeUnixNano ?? 0n)),
)
const barFixedLeft = computed(() => (relativeStartTimeNano.value / config.totalDurationNano) * config.zoom)
const barShiftLeft = computed(() => -config.viewport.left * config.zoom)
const barLeft = computed(() => `min(100% - 3px, ${(barFixedLeft.value + barShiftLeft.value) * 100}%)`)
const barWidth = computed(() => {
return `max(3px, ${props.spanNode.durationNano / config.totalDurationNano * config.zoom * 100}%)`
const barLeft = computed(() => `min(100% - ${MIN_WIDTH}px, calc((100% - ${MIN_WIDTH}px) * ${(barFixedLeft.value + barShiftLeft.value)})`)
const barExtraWidthFactor = computed(() => {
if (!config.root) {
return 0
}
/**
* 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 (props.spanNode.durationNano - config.root.subtreeValues.minDurationNano)
/ (config.totalDurationNano - config.root.subtreeValues.minDurationNano)
})
const barWidth = computed(() =>
`calc((${MIN_WIDTH}px + (100% - ${MIN_WIDTH}px) * ${barExtraWidthFactor.value}) * ${config.zoom})`,
)
</script>

<style lang="scss" scoped>
Expand Down
12 changes: 8 additions & 4 deletions packages/core/tracing/src/components/waterfall/WaterfallView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,14 @@ const spanBarMeasurementRef = useTemplateRef<HTMLElement>('spanBarMeasurement')
const interaction = ref<'scroll' | 'zoom'>('scroll')
const rowsAreaGuideX = ref<number | undefined>(undefined)
const config = reactive<MarkReactiveInputRefs<WaterfallConfig, 'ticks' | 'totalDurationNano' | 'startTimeUnixNano'>>({
ticks: toRef(props, 'ticks'), // This will be unwrapped
totalDurationNano: computed(() => props.rootSpan?.durationNano ?? 0), // This will be unwrapped
startTimeUnixNano: computed(() => props.rootSpan ? BigInt(props.rootSpan.span.startTimeUnixNano) : 0n), // This will be unwrapped
const config = reactive<MarkReactiveInputRefs<WaterfallConfig, 'ticks' | 'root' | 'totalDurationNano'>>({
ticks: toRef(props, 'ticks'),
root: toRef(props, 'rootSpan'),
totalDurationNano: computed(() =>
props.rootSpan
? Number(props.rootSpan.subtreeValues.endTimeUnixNano - props.rootSpan.subtreeValues.startTimeUnixNano)
: 0,
),
zoom: 1,
viewportShift: 0,
viewport: { left: 0, right: 0 },
Expand Down
Loading

0 comments on commit c84d4cf

Please sign in to comment.