- {{
- t(dashboardStore.selectedGrid.metricMode === MetricModes.General ? "metrics" : "expressions")
- }}
+
+ {{ t("expressions") }}
+
+
+ {{ t(dashboardStore.selectedGrid.metricMode === MetricModes.General ? "metrics" : "expressions") }}
+
@@ -40,7 +43,10 @@ limitations under the License. -->
@change="changeConfigs({ label: currentConfig.label })"
/>
-
+
{{ t("aggregation") }}
diff --git a/src/views/dashboard/related/topology/components/PodTopology.vue b/src/views/dashboard/related/topology/pod/PodMap.vue
similarity index 99%
rename from src/views/dashboard/related/topology/components/PodTopology.vue
rename to src/views/dashboard/related/topology/pod/PodMap.vue
index 3d445895..30e61037 100644
--- a/src/views/dashboard/related/topology/components/PodTopology.vue
+++ b/src/views/dashboard/related/topology/pod/PodMap.vue
@@ -69,10 +69,10 @@ limitations under the License. -->
import { useDashboardStore } from "@/store/modules/dashboard";
import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
- import { EntityType, DepthList, MetricModes, CallTypes } from "../../../data";
+ import { EntityType, DepthList, MetricModes, CallTypes } from "@/views/dashboard/data";
import { ElMessage } from "element-plus";
import Sankey from "./Sankey.vue";
- import Settings from "./Settings.vue";
+ import Settings from "../config/Settings.vue";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
diff --git a/src/views/dashboard/related/topology/components/Sankey.vue b/src/views/dashboard/related/topology/pod/Sankey.vue
similarity index 99%
rename from src/views/dashboard/related/topology/components/Sankey.vue
rename to src/views/dashboard/related/topology/pod/Sankey.vue
index 553bf0b9..231b366f 100644
--- a/src/views/dashboard/related/topology/components/Sankey.vue
+++ b/src/views/dashboard/related/topology/pod/Sankey.vue
@@ -23,7 +23,7 @@ limitations under the License. -->
import type { Node, Call } from "@/types/topology";
import type { MetricConfigOpt } from "@/types/dashboard";
import { aggregation } from "@/hooks/useMetricsProcessor";
- import { MetricModes } from "../../../data";
+ import { MetricModes } from "@/views/dashboard/data";
import { useAppStoreWithOut } from "@/store/modules/app";
import { Themes } from "@/constants/data";
diff --git a/src/views/dashboard/related/topology/service/HierarchyMap.vue b/src/views/dashboard/related/topology/service/HierarchyMap.vue
new file mode 100644
index 00000000..4f0265a7
--- /dev/null
+++ b/src/views/dashboard/related/topology/service/HierarchyMap.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
diff --git a/src/views/dashboard/related/topology/service/ServiceMap.vue b/src/views/dashboard/related/topology/service/ServiceMap.vue
new file mode 100644
index 00000000..111f2380
--- /dev/null
+++ b/src/views/dashboard/related/topology/service/ServiceMap.vue
@@ -0,0 +1,793 @@
+
+
+
+
+
+
+
+
![]()
+
+ {{ settings.description ? settings.description.healthy || "" : "" }}
+
+
+
+
![]()
+
+ {{ settings.description ? settings.description.unhealthy || "" : "" }}
+
+
+
+
+
+
+
+
+ {{ t("currentDepth") }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dashboard/related/topology/style.scss b/src/views/dashboard/related/topology/style.scss
new file mode 100644
index 00000000..3f3b07ec
--- /dev/null
+++ b/src/views/dashboard/related/topology/style.scss
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+.hierarchy-services-topo {
+ position: absolute;
+ width: 100%;
+}
+
+.el-loading-spinner {
+ top: 30%;
+}
+
+#popover {
+ position: absolute;
+ visibility: hidden;
+ padding: 5px;
+ border: var(--sw-topology-border);
+ border-radius: 3px;
+ background-color: var(--theme-background);
+ z-index: 9999;
+}
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/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 2ec6016d..ba2991a8 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 {
@@ -226,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 f848f8c2..ff4705e7 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;
@@ -132,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",
@@ -163,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);
}
@@ -273,7 +285,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/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. -->