skywalking-booster-ui/src/views/dashboard/related/trace/utils/trace-table.ts
2025-03-06 17:10:54 +08:00

314 lines
10 KiB
TypeScript

/**
* 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 { Ref, Span, StatisticsSpan, StatisticsGroupRef, TraceTreeRef } from "@/types/trace";
export default class TraceUtil {
public static buildTraceDataList(data: Span[]): string[] {
return Array.from(new Set(data.map((span: Span) => span.serviceCode)));
}
public static changeTree(data: Span[], currentTraceId: string) {
const segmentIdList: Span[] = [];
const traceTreeRef: Recordable = this.changeTreeCore(data);
traceTreeRef.segmentIdGroup.forEach((segmentId: string) => {
if (traceTreeRef.segmentMap.get(segmentId).refs) {
traceTreeRef.segmentMap.get(segmentId).refs.forEach((ref: Ref) => {
if (ref.traceId === currentTraceId) {
this.traverseTree(
traceTreeRef.segmentMap.get(ref.parentSegmentId) as Span,
ref.parentSpanId,
ref.parentSegmentId,
traceTreeRef.segmentMap.get(segmentId) as Span,
);
}
});
}
});
// set a breakpoint at this line
traceTreeRef.segmentMap.forEach((value: Span) => {
if ((value.refs && value.refs.length === 0) || !value.refs) {
segmentIdList.push(value as Span);
}
});
segmentIdList.forEach((segmentId: Span) => {
this.collapse(segmentId);
});
return segmentIdList;
}
public static changeStatisticsTree(data: Span[]): Map<string, Span[]> {
const result = new Map<string, Span[]>();
const traceTreeRef = this.changeTreeCore(data);
traceTreeRef.segmentMap.forEach((span) => {
const groupRef = span.endpointName + ":" + span.type;
if (span.children && span.children.length > 0) {
this.calculationChildren(span.children, result);
this.collapse(span);
}
if (result.get(groupRef) === undefined) {
result.set(groupRef, []);
result.get(groupRef)!.push(span);
} else {
result.get(groupRef)!.push(span);
}
});
return result;
}
private static changeTreeCore(data: Span[]): TraceTreeRef {
// set a breakpoint at this line
if (data.length === 0) {
return {
segmentMap: new Map(),
segmentIdGroup: [],
};
}
const segmentGroup: Recordable = {};
const segmentMap: Map<string, Span> = new Map();
const segmentIdGroup: string[] = [];
const fixSpans: Span[] = [];
const segmentHeaders: Span[] = [];
data.forEach((span) => {
if (span.parentSpanId === -1) {
segmentHeaders.push(span);
} else {
const index = data.findIndex((patchSpan: Span) => {
return patchSpan.segmentId === span.segmentId && patchSpan.spanId === span.spanId - 1;
});
const content = fixSpans.find(
(i: Span) =>
i.traceId === span.traceId &&
i.segmentId === span.segmentId &&
i.spanId === span.spanId - 1 &&
i.parentSpanId === span.spanId - 2,
);
if (index === -1 && !content) {
fixSpans.push({
traceId: span.traceId,
segmentId: span.segmentId,
spanId: span.spanId - 1,
parentSpanId: span.spanId - 2,
refs: [],
endpointName: `VNode: ${span.segmentId}`,
serviceCode: "VirtualNode",
type: `[Broken] ${span.type}`,
peer: "",
component: `VirtualNode: #${span.spanId - 1}`,
isError: true,
isBroken: true,
layer: "Broken",
tags: [],
logs: [],
startTime: 0,
endTime: 0,
});
}
}
});
segmentHeaders.forEach((span) => {
if (span.refs && span.refs.length) {
span.refs.forEach((ref) => {
const index = data.findIndex((patchSpan: Span) => {
return ref.parentSegmentId === patchSpan.segmentId && ref.parentSpanId === patchSpan.spanId;
});
if (index === -1) {
// create a known broken node.
const parentSpanId = ref.parentSpanId > -1 ? 0 : -1;
const item = fixSpans.find(
(i: Span) =>
i.traceId === ref.traceId &&
i.segmentId === ref.parentSegmentId &&
i.spanId === ref.parentSpanId &&
i.parentSpanId === parentSpanId,
);
if (!item) {
fixSpans.push({
traceId: ref.traceId,
segmentId: ref.parentSegmentId,
spanId: ref.parentSpanId,
parentSpanId: parentSpanId,
refs: [],
endpointName: `VNode: ${ref.parentSegmentId}`,
serviceCode: "VirtualNode",
type: `[Broken] ${ref.type}`,
peer: "",
component: `VirtualNode: #${parentSpanId}`,
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 (parentSpanId > -1) {
const item = fixSpans.find(
(i: Span) =>
i.traceId === ref.traceId &&
i.segmentId === ref.parentSegmentId &&
i.spanId === 0 &&
i.parentSpanId === -1,
);
if (!item) {
fixSpans.push({
traceId: ref.traceId,
segmentId: ref.parentSegmentId,
spanId: 0,
parentSpanId: -1,
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, ...data].forEach((fixSpan: Span) => {
fixSpan.label = fixSpan.endpointName || "no operation name";
fixSpan.children = [];
const id = fixSpan.segmentId || "top";
if (segmentGroup[id] === undefined) {
segmentIdGroup.push(id);
segmentGroup[id] = [];
segmentGroup[id].push(fixSpan);
} else {
segmentGroup[id].push(fixSpan);
}
});
segmentIdGroup.forEach((segmentId: string) => {
const currentSegmentSet = segmentGroup[segmentId].sort((a: Span, b: Span) => b.parentSpanId - a.parentSpanId);
currentSegmentSet.forEach((curSegment: Span) => {
const index = currentSegmentSet.findIndex(
(curSegment2: Span) => curSegment2.spanId === curSegment.parentSpanId,
);
if (index !== -1) {
if (
(currentSegmentSet[index].isBroken && currentSegmentSet[index].parentSpanId === -1) ||
!currentSegmentSet[index].isBroken
) {
currentSegmentSet[index].children.push(curSegment);
currentSegmentSet[index].children.sort((a: Span, b: Span) => a.spanId - b.spanId);
}
}
if (curSegment.isBroken) {
const children = data.filter((span: Span) =>
span.refs.find(
(d) =>
d.traceId === curSegment.traceId &&
d.parentSegmentId === curSegment.segmentId &&
d.parentSpanId === curSegment.spanId,
),
);
if (children.length) {
curSegment.children = curSegment.children || [];
curSegment.children.push(...children);
}
}
});
segmentMap.set(segmentId, currentSegmentSet[currentSegmentSet.length - 1]);
});
return {
segmentMap,
segmentIdGroup,
};
}
private static collapse(span: Span) {
if (span.children) {
let dur = span.endTime - span.startTime;
span.children.forEach((child: Span) => {
dur -= child.endTime - child.startTime;
});
span.dur = dur < 0 ? 0 : dur;
span.children.forEach((child) => this.collapse(child));
}
}
private static traverseTree(node: Span, spanId: number, segmentId: string, childNode: Span) {
if (!node || node.isBroken) {
return;
}
if (node.spanId === spanId && node.segmentId === segmentId) {
node.children!.push(childNode);
return;
}
if (node.children && node.children.length > 0) {
for (const grandchild of node.children) {
this.traverseTree(grandchild, spanId, segmentId, childNode);
}
}
}
private static getSpanGroupData(groupspans: Span[], groupRef: StatisticsGroupRef): StatisticsSpan {
let maxTime = 0;
let minTime = 0;
let sumTime = 0;
const count = groupspans.length;
groupspans.forEach((groupspan: Span) => {
const duration = groupspan.dur || 0;
if (duration > maxTime) {
maxTime = duration;
}
if (duration < minTime) {
minTime = duration;
}
sumTime = sumTime + duration;
});
const avgTime = count === 0 ? 0 : sumTime / count;
return {
groupRef,
maxTime,
minTime,
sumTime,
avgTime,
count,
};
}
private static calculationChildren(nodes: Span[], result: Map<string, Span[]>): void {
nodes.forEach((node: Span) => {
const groupRef = node.endpointName + ":" + node.type;
if (node.children && node.children.length > 0) {
this.calculationChildren(node.children, result);
}
if (result.get(groupRef) === undefined) {
result.set(groupRef, []);
result.get(groupRef)!.push(node);
} else {
result.get(groupRef)!.push(node);
}
});
}
}