Skip to content

Commit

Permalink
fix(tracing): overall and per-span latency display (#1871)
Browse files Browse the repository at this point in the history
* fix(tracing): overall and per-span latency display

* fix(tracing): update and reorganize sampler attributes

* fix(tracing): remove unused color constants

* fix(tracing): output type

* fix(tracing): add more span attribute keys

* fix(tracing): move all latency attributes to the dedicated section

* fix(tracing): revert

* fix(tracing): address feedback on latency display
  • Loading branch information
sumimakito authored Dec 26, 2024
1 parent 8f71361 commit 0b418a4
Show file tree
Hide file tree
Showing 20 changed files with 495 additions and 481 deletions.
5 changes: 5 additions & 0 deletions packages/core/tracing/sandbox/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@
</div>
</template>

<style lang="scss" scoped>
body {
overscroll-behavior-y: none;
}
</style>
2 changes: 1 addition & 1 deletion packages/core/tracing/sandbox/fixtures/trace-batches.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions packages/core/tracing/src/components/trace-viewer/SpanAttribute.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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, SpanAttributeKeys, TRACE_VIEWER_CONFIG } from '../../constants'
import { SPAN_ATTRIBUTE_KEYS, SPAN_ATTRIBUTE_VALUE_UNKNOWN, TRACE_VIEWER_CONFIG } from '../../constants'
import type { EntityRequest, IKeyValue, SpanNode, TraceViewerConfig } from '../../types'
import { getPhaseAndPlugin, unwrapAnyValue } from '../../utils'
Expand Down Expand Up @@ -75,12 +75,12 @@ 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: 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',
[SPAN_ATTRIBUTE_KEYS.KONG_SERVICE_ID]: 'services',
[SPAN_ATTRIBUTE_KEYS.KONG_ROUTE_ID]: 'routes',
[SPAN_ATTRIBUTE_KEYS.KONG_CONSUMER_ID]: 'consumers',
[SPAN_ATTRIBUTE_KEYS.KONG_PLUGIN_ID]: 'plugins',
[SPAN_ATTRIBUTE_KEYS.KONG_TARGET_ID]: 'targets',
[SPAN_ATTRIBUTE_KEYS.KONG_UPSTREAM_ID]: 'upstreams',
}
// Let's only make the attributes listed above copyable, for now.
Expand Down Expand Up @@ -110,7 +110,7 @@ const entityRequest = computed(() => {
}
switch (props.keyValue.key) {
case SpanAttributeKeys.KONG_PLUGIN_ID: {
case SPAN_ATTRIBUTE_KEYS.KONG_PLUGIN_ID: {
// We will need to parse the plugin name from the span name
request.plugin = getPhaseAndPlugin(props.span.name)?.plugin
if (!request.plugin) {
Expand All @@ -120,8 +120,8 @@ const entityRequest = computed(() => {
}
break
}
case SpanAttributeKeys.KONG_TARGET_ID: {
const upstreamIdAttrValue = props.span.attributes?.find((attr) => attr.key === SpanAttributeKeys.KONG_UPSTREAM_ID)?.value
case SPAN_ATTRIBUTE_KEYS.KONG_TARGET_ID: {
const upstreamIdAttrValue = props.span.attributes?.find((attr) => attr.key === SPAN_ATTRIBUTE_KEYS.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 @@ -13,9 +13,9 @@
:span="span"
/>

<template v-if="span.attributes">
<template v-if="filteredAttributes.length > 0">
<SpanAttribute
v-for="keyValue in span.attributes"
v-for="keyValue in filteredAttributes"
:key="keyValue.key"
:key-value="keyValue"
:span="span"
Expand All @@ -28,7 +28,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import composables from '../../composables'
import { SpanAttributeKeys, WATERFALL_ROW_PADDING_X } from '../../constants'
import { SPAN_ATTR_KEY_KONG_LATENCY_PREFIX, WATERFALL_ROW_PADDING_X } from '../../constants'
import type { IKeyValue, SpanNode } from '../../types'
import { formatNanoDateTimeString } from '../../utils'
import SpanAttribute from './SpanAttribute.vue'
Expand All @@ -40,21 +40,32 @@ const props = defineProps<{ span: SpanNode['span'] }>()
const internalAttributes = computed<(IKeyValue & { label?: string })[]>(() => {
return [
{
key: SpanAttributeKeys._INTERNAL_START_TIME,
// Hardcoding the key here as it's not used elsewhere
key: '_internal.start_time',
value: {
stringValue: formatNanoDateTimeString(props.span.startTimeUnixNano),
},
label: t('span_attributes.labels.start_time'),
},
{
key: SpanAttributeKeys._INTERNAL_END_TIME,
// Hardcoding the key here as it's not used elsewhere
key: '_internal.end_time',
value: {
stringValue: formatNanoDateTimeString(props.span.endTimeUnixNano),
},
label: t('span_attributes.labels.end_time'),
},
]
})
const filteredAttributes = computed(() => {
if (!props.span.attributes) {
return []
}
return props.span.attributes
.filter((attr) => !attr.key.startsWith(SPAN_ATTR_KEY_KONG_LATENCY_PREFIX))
})
</script>

<style lang="scss" scoped>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
<template>
<KCard class="span-description-card">
<p class="span-description">
<span class="name">
{{ span.name }}:
</span>
<span :class="['description', { 'not-available': !description }]">
{{ description || t('trace_viewer.no_span_description') }}
</span>
</p>
<KCard class="span-basic-info">
<div class="rows">
<div class="label">
{{ t('trace_viewer.span_basic_info.labels.name') }}
</div>
<div class="value">
{{ span.name }}
</div>

<template v-if="description">
<div class="label">
{{ t('trace_viewer.span_basic_info.labels.description') }}
</div>
<div class="value">
{{ description }}
</div>
</template>
</div>

<KExternalLink
v-if="description"
Expand All @@ -34,29 +43,28 @@ const description = computed(() => {
const pluginSpan = getPhaseAndPlugin(props.span.name)
// We will use general description for plugin spans that exactly match `kong.(phase).plugin.(plugin)`.
const subI18nKey = pluginSpan && !pluginSpan.suffix ? `kong.${pluginSpan.phase}.plugin` : props.span.name
const i18nKey = `trace_viewer.span_descriptions.${subI18nKey}`
const i18nKey = `trace_viewer.span_basic_info.descriptions.${subI18nKey}.$`
return te(i18nKey as any) ? t(i18nKey as any) : undefined
})
</script>

<style lang="scss" scoped>
.span-description-card {
padding: $kui-space-50;
}
.span-basic-info {
padding: $kui-space-60;
.span-description {
margin: 0;
.name {
font-weight: $kui-font-weight-semibold;
}
.rows {
display: grid;
gap: $kui-space-20 $kui-space-40;
grid-template-columns: min-content auto;
.description.not-available {
font-style: italic;
.label {
font-weight: $kui-font-weight-semibold;
}
}
}
.docs-link {
align-self: flex-end;
margin-top: $kui-space-40;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import composables from '../../composables'
import { SPAN_EVENT_ATTRIBUTES } from '../../constants'
import { SPAN_EVENT_ATTRIBUTE_KEYS } from '../../constants'
import type { Event } from '../../types'
const { i18n: { t } } = composables.useI18n()
Expand All @@ -22,7 +22,7 @@ const props = defineProps<{ event: Event }>()
// We only have exception-typed events for now
const exceptionMessage = computed(() =>
props.event.attributes?.find(
(keyValue) => keyValue.key === SPAN_EVENT_ATTRIBUTES.EXCEPTION_MESSAGE.name,
(keyValue) => keyValue.key === SPAN_EVENT_ATTRIBUTE_KEYS.EXCEPTION_MESSAGE,
)?.value.stringValue,
)
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<template>
<div
v-if="latencies.length > 0"
class="span-latency-table"
>
<div class="title">
{{ t('trace_viewer.latency.section_title') }}
</div>

<div class="latencies">
<template
v-for="latency in latencies"
:key="latency.key"
>
<ConfigCardItem
:item="{
type: ConfigurationSchemaType.Text,
key: latency.key,
label: latency.labelKey ? t(latency.labelKey) : latency.key,
value: formatLatency(latency.milliseconds),
}"
/>

<template v-if="Array.isArray(latency.children) && latency.children.length > 0">
<ConfigCardItem
v-for="child in latency.children"
:key="child.key"
class="latency-nested"
:item="{
type: ConfigurationSchemaType.Text,
key: child.key,
label: child.labelKey ? t(child.labelKey) : child.key,
value: formatLatency(child.milliseconds),
}"
/>
</template>
</template>
</div>
</div>
</template>

<script setup lang="ts">
import { ConfigCardItem, ConfigurationSchemaType } from '@kong-ui-public/entities-shared'
import { computed } from 'vue'
import composables from '../../composables'
import { WATERFALL_ROW_PADDING_X } from '../../constants'
import type { SpanNode } from '../../types'
import { formatLatency, toSpanLatencies } from '../../utils'
const { i18n: { t } } = composables.useI18n()
const props = defineProps<{ span: SpanNode['span'] }>()
const latencies = computed(() => toSpanLatencies(props.span.attributes))
</script>

<style lang="scss" scoped>
.span-latency-table {
align-items: flex-start;
display: flex;
flex-direction: column;
.title {
background-color: $kui-color-background-neutral-weakest;
border-bottom: 1px solid $kui-color-border-neutral-weaker;
font-size: $kui-font-size-30;
font-weight: $kui-font-weight-semibold;
padding: $kui-space-60 v-bind(WATERFALL_ROW_PADDING_X);
width: 100%;
}
.latencies {
width: 100%;
:deep(.config-card-details-value) {
font-family: $kui-font-family-code;
font-size: $kui-font-size-30;
.copy-text {
font-size: $kui-font-size-30;
}
}
.latency-nested {
:deep(.config-card-details-label) {
padding-left: $kui-space-40;
}
}
}
}
</style>
45 changes: 45 additions & 0 deletions packages/core/tracing/src/components/trace-viewer/TraceLatency.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<div class="trace-latency">
<div class="title">
{{ t('trace_viewer.latency.label') }}
</div>

<div
v-for="latency in latencies"
:key="latency.key"
class="latency"
>
{{ latency.labelKey ? t(latency.labelKey) : latency.key }}: {{ formatLatency(latency.milliseconds) }}
</div>
</div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import composables from '../../composables'
import type { SpanNode } from '../../types'
import { formatLatency, toOverviewLatencies } from '../../utils'
const { i18n: { t } } = composables.useI18n()
const props = defineProps<{ span: SpanNode['span'] }>()
const latencies = computed(() => toOverviewLatencies(props.span.attributes))
</script>

<style lang="scss" scoped>
.trace-latency {
align-items: center;
display: flex;
flex-direction: row;
flex-grow: 1;
flex-shrink: 0;
font-size: $kui-font-size-30;
gap: $kui-space-40;
justify-content: flex-start;
.title {
font-weight: $kui-font-weight-semibold;
}
}
</style>
Loading

0 comments on commit 0b418a4

Please sign in to comment.