+
{{ t("exportImage") }}
-
diff --git a/src/views/dashboard/related/trace/components/index.ts b/src/views/dashboard/related/trace/components/index.ts
new file mode 100644
index 00000000..eafd9c4a
--- /dev/null
+++ b/src/views/dashboard/related/trace/components/index.ts
@@ -0,0 +1,24 @@
+/**
+ * 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 List from "./List.vue";
+import Tree from "./Tree.vue";
+
+export default {
+ List,
+ Tree,
+};
diff --git a/src/views/dashboard/related/trace/components/utils/d3-trace.ts b/src/views/dashboard/related/trace/utils/d3-trace-list.ts
similarity index 95%
rename from src/views/dashboard/related/trace/components/utils/d3-trace.ts
rename to src/views/dashboard/related/trace/utils/d3-trace-list.ts
index bdd224e3..c46e736d 100644
--- a/src/views/dashboard/related/trace/components/utils/d3-trace.ts
+++ b/src/views/dashboard/related/trace/utils/d3-trace-list.ts
@@ -17,17 +17,8 @@
import * as d3 from "d3";
import d3tip from "d3-tip";
-import { Span, Trace } from "@/types/trace";
-import { func } from "vue-types";
+import { Trace } from "@/types/trace";
-const type = {
- MQ: "#bf99f8",
- Http: "#72a5fd",
- Database: "#ff6732",
- Unknown: "#ffc107",
- Cache: "#00bcd4",
- RPCFramework: "#ee4395",
-};
export default class ListGraph {
private barHeight = 48;
private handleSelectSpan: Nullable<(i: Trace) => void> = null;
@@ -36,7 +27,6 @@ export default class ListGraph {
private width = 0;
private height = 0;
private svg: any = null;
- private treemap: any = null;
private tip: any = null;
private row: any[] = [];
private data: any = [];
@@ -58,7 +48,6 @@ export default class ListGraph {
.attr("class", "trace-list-dowanload")
.attr("width", this.width)
.attr("height", this.height);
- this.treemap = d3.tree().size([this.height * 0.7, this.width]);
this.tip = (d3tip as any)()
.attr("class", "d3-tip")
.offset([-8, 0])
@@ -250,7 +239,7 @@ export default class ListGraph {
)
.on("click", (d: any) => {
this.click(d, this);
- (d3 as any).event.stopPropagation();
+ // (d3 as any).event.stopPropagation();
});
node
.transition()
diff --git a/src/views/dashboard/related/trace/utils/d3-trace-tree.ts b/src/views/dashboard/related/trace/utils/d3-trace-tree.ts
new file mode 100644
index 00000000..28d7af37
--- /dev/null
+++ b/src/views/dashboard/related/trace/utils/d3-trace-tree.ts
@@ -0,0 +1,410 @@
+/**
+ * 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 * as d3 from "d3";
+import d3tip from "d3-tip";
+import { Trace, Span } from "@/types/trace";
+
+export default class TraceMap {
+ private i = 0;
+ private el: Nullable
= null;
+ private handleSelectSpan: Nullable<(i: Trace) => void> = null;
+ private topSlow: any = [];
+ private height = 0;
+ private width = 0;
+ private topChild: any[] = [];
+ private body: any = null;
+ private tip: any = null;
+ private svg: any = null;
+ private treemap: any = null;
+ private data: any = null;
+ private row: any = null;
+ private min = 0;
+ private max = 0;
+ private list: string[] = [];
+ private xScale: any = null;
+ private sequentialScale: any = null;
+ private root: any = null;
+ private topSlowMax: number[] = [];
+ private topSlowMin: number[] = [];
+ private topChildMax: number[] = [];
+ private topChildMin: number[] = [];
+ private nodeUpdate: any = null;
+
+ constructor(el: HTMLDivElement, handleSelectSpan: (i: Trace) => void) {
+ this.el = el;
+ this.handleSelectSpan = handleSelectSpan;
+ this.i = 0;
+ this.topSlow = [];
+ this.topChild = [];
+ this.width = el.clientWidth;
+ this.height = el.clientHeight - 28;
+ this.body = d3
+ .select(this.el)
+ .append("svg")
+ .attr("width", this.width)
+ .attr("height", this.height);
+ this.tip = (d3tip as any)()
+ .attr("class", "d3-tip")
+ .offset([-8, 0])
+ .html(
+ (d: any) => `
+ ${d.data.label}
+ ${
+ d.data.dur
+ ? 'SelfDuration: ' + d.data.dur + "ms
"
+ : ""
+ }
+ ${
+ d.data.endTime - d.data.startTime
+ ? 'TotalDuration: ' +
+ (d.data.endTime - d.data.startTime) +
+ "ms
"
+ : ""
+ }
+ `
+ );
+ this.svg = this.body
+ .append("g")
+ .attr("transform", () => `translate(120, 0)`);
+ this.svg.call(this.tip);
+ }
+ resize() {
+ if (!this.el) {
+ return;
+ }
+ // reset svg size
+ this.width = this.el.clientWidth;
+ this.height = this.el.clientHeight - 28;
+ this.body.attr("width", this.width).attr("height", this.height);
+ this.body.select("g").attr("transform", () => `translate(160, 0)`);
+ // reset zoom function for translate
+ const transform = d3.zoomTransform(this.body).translate(0, 0);
+ d3.zoom().transform(this.body, transform);
+ }
+ init(data: any, row: any) {
+ this.treemap = d3.tree().size([row.length * 35, this.width]);
+ this.row = row;
+ this.data = data;
+ this.min = Number(d3.min(this.row.map((i: Span) => i.startTime)));
+ this.max = Number(d3.max(this.row.map((i: Span) => i.endTime - this.min)));
+ this.list = Array.from(new Set(this.row.map((i: Span) => i.serviceCode)));
+ this.xScale = d3.scaleLinear().range([0, 100]).domain([0, this.max]);
+ this.sequentialScale = d3
+ .scaleSequential()
+ .domain([0, this.list.length + 1])
+ .interpolator(d3.interpolateCool);
+
+ this.body.call(this.getZoomBehavior(this.svg));
+ this.root = d3.hierarchy(this.data, (d) => d.children);
+ this.root.x0 = this.height / 2;
+ this.root.y0 = 0;
+ this.topSlow = [];
+ this.topChild = [];
+ const that = this;
+ this.root.children.forEach(collapse);
+ this.topSlowMax = this.topSlow.sort((a: number, b: number) => b - a)[0];
+ this.topSlowMin = this.topSlow.sort((a: number, b: number) => b - a)[4];
+ this.topChildMax = this.topChild.sort((a: number, b: number) => b - a)[0];
+ this.topChildMin = this.topChild.sort((a: number, b: number) => b - a)[4];
+ this.update(this.root);
+ // Collapse the node and all it's children
+ function collapse(d: any) {
+ if (d.children) {
+ let dur = d.data.endTime - d.data.startTime;
+ d.children.forEach((i: any) => {
+ dur -= i.data.endTime - i.data.startTime;
+ });
+ d.dur = dur < 0 ? 0 : dur;
+ that.topSlow.push(dur);
+ that.topChild.push(d.children.length);
+ d.childrenLength = d.children.length;
+ d.children.forEach(collapse);
+ }
+ }
+ }
+ draw() {
+ this.update(this.root);
+ }
+ update(source: any) {
+ const that: any = this;
+ const treeData = this.treemap(this.root);
+ const nodes = treeData.descendants(),
+ links = treeData.descendants().slice(1);
+
+ nodes.forEach(function (d: any) {
+ d.y = d.depth * 140;
+ });
+
+ const node = this.svg.selectAll("g.node").data(nodes, (d: any) => {
+ return d.id || (d.id = ++this.i);
+ });
+
+ const nodeEnter = node
+ .enter()
+ .append("g")
+ .attr("class", "node")
+ .attr("cursor", "pointer")
+ .attr("transform", function () {
+ return "translate(" + source.y0 + "," + source.x0 + ")";
+ })
+ .on("mouseover", function (event: any, d: any) {
+ that.tip.show(d, this);
+ if (!that.timeUpdate) {
+ return;
+ }
+ const _node = that.timeUpdate._groups[0].filter(
+ (group: any) => group.__data__.id === that.i + 1
+ );
+ if (_node.length) {
+ that.timeTip.show(d, _node[0].children[1]);
+ }
+ })
+ .on("mouseout", function (event: any, d: any) {
+ that.tip.hide(d, this);
+ if (!that.timeUpdate) {
+ return;
+ }
+ const _node = that.timeUpdate._groups[0].filter(
+ (group: any) => group.__data__.id === that.i + 1
+ );
+ if (_node.length) {
+ that.timeTip.hide(d, _node[0].children[1]);
+ }
+ })
+ .on("click", function (d: any) {
+ (d3 as any).event.stopPropagation();
+ that.handleSelectSpan(d);
+ });
+
+ nodeEnter
+ .append("circle")
+ .attr("class", "node")
+ .attr("r", 1e-6)
+ .style("fill", (d: any) =>
+ d._children
+ ? this.sequentialScale(this.list.indexOf(d.data.serviceCode))
+ : "#fff"
+ )
+ .attr("stroke", (d: any) =>
+ this.sequentialScale(this.list.indexOf(d.data.serviceCode))
+ )
+ .attr("stroke-width", 2.5);
+
+ nodeEnter
+ .append("text")
+ .attr("font-size", 11)
+ .attr("dy", "-0.5em")
+ .attr("x", function (d: any) {
+ return d.children || d._children ? -15 : 15;
+ })
+ .attr("text-anchor", function (d: any) {
+ return d.children || d._children ? "end" : "start";
+ })
+ .text((d: any) =>
+ d.data.label.length > 19
+ ? (d.data.isError ? "◉ " : "") + d.data.label.slice(0, 19) + "..."
+ : (d.data.isError ? "◉ " : "") + d.data.label
+ )
+ .style("fill", (d: any) => (!d.data.isError ? "#3d444f" : "#E54C17"));
+ nodeEnter
+ .append("text")
+ .attr("class", "node-text")
+ .attr("x", function (d: any) {
+ return d.children || d._children ? -15 : 15;
+ })
+ .attr("dy", "1em")
+ .attr("fill", "#bbb")
+ .attr("text-anchor", function (d: any) {
+ return d.children || d._children ? "end" : "start";
+ })
+ .style("font-size", "10px")
+ .text(
+ (d: any) =>
+ `${d.data.layer || ""}${
+ d.data.component ? "-" + d.data.component : d.data.component || ""
+ }`
+ );
+ nodeEnter
+ .append("rect")
+ .attr("rx", 1)
+ .attr("ry", 1)
+ .attr("height", 2)
+ .attr("width", 100)
+ .attr("x", function (d: any) {
+ return d.children || d._children ? "-110" : "10";
+ })
+ .attr("y", -1)
+ .style("fill", "#00000020");
+ nodeEnter
+ .append("rect")
+ .attr("rx", 1)
+ .attr("ry", 1)
+ .attr("height", 2)
+ .attr("width", (d: any) => {
+ if (!d.data.endTime || !d.data.startTime) return 0;
+ return this.xScale(d.data.endTime - d.data.startTime) + 1 || 0;
+ })
+ .attr("x", (d: any) => {
+ if (!d.data.endTime || !d.data.startTime) {
+ return 0;
+ }
+ if (d.children || d._children) {
+ return -110 + this.xScale(d.data.startTime - this.min);
+ }
+ return 10 + this.xScale(d.data.startTime - this.min);
+ })
+ .attr("y", -1)
+ .style("fill", (d: any) =>
+ this.sequentialScale(this.list.indexOf(d.data.serviceCode))
+ );
+ const nodeUpdate = nodeEnter.merge(node);
+ this.nodeUpdate = nodeUpdate;
+ nodeUpdate
+ .transition()
+ .duration(600)
+ .attr("transform", function (d: any) {
+ return "translate(" + d.y + "," + d.x + ")";
+ });
+
+ // Update the node attributes and style
+ nodeUpdate
+ .select("circle.node")
+ .attr("r", 5)
+ .style("fill", (d: any) =>
+ d._children
+ ? this.sequentialScale(this.list.indexOf(d.data.serviceCode))
+ : "#fff"
+ )
+ .attr("cursor", "pointer")
+ .on("click", (d: any) => {
+ (d3 as any).event.stopPropagation();
+ click(d);
+ });
+
+ // Remove any exiting nodes
+ const nodeExit = node
+ .exit()
+ .transition()
+ .duration(600)
+ .attr("transform", function () {
+ return "translate(" + source.y + "," + source.x + ")";
+ })
+ .remove();
+
+ nodeExit.select("circle").attr("r", 1e-6);
+
+ nodeExit.select("text").style("fill-opacity", 1e-6);
+
+ const link = this.svg
+ .selectAll("path.tree-link")
+ .data(links, function (d: any) {
+ return d.id;
+ })
+ .style("stroke-width", 1.5);
+
+ const linkEnter = link
+ .enter()
+ .insert("path", "g")
+ .attr("class", "tree-link")
+ .attr("d", function () {
+ const o = { x: source.x0, y: source.y0 };
+ return diagonal(o, o);
+ })
+ .style("stroke-width", 1.5);
+
+ const linkUpdate = linkEnter.merge(link);
+ linkUpdate
+ .transition()
+ .duration(600)
+ .attr("d", function (d: any) {
+ return diagonal(d, d.parent);
+ });
+
+ nodes.forEach(function (d: any) {
+ d.x0 = d.x;
+ d.y0 = d.y;
+ });
+ function diagonal(s: any, d: any) {
+ return `M ${s.y} ${s.x}
+ C ${(s.y + d.y) / 2} ${s.x}, ${(s.y + d.y) / 2} ${d.x},
+ ${d.y} ${d.x}`;
+ }
+ function click(d: any) {
+ if (d.children) {
+ d._children = d.children;
+ d.children = null;
+ } else {
+ d.children = d._children;
+ d._children = null;
+ }
+ that.update(d);
+ }
+ }
+ setDefault() {
+ d3.selectAll(".time-inner").style("opacity", 1);
+ d3.selectAll(".time-inner-duration").style("opacity", 0);
+ d3.selectAll(".trace-tree-node-selfdur").style("opacity", 0);
+ d3.selectAll(".trace-tree-node-selfchild").style("opacity", 0);
+ this.nodeUpdate._groups[0].forEach((i: any) => {
+ d3.select(i).style("opacity", 1);
+ });
+ }
+ getTopChild() {
+ d3.selectAll(".time-inner").style("opacity", 1);
+ d3.selectAll(".time-inner-duration").style("opacity", 0);
+ d3.selectAll(".trace-tree-node-selfdur").style("opacity", 0);
+ d3.selectAll(".trace-tree-node-selfchild").style("opacity", 1);
+ this.nodeUpdate._groups[0].forEach((i: any) => {
+ d3.select(i).style("opacity", 0.2);
+ if (
+ i.__data__.data.children.length >= this.topChildMin &&
+ i.__data__.data.children.length <= this.topChildMax
+ ) {
+ d3.select(i).style("opacity", 1);
+ }
+ });
+ }
+ getTopSlow() {
+ d3.selectAll(".time-inner").style("opacity", 0);
+ d3.selectAll(".time-inner-duration").style("opacity", 1);
+ d3.selectAll(".trace-tree-node-selfchild").style("opacity", 0);
+ d3.selectAll(".trace-tree-node-selfdur").style("opacity", 1);
+ this.nodeUpdate._groups[0].forEach((i: any) => {
+ d3.select(i).style("opacity", 0.2);
+ if (
+ i.__data__.data.dur >= this.topSlowMin &&
+ i.__data__.data.dur <= this.topSlowMax
+ ) {
+ d3.select(i).style("opacity", 1);
+ }
+ });
+ }
+ getZoomBehavior(g: any) {
+ return d3
+ .zoom()
+ .scaleExtent([0.3, 10])
+ .on("zoom", () => {
+ g.attr(
+ "transform",
+ `translate(${(d3 as any).event.transform.x + 120},${
+ (d3 as any).event.transform.y
+ })scale(${(d3 as any).event.transform.k})`
+ );
+ });
+ }
+}