| import { CalculationType } from './../dashboard/charts/constant'; |
| /** |
| * 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 { Ref, Span, StatisticsSpan, StatisticsGroupRef, TraceTreeRef } from '@/types/trace'; |
| import lodash from 'lodash'; |
| |
| 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[], cureentTraceId: string) { |
| const segmentIdList: Span[] = []; |
| const traceTreeRef: any = this.changeTreeCore(data, cureentTraceId); |
| traceTreeRef.segmentIdGroup.forEach((segmentId: string) => { |
| if (traceTreeRef.segmentMap.get(segmentId).refs) { |
| traceTreeRef.segmentMap.get(segmentId).refs.forEach((ref: Ref) => { |
| if (ref.traceId === cureentTraceId) { |
| 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[], cureentTraceId: string): Map<string, Span[]> { |
| const result = new Map<string, Span[]>(); |
| const traceTreeRef = this.changeTreeCore(data, cureentTraceId); |
| traceTreeRef.segmentMap.forEach((span, segmentId) => { |
| 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[], cureentTraceId: string): TraceTreeRef { |
| // set a breakpoint at this line |
| if (data.length === 0) { |
| return { |
| segmentMap: new Map(), |
| segmentIdGroup: [], |
| }; |
| } |
| const segmentGroup: any = {}; |
| 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 fixSpanKeyContent = { |
| traceId: span.traceId, |
| segmentId: span.segmentId, |
| spanId: span.spanId - 1, |
| parentSpanId: span.spanId - 2, |
| }; |
| if (index === -1 && !lodash.find(fixSpans, fixSpanKeyContent)) { |
| fixSpans.push({ |
| ...fixSpanKeyContent, |
| 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: number = ref.parentSpanId; |
| const fixSpanKeyContent = { |
| traceId: ref.traceId, |
| segmentId: ref.parentSegmentId, |
| spanId: parentSpanId, |
| parentSpanId: parentSpanId > -1 ? 0 : -1, |
| }; |
| if (lodash.find(fixSpans, fixSpanKeyContent)) { |
| fixSpans.push({ |
| ...fixSpanKeyContent, |
| 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 (fixSpanKeyContent.parentSpanId > -1) { |
| const fixRootSpanKeyContent = { |
| traceId: ref.traceId, |
| segmentId: ref.parentSegmentId, |
| spanId: 0, |
| parentSpanId: -1, |
| }; |
| if (!lodash.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, ...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 = lodash.filter(data, (span: Span) => { |
| return lodash.find(span.refs, { |
| traceId: curSegment.traceId, |
| parentSegmentId: curSegment.segmentId, |
| parentSpanId: curSegment.spanId, |
| }); |
| }) as Span[]; |
| 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((chlid: Span) => { |
| dur -= chlid.endTime - chlid.startTime; |
| }); |
| span.dur = dur < 0 ? 0 : dur; |
| span.children.forEach((chlid) => this.collapse(chlid)); |
| } |
| } |
| |
| 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); |
| } |
| }); |
| } |
| } |