From 7aef327d2e36d34337bd1bcff0b137c9365a5166 Mon Sep 17 00:00:00 2001 From: Fine0830 Date: Mon, 22 Jan 2024 22:07:03 +0800 Subject: [PATCH 1/4] fix: resizing window causes the trace graph to display incorrectly (#367) --- src/utils/debounce.ts | 29 +++++++++++++++++++ src/views/dashboard/controls/Trace.vue | 4 +-- src/views/dashboard/related/trace/Detail.vue | 3 +- .../dashboard/related/trace/TraceList.vue | 5 ++-- .../trace/components/D3Graph/Index.vue | 19 ++++++++---- .../related/trace/utils/d3-trace-list.ts | 12 +------- .../related/trace/utils/d3-trace-tree.ts | 12 +------- 7 files changed, 50 insertions(+), 34 deletions(-) create mode 100644 src/utils/debounce.ts diff --git a/src/utils/debounce.ts b/src/utils/debounce.ts new file mode 100644 index 00000000..83cc8b0f --- /dev/null +++ b/src/utils/debounce.ts @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function debounce(callback: Function, dur: number) { + let timer: any; + + return function () { + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(function () { + callback(); + }, dur); + }; +} diff --git a/src/views/dashboard/controls/Trace.vue b/src/views/dashboard/controls/Trace.vue index d717b496..4daa614b 100644 --- a/src/views/dashboard/controls/Trace.vue +++ b/src/views/dashboard/controls/Trace.vue @@ -81,7 +81,7 @@ limitations under the License. --> padding: 10px; font-size: $font-size-smaller; border-bottom: 1px solid $border-color; - min-width: 1200px; + min-width: 1000px; } .tools { @@ -101,6 +101,6 @@ limitations under the License. --> min-height: calc(100% - 150px); width: 100%; overflow: auto; - min-width: 1200px; + min-width: 1000px; } diff --git a/src/views/dashboard/related/trace/Detail.vue b/src/views/dashboard/related/trace/Detail.vue index f58f80fc..c5c1ea2a 100644 --- a/src/views/dashboard/related/trace/Detail.vue +++ b/src/views/dashboard/related/trace/Detail.vue @@ -172,9 +172,8 @@ limitations under the License. --> } .trace-chart { - height: calc(100% - 100px); + height: calc(100% - 95px); overflow: auto; - padding-bottom: 20px; } .trace-detail-wrapper { diff --git a/src/views/dashboard/related/trace/TraceList.vue b/src/views/dashboard/related/trace/TraceList.vue index 2ec6016d..6b2ca76a 100644 --- a/src/views/dashboard/related/trace/TraceList.vue +++ b/src/views/dashboard/related/trace/TraceList.vue @@ -159,6 +159,7 @@ limitations under the License. --> .selectors { margin: 2px 2px 0 0; + width: 120px; } .trace-t-wrapper { @@ -182,11 +183,11 @@ limitations under the License. --> } .trace-t { - width: 420px; + width: 300px; } .list { - width: 300px; + width: 280px; } .trace-tr { diff --git a/src/views/dashboard/related/trace/components/D3Graph/Index.vue b/src/views/dashboard/related/trace/components/D3Graph/Index.vue index f848f8c2..c96c04b7 100644 --- a/src/views/dashboard/related/trace/components/D3Graph/Index.vue +++ b/src/views/dashboard/related/trace/components/D3Graph/Index.vue @@ -29,6 +29,7 @@ limitations under the License. --> import type { Span, Ref } from "@/types/trace"; import SpanDetail from "./SpanDetail.vue"; import { useAppStoreWithOut } from "@/store/modules/app"; + import { debounce } from "@/utils/debounce"; /* global defineProps, Nullable, defineExpose,Recordable*/ const props = defineProps({ @@ -45,6 +46,8 @@ limitations under the License. --> const refSpans = ref>([]); const tree = ref>(null); const traceGraph = ref>(null); + const debounceFunc = debounce(draw, 500); + defineExpose({ tree, }); @@ -55,6 +58,15 @@ limitations under the License. --> loading.value = false; return; } + draw(); + loading.value = false; + window.addEventListener("resize", debounceFunc); + }); + + function draw() { + if (!traceGraph.value) { + return; + } if (props.type === "List") { tree.value = new ListGraph(traceGraph.value, handleSelectSpan); tree.value.init({ label: "TRACE_ROOT", children: segmentId.value }, props.data, fixSpansSize.value); @@ -63,11 +75,6 @@ limitations under the License. --> tree.value = new TreeGraph(traceGraph.value, handleSelectSpan); tree.value.init({ label: `${props.traceId}`, children: segmentId.value }, props.data); } - loading.value = false; - window.addEventListener("resize", resize); - }); - function resize() { - tree.value.resize(); } function handleSelectSpan(i: Recordable) { currentSpan.value = i.data; @@ -273,7 +280,7 @@ limitations under the License. --> } onBeforeUnmount(() => { d3.selectAll(".d3-tip").remove(); - window.removeEventListener("resize", resize); + window.removeEventListener("resize", debounceFunc); }); watch( () => props.data, diff --git a/src/views/dashboard/related/trace/utils/d3-trace-list.ts b/src/views/dashboard/related/trace/utils/d3-trace-list.ts index 3d738832..ac244e48 100644 --- a/src/views/dashboard/related/trace/utils/d3-trace-list.ts +++ b/src/views/dashboard/related/trace/utils/d3-trace-list.ts @@ -47,6 +47,7 @@ export default class ListGraph { this.el = el; this.width = el.getBoundingClientRect().width - 10; this.height = el.getBoundingClientRect().height - 10; + d3.select(".trace-list-dowanload").remove(); this.svg = d3 .select(this.el) .append("svg") @@ -384,15 +385,4 @@ export default class ListGraph { visDate(date: number, pattern = "YYYY-MM-DD HH:mm:ss:SSS") { return dayjs(date).format(pattern); } - resize() { - if (!this.el) { - return; - } - this.width = this.el.getBoundingClientRect().width - 20; - this.height = this.el.getBoundingClientRect().height - 10; - this.svg.attr("width", this.width).attr("height", this.height); - this.svg.select("g").attr("transform", () => `translate(160, 0)`); - const transform = d3.zoomTransform(this.svg).translate(0, 0); - d3.zoom().transform(this.svg, transform); - } } diff --git a/src/views/dashboard/related/trace/utils/d3-trace-tree.ts b/src/views/dashboard/related/trace/utils/d3-trace-tree.ts index e7001ac2..9647e627 100644 --- a/src/views/dashboard/related/trace/utils/d3-trace-tree.ts +++ b/src/views/dashboard/related/trace/utils/d3-trace-tree.ts @@ -55,6 +55,7 @@ export default class TraceMap { this.topChild = []; this.width = el.clientWidth - 20; this.height = el.clientHeight - 30; + d3.select(".d3-trace-tree").remove(); this.body = d3 .select(this.el) .append("svg") @@ -78,17 +79,6 @@ export default class TraceMap { this.svg = this.body.append("g").attr("transform", () => `translate(120, 0)`); this.svg.call(this.tip); } - resize() { - if (!this.el) { - return; - } - this.width = this.el.clientWidth; - this.height = this.el.clientHeight + 100; - this.body.attr("width", this.width).attr("height", this.height); - this.body.select("g").attr("transform", () => `translate(160, 0)`); - const transform = d3.zoomTransform(this.body).translate(0, 0); - d3.zoom().transform(this.body, transform); - } init(data: Recordable, row: Recordable) { this.treemap = d3.tree().size([row.length * 35, this.width]); this.row = row; From 7d24e065e962659cab4e30858628414dbd734785 Mon Sep 17 00:00:00 2001 From: Fine0830 Date: Wed, 24 Jan 2024 16:09:15 +0800 Subject: [PATCH 2/4] feat: add the not found page (#368) --- src/assets/icons/logo-light.svg | 138 ++++++++++++++++++++++++++++++++ src/router/index.ts | 2 + src/router/notFound.ts | 26 ++++++ src/views/NotFound.vue | 33 ++++++++ 4 files changed, 199 insertions(+) create mode 100644 src/assets/icons/logo-light.svg create mode 100644 src/router/notFound.ts create mode 100644 src/views/NotFound.vue diff --git a/src/assets/icons/logo-light.svg b/src/assets/icons/logo-light.svg new file mode 100644 index 00000000..b2a04196 --- /dev/null +++ b/src/assets/icons/logo-light.svg @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/router/index.ts b/src/router/index.ts index 2cfd6c9d..819539e7 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -21,6 +21,7 @@ import { routesMarketplace } from "./marketplace"; import { routesAlarm } from "./alarm"; import routesLayers from "./layer"; import { routesSettings } from "./settings"; +import { routesNotFound } from "./notFound"; const routes: RouteRecordRaw[] = [ ...routesMarketplace, @@ -28,6 +29,7 @@ const routes: RouteRecordRaw[] = [ ...routesAlarm, ...routesDashboard, ...routesSettings, + ...routesNotFound, ]; const router = createRouter({ diff --git a/src/router/notFound.ts b/src/router/notFound.ts new file mode 100644 index 00000000..8efe0da0 --- /dev/null +++ b/src/router/notFound.ts @@ -0,0 +1,26 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import type { RouteRecordRaw } from "vue-router"; +import NotFound from "@/views/NotFound.vue"; + +export const routesNotFound: Array = [ + { + path: "/:pathMatch(.*)*", + name: "NotFound", + component: NotFound, + }, +]; diff --git a/src/views/NotFound.vue b/src/views/NotFound.vue new file mode 100644 index 00000000..fe7bcee9 --- /dev/null +++ b/src/views/NotFound.vue @@ -0,0 +1,33 @@ + + + From 860af150f71f8dd0a2cbf555c8f7bd97954c5e53 Mon Sep 17 00:00:00 2001 From: Fine0830 Date: Mon, 29 Jan 2024 23:09:00 +0800 Subject: [PATCH 3/4] fix: enhance VNode logic and support multiple Trace IDs in span's ref (#369) --- src/hooks/useDashboardsSession.ts | 5 +- src/store/modules/dashboard.ts | 1 - src/views/dashboard/related/trace/Filter.vue | 10 ++- .../dashboard/related/trace/TraceList.vue | 3 +- .../trace/components/D3Graph/Index.vue | 87 ++++++++++--------- .../trace/components/D3Graph/SpanDetail.vue | 37 +++++++- 6 files changed, 95 insertions(+), 48 deletions(-) diff --git a/src/hooks/useDashboardsSession.ts b/src/hooks/useDashboardsSession.ts index a97d7c33..3fc9d1af 100644 --- a/src/hooks/useDashboardsSession.ts +++ b/src/hooks/useDashboardsSession.ts @@ -24,7 +24,7 @@ export default function getDashboard(param?: { name?: string; layer: string; ent const dashboardStore = useDashboardStore(); const opt = param || dashboardStore.currentDashboard; const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]"); - let dashboard; + let dashboard: Recordable; if (type === ConfigFieldTypes.NAME) { dashboard = list.find( (d: { name: string; layer: string; entity: string }) => @@ -62,6 +62,9 @@ export default function getDashboard(param?: { name?: string; layer: string; ent filters, }; dashboardStore.setWidget(item); + if (widget.id === sourceId) { + return; + } const targetTabIndex = (widget.id || "").split("-"); const sourceTabindex = (sourceId || "").split("-") || []; let container: Nullable; diff --git a/src/store/modules/dashboard.ts b/src/store/modules/dashboard.ts index c784acee..46f6cac3 100644 --- a/src/store/modules/dashboard.ts +++ b/src/store/modules/dashboard.ts @@ -24,7 +24,6 @@ import { useSelectorStore } from "@/store/modules/selectors"; import { NewControl, TextConfig, TimeRangeConfig, ControlsTypes } from "../data"; import type { AxiosResponse } from "axios"; import { ElMessage } from "element-plus"; -import { useI18n } from "vue-i18n"; import { EntityType, MetricModes, WidgetType } from "@/views/dashboard/data"; interface DashboardState { showConfig: boolean; diff --git a/src/views/dashboard/related/trace/Filter.vue b/src/views/dashboard/related/trace/Filter.vue index b9eeb38f..0ca285a1 100644 --- a/src/views/dashboard/related/trace/Filter.vue +++ b/src/views/dashboard/related/trace/Filter.vue @@ -150,7 +150,13 @@ limitations under the License. --> ElMessage.error(resp.errors); return; } - state.service = getCurrentNode(traceStore.services) || traceStore.services[0]; + if (props.data.filters && props.data.filters.id === "0") { + state.service = { value: "", label: "" }; + return; + } else { + state.service = getCurrentNode(traceStore.services) || traceStore.services[0]; + } + emits("get", state.service.id); getEndpoints(state.service.id); @@ -198,7 +204,7 @@ limitations under the License. --> if (props.data.filters && props.data.filters.id) { param = { ...param, - serviceId: props.data.filters.id || undefined, + serviceId: props.data.filters.id && props.data.filters.id !== "0" ? props.data.filters.id : undefined, endpointId: state.endpoint.id || undefined, serviceInstanceId: state.instance.id || undefined, }; diff --git a/src/views/dashboard/related/trace/TraceList.vue b/src/views/dashboard/related/trace/TraceList.vue index 6b2ca76a..ba2991a8 100644 --- a/src/views/dashboard/related/trace/TraceList.vue +++ b/src/views/dashboard/related/trace/TraceList.vue @@ -227,7 +227,8 @@ limitations under the License. --> .no-data { padding-top: 50px; - width: 100%; + width: 280px; text-align: center; + height: 100px; } diff --git a/src/views/dashboard/related/trace/components/D3Graph/Index.vue b/src/views/dashboard/related/trace/components/D3Graph/Index.vue index c96c04b7..ff4705e7 100644 --- a/src/views/dashboard/related/trace/components/D3Graph/Index.vue +++ b/src/views/dashboard/related/trace/components/D3Graph/Index.vue @@ -139,28 +139,61 @@ limitations under the License. --> } segmentHeaders.forEach((span: Span) => { if (span.refs.length) { + let exit = 0; span.refs.forEach((ref) => { - const index = props.data.findIndex( + const i = props.data.findIndex( (i: Recordable) => ref.parentSegmentId === i.segmentId && ref.parentSpanId === i.spanId, ); - if (index === -1) { - // create a known broken node. - const i = ref.parentSpanId; - const fixSpanKeyContent = { + if (i > -1) { + exit = 1; + } + }); + + if (!exit) { + const ref = span.refs[0]; + // create a known broken node. + const i = ref.parentSpanId; + const fixSpanKeyContent = { + traceId: ref.traceId, + segmentId: ref.parentSegmentId, + spanId: i, + parentSpanId: i > -1 ? 0 : -1, + }; + if (!_.find(fixSpans, fixSpanKeyContent)) { + fixSpans.push({ + ...fixSpanKeyContent, + refs: [], + endpointName: `VNode: ${ref.parentSegmentId}`, + serviceCode: "VirtualNode", + type: `[Broken] ${ref.type}`, + peer: "", + component: `VirtualNode: #${i}`, + isError: true, + isBroken: true, + layer: "Broken", + tags: [], + logs: [], + startTime: 0, + endTime: 0, + }); + } + // if root broken node is not exist, create a root broken node. + if (fixSpanKeyContent.parentSpanId > -1) { + const fixRootSpanKeyContent = { traceId: ref.traceId, segmentId: ref.parentSegmentId, - spanId: i, - parentSpanId: i > -1 ? 0 : -1, + spanId: 0, + parentSpanId: -1, }; - if (!_.find(fixSpans, fixSpanKeyContent)) { + if (!_.find(fixSpans, fixRootSpanKeyContent)) { fixSpans.push({ - ...fixSpanKeyContent, + ...fixRootSpanKeyContent, refs: [], endpointName: `VNode: ${ref.parentSegmentId}`, serviceCode: "VirtualNode", type: `[Broken] ${ref.type}`, peer: "", - component: `VirtualNode: #${i}`, + component: `VirtualNode: #0`, isError: true, isBroken: true, layer: "Broken", @@ -170,44 +203,16 @@ limitations under the License. --> endTime: 0, }); } - // if root broken node is not exist, create a root broken node. - if (fixSpanKeyContent.parentSpanId > -1) { - const fixRootSpanKeyContent = { - traceId: ref.traceId, - segmentId: ref.parentSegmentId, - spanId: 0, - parentSpanId: -1, - }; - if (!_.find(fixSpans, fixRootSpanKeyContent)) { - fixSpans.push({ - ...fixRootSpanKeyContent, - refs: [], - endpointName: `VNode: ${ref.parentSegmentId}`, - serviceCode: "VirtualNode", - type: `[Broken] ${ref.type}`, - peer: "", - component: `VirtualNode: #0`, - isError: true, - isBroken: true, - layer: "Broken", - tags: [], - logs: [], - startTime: 0, - endTime: 0, - }); - } - } } - }); + } } }); [...fixSpans, ...props.data].forEach((i) => { i.label = i.endpointName || "no operation name"; i.children = []; - if (segmentGroup[i.segmentId] === undefined) { + if (!segmentGroup[i.segmentId]) { segmentIdGroup.push(i.segmentId); - segmentGroup[i.segmentId] = []; - segmentGroup[i.segmentId].push(i); + segmentGroup[i.segmentId] = [i]; } else { segmentGroup[i.segmentId].push(i); } diff --git a/src/views/dashboard/related/trace/components/D3Graph/SpanDetail.vue b/src/views/dashboard/related/trace/components/D3Graph/SpanDetail.vue index a9d54711..eebdd416 100644 --- a/src/views/dashboard/related/trace/components/D3Graph/SpanDetail.vue +++ b/src/views/dashboard/related/trace/components/D3Graph/SpanDetail.vue @@ -42,6 +42,12 @@ limitations under the License. --> {{ t("isError") }}: {{ currentSpan.isError }} +
{{ t("traceID") }}.
+
+ + {{ item.traceId }} + +
{{ t("tags") }}.
{{ i.key }}: @@ -127,7 +133,7 @@ limitations under the License. -->