Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tracing): overall and per-span latency display #1871

Merged
merged 8 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>

Large diffs are not rendered by default.

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>
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
Loading