| /** |
| * 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 React, { Component } from 'react'; |
| import { Button } from 'antd'; |
| import './style.less'; |
| import Tree from './d3-trace'; |
| |
| const ButtonGroup = Button.Group; |
| |
| export default class Trace extends Component { |
| constructor(props) { |
| super(props); |
| this.cache = 0; |
| this.db = 0; |
| this.http = 0; |
| this.mq = 0; |
| this.rpc = 0; |
| this.state = { |
| cache: 0, |
| db: 0, |
| http: 0, |
| mq: 0, |
| rpc: 0, |
| }; |
| } |
| |
| componentDidMount() { |
| this.changeTree(); |
| window.addEventListener('resize', this.resize); |
| } |
| |
| destroyed() { |
| window.removeEventListener('resize', this.resize); |
| } |
| |
| traverseTree(node, spanId, segmentId, data) { |
| if (!node) return; |
| if(node.spanId === spanId && node.segmentId === segmentId) {node.children.push(data);return;} |
| if (node.children && node.children.length > 0) { |
| for (let i = 0; i < node.children.length; i+=1) { |
| this.traverseTree(node.children[i],spanId,segmentId,data); |
| } |
| } |
| } |
| |
| changeTree() { |
| const propsData = this.props; |
| this.segmentId = []; |
| const segmentGroup = {} |
| const segmentIdGroup = [] |
| const [...treeData] = propsData.data; |
| const [...rowData] = propsData.data; |
| this.traceId = propsData.data[0].traceId; |
| treeData.forEach(i => { |
| /* eslint-disable */ |
| if(i.endpointName) { |
| i.label = i.endpointName; |
| i.content = i.endpointName; |
| } else { |
| i.label = 'no operation name'; |
| } |
| i.duration = i.endTime - i.startTime; |
| i.spanSegId = `${i.segmentId},${i.spanId}` |
| i.parentSpanSegId = i.parentSpanId === -1 ? null : `${i.segmentId},${i.spanId}` |
| i.children = []; |
| if(segmentGroup[i.segmentId] === undefined){ |
| segmentIdGroup.push(i.segmentId); |
| segmentGroup[i.segmentId] = []; |
| segmentGroup[i.segmentId].push(i); |
| }else{ |
| segmentGroup[i.segmentId].push(i); |
| } |
| }); |
| segmentIdGroup.forEach(id => { |
| const currentSegment = segmentGroup[id].sort((a,b) => b.parentSpanId-a.parentSpanId); |
| currentSegment.forEach(s =>{ |
| const index = currentSegment.findIndex(i => i.spanId === s.parentSpanId); |
| if(index !== -1){ |
| currentSegment[index].children.push(s); |
| currentSegment[index].children.sort((a, b) => a.spanId - b.spanId ); |
| } |
| }) |
| segmentGroup[id] = currentSegment[currentSegment.length-1] |
| }) |
| segmentIdGroup.forEach(id => { |
| segmentGroup[id].refs.forEach(ref => { |
| if(ref.traceId === this.traceId) { |
| this.traverseTree(segmentGroup[ref.parentSegmentId],ref.parentSpanId,ref.parentSegmentId,segmentGroup[id]) |
| }; |
| }) |
| }) |
| for (const i in segmentGroup) { |
| if(segmentGroup[i].refs.length ===0 ) |
| this.segmentId.push(segmentGroup[i]); |
| } |
| this.topSlow = []; |
| this.topChild = []; |
| this.segmentId.forEach((_, i) => { |
| this.collapse(this.segmentId[i]); |
| }) |
| this.topSlowMax = this.topSlow.sort((a,b) => b - a)[0]; |
| this.topSlowMin = this.topSlow.sort((a,b) => b - a)[4]; |
| |
| this.topChildMax = this.topChild.sort((a,b) => b - a)[0]; |
| this.topChildMin = this.topChild.sort((a,b) => b - a)[4]; |
| this.tree = new Tree(this.echartsElement, propsData.showSpanModal, this.topSlowMax,this.topSlowMin,this.topChildMax,this.topChildMin) |
| this.tree.init({label:`${this.traceId}`, children: this.segmentId}, rowData); |
| this.tree.draw(); |
| this.resize = this.tree.resize.bind(this.tree); |
| } |
| collapse(d) { |
| if(d.children){ |
| let dur = d.endTime - d.startTime; |
| d.children.forEach(i => { |
| dur -= (i.endTime - i.startTime); |
| }) |
| if(d.layer === "Http"){ |
| this.http += dur |
| this.setState({http: this.http}); |
| } |
| if(d.layer === "RPCFramework"){ |
| this.rpc += dur |
| this.setState({rpc: this.rpc}); |
| } |
| if(d.layer === "Database"){ |
| this.db += dur |
| this.setState({db: this.db}); |
| } |
| if(d.layer === "Cache"){ |
| this.cache += dur |
| this.setState({cache: this.cache}); |
| } |
| if(d.layer === "MQ"){ |
| this.mq += dur |
| this.setState({mq: this.mq}); |
| } |
| d.dur = dur < 0 ? 0 : dur; |
| this.topSlow.push(dur); |
| this.topChild.push(d.children.length); |
| d.childrenLength = d.children.length |
| d.children.forEach((i) => this.collapse(i)); |
| } |
| } |
| |
| render() { |
| const newStyle = { |
| height: 800, |
| // ...style, |
| }; |
| return ( |
| <div> |
| <ButtonGroup> |
| <Button onClick={() => {this.tree.setDefault();}}>Default</Button> |
| <Button onClick={() => {this.tree.topSlow();}}>Top 5 of slow span</Button> |
| <Button onClick={() => {this.tree.topChild();}}>Top 5 of children span number</Button> |
| </ButtonGroup> |
| <div style={{marginTop:10,marginBottom: 10}}> |
| {this.state.cache ? (<span class="ant-tag">Cache: {this.state.cache} ms</span>): null} |
| {this.state.db ? (<span class="ant-tag">DB: {this.state.db} ms</span>): null} |
| {this.state.mq ? (<span class="ant-tag">MQ: {this.state.mq} ms</span>): null} |
| {this.state.http ? (<span class="ant-tag">Http: {this.state.http} ms</span>): null} |
| {this.state.rpc ? (<span class="ant-tag">RPCFramework: {this.state.rpc} ms</span>): null} |
| </div> |
| <div |
| ref={(e) => { this.echartsElement = e; }} |
| style={newStyle} |
| className="trace-tree" |
| /> |
| </div> |
| ) |
| } |
| } |