refactor: optimize the attached events visualization in the trace widget (#234)

This commit is contained in:
Fine0830 2023-02-15 13:32:32 +08:00 committed by GitHub
parent efed817f73
commit a1066f09e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 51 deletions

View File

@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div class="side-bar" v-if="showMenu" @click="isCollapse = false" @mouseleave="isCollapse = true"> <div class="side-bar" v-if="showMenu" @click="isCollapse = false" @mouseleave="closeMenu">
<div :class="isCollapse ? 'logo-icon-collapse' : 'logo-icon'"> <div :class="isCollapse ? 'logo-icon-collapse' : 'logo-icon'">
<Icon :size="isCollapse ? 'xl' : 'logo'" :iconName="isCollapse ? 'logo' : 'logo-sw'" /> <Icon :size="isCollapse ? 'xl' : 'logo'" :iconName="isCollapse ? 'logo' : 'logo-sw'" />
</div> </div>
@ -81,6 +81,7 @@ limitations under the License. -->
const route = useRoute(); const route = useRoute();
const isCollapse = ref(true); const isCollapse = ref(true);
const showMenu = ref(true); const showMenu = ref(true);
const open = ref<boolean>(false);
if (/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) { if (/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
appStore.setIsMobile(true); appStore.setIsMobile(true);
@ -97,7 +98,17 @@ limitations under the License. -->
return menus.filter((d) => d.meta && !d.meta.notShow); return menus.filter((d) => d.meta && !d.meta.notShow);
}; };
function setCollapse() { function setCollapse() {
isCollapse.value = false; open.value = true;
setTimeout(() => {
if (open.value) {
isCollapse.value = false;
}
open.value = false;
}, 1000);
}
function closeMenu() {
isCollapse.value = true;
open.value = false;
} }
</script> </script>

View File

@ -74,7 +74,7 @@ limitations under the License. -->
<h5 class="mb-10" v-if="currentSpan.attachedEvents && currentSpan.attachedEvents.length"> {{ t("events") }}. </h5> <h5 class="mb-10" v-if="currentSpan.attachedEvents && currentSpan.attachedEvents.length"> {{ t("events") }}. </h5>
<div <div
class="attach-events" class="attach-events"
ref="timeline" ref="eventGraph"
v-if="currentSpan.attachedEvents && currentSpan.attachedEvents.length" v-if="currentSpan.attachedEvents && currentSpan.attachedEvents.length"
></div> ></div>
<el-button class="popup-btn" type="primary" @click="getTaceLogs"> <el-button class="popup-btn" type="primary" @click="getTaceLogs">
@ -131,8 +131,7 @@ limitations under the License. -->
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import type { PropType } from "vue"; import type { PropType } from "vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { DataSet, Timeline } from "vis-timeline/standalone"; import ListGraph from "../../utils/d3-trace-list";
import "vis-timeline/styles/vis-timeline-graph2d.css";
import copy from "@/utils/copy"; import copy from "@/utils/copy";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { dateFormat } from "@/utils/dateFormat"; import { dateFormat } from "@/utils/dateFormat";
@ -146,8 +145,6 @@ limitations under the License. -->
}); });
const { t } = useI18n(); const { t } = useI18n();
const traceStore = useTraceStore(); const traceStore = useTraceStore();
const timeline = ref<Nullable<HTMLDivElement>>(null);
const visGraph = ref<Nullable<any>>(null);
const pageNum = ref<number>(1); const pageNum = ref<number>(1);
const showRelatedLogs = ref<boolean>(false); const showRelatedLogs = ref<boolean>(false);
const showEventDetail = ref<boolean>(false); const showEventDetail = ref<boolean>(false);
@ -156,6 +153,8 @@ limitations under the License. -->
const total = computed(() => const total = computed(() =>
traceStore.traceList.length === pageSize ? pageSize * pageNum.value + 1 : pageSize * pageNum.value, traceStore.traceList.length === pageSize ? pageSize * pageNum.value + 1 : pageSize * pageNum.value,
); );
const tree = ref<any>(null);
const eventGraph = ref<Nullable<HTMLDivElement>>(null);
const visDate = (date: number, pattern = "YYYY-MM-DD HH:mm:ss:SSS") => dayjs(date).format(pattern); const visDate = (date: number, pattern = "YYYY-MM-DD HH:mm:ss:SSS") => dayjs(date).format(pattern);
onMounted(() => { onMounted(() => {
@ -180,52 +179,46 @@ limitations under the License. -->
} }
} }
function visTimeline() { function visTimeline() {
if (!timeline.value) { if (!eventGraph.value) {
return; return;
} }
if (visGraph.value) {
visGraph.value.destroy();
}
const h = timeline.value.getBoundingClientRect().height;
const attachedEvents = props.currentSpan.attachedEvents || []; const attachedEvents = props.currentSpan.attachedEvents || [];
const events: any[] = attachedEvents.map((d: SpanAttachedEvent, index: number) => { const events: any[] = attachedEvents
let startTimeNanos = String(d.startTime.nanos).slice(-6).padStart(6, "0"); .map((d: SpanAttachedEvent) => {
let endTimeNanos = String(d.endTime.nanos).slice(-6).padStart(6, "0"); let startTimeNanos = String(d.startTime.nanos).slice(-6).padStart(6, "0");
endTimeNanos = toString(endTimeNanos); let endTimeNanos = String(d.endTime.nanos).slice(-6).padStart(6, "0");
startTimeNanos = toString(startTimeNanos); endTimeNanos = toString(endTimeNanos);
return { startTimeNanos = toString(startTimeNanos);
id: index + 1, const startTime = d.startTime.seconds * 1000 + d.startTime.nanos / 1000000;
content: d.event, const endTime = d.endTime.seconds * 1000 + d.endTime.nanos / 1000000;
start: new Date(Number(d.startTime.seconds * 1000 + d.startTime.nanos / 1000000)), return {
end: new Date(Number(d.endTime.seconds * 1000 + d.endTime.nanos / 1000000)), label: d.event,
...d, ...d,
startTime: d.startTime.seconds * 1000 + d.startTime.nanos / 1000000, startTime,
endTime: d.endTime.seconds * 1000 + d.endTime.nanos / 1000000, endTime,
className: "Normal", startTimeNanos,
startTimeNanos, endTimeNanos,
endTimeNanos, };
}; })
}); .sort((a: { startTime: number; endTime: number }, b: { startTime: number; endTime: number }) => {
return a.startTime - b.startTime;
});
const items = new DataSet(events); tree.value = new ListGraph(eventGraph.value, selectEvent);
const options: any = { tree.value.init(
height: h, {
width: "100%", children: events,
locale: "en", label: "",
groupHeightMode: "fitItems", },
zoomMin: 80, events,
}; 0,
);
tree.value.draw();
}
visGraph.value = new Timeline(timeline.value, items, options); function selectEvent(i: any) {
visGraph.value.on("select", (data: { items: number[] }) => { currentEvent.value = i.data;
const index = data.items[0]; showEventDetail.value = true;
currentEvent.value = events[index - 1 || 0] || {};
if (data.items.length) {
showEventDetail.value = true;
return;
}
showEventDetail.value = false;
});
} }
function toString(str: string) { function toString(str: string) {
return str.replace(/\d(?=(\d{3})+$)/g, "$&,"); return str.replace(/\d(?=(\d{3})+$)/g, "$&,");
@ -247,7 +240,8 @@ limitations under the License. -->
.attach-events { .attach-events {
width: 100%; width: 100%;
margin: 0 5px 5px 0; margin: 0 5px 5px 0;
height: 200px; height: 400px;
overflow: auto;
} }
.popup-btn { .popup-btn {

View File

@ -18,6 +18,7 @@
import * as d3 from "d3"; import * as d3 from "d3";
import d3tip from "d3-tip"; import d3tip from "d3-tip";
import type { Trace } from "@/types/trace"; import type { Trace } from "@/types/trace";
import dayjs from "dayjs";
export default class ListGraph { export default class ListGraph {
private barHeight = 48; private barHeight = 48;
@ -91,7 +92,6 @@ export default class ListGraph {
this.svg this.svg
.append("g") .append("g")
.attr("class", "trace-xaxis") .attr("class", "trace-xaxis")
.attr("transform", `translate(${this.width * 0.618 - 20},${30})`) .attr("transform", `translate(${this.width * 0.618 - 20},${30})`)
.call(this.xAxis); .call(this.xAxis);
this.sequentialScale = d3 this.sequentialScale = d3
@ -164,6 +164,7 @@ export default class ListGraph {
.attr("x", 35) .attr("x", 35)
.attr("y", -6) .attr("y", -6)
.attr("fill", "#333") .attr("fill", "#333")
.style("font-size", "12px")
.html((d: any) => { .html((d: any) => {
if (d.data.label === "TRACE_ROOT") { if (d.data.label === "TRACE_ROOT") {
return ""; return "";
@ -214,7 +215,16 @@ export default class ListGraph {
.attr("y", 12) .attr("y", 12)
.attr("fill", "#ccc") .attr("fill", "#ccc")
.style("font-size", "11px") .style("font-size", "11px")
.text((d: any) => `${d.data.layer || ""} ${d.data.component ? "- " + d.data.component : d.data.component || ""}`); .text(
(d: any) =>
`${d.data.layer || ""} ${
d.data.component
? "- " + d.data.component
: d.data.event
? this.visDate(d.data.startTime) + ":" + d.data.startTimeNanos
: ""
}`,
);
nodeEnter nodeEnter
.append("rect") .append("rect")
.attr("rx", 2) .attr("rx", 2)
@ -305,6 +315,9 @@ export default class ListGraph {
callback(); callback();
} }
} }
visDate(date: number, pattern = "YYYY-MM-DD HH:mm:ss:SSS") {
return dayjs(date).format(pattern);
}
resize() { resize() {
if (!this.el) { if (!this.el) {
return; return;