Feature: update spans display (#215)
authorAllen Wang <Allen.Wang.123@outlook.com>
Thu, 27 Dec 2018 09:50:27 +0000 (17:50 +0800)
committer吴晟 Wu Sheng <wu.sheng@foxmail.com>
Thu, 27 Dec 2018 09:50:27 +0000 (17:50 +0800)
* Feature: update spans display

1.Add top childrens and top slow span.
2.fix windowresize bug.
3.fix time and span tiptip connection.
4.Optimized tree rendering algorithm.

* Feature: update span durations & types durations.

.webpackrc.js
src/components/TraceTree/d3-trace.js
src/components/TraceTree/index.js
src/components/TraceTree/style.less

index aba80ef..c3bf5d8 100755 (executable)
@@ -42,7 +42,7 @@ const config = {
 
 if (process.env.NO_MOCK) {
   config.proxy['/api'] =  {
-    target: 'http://localhost:12800',
+    target: 'http://106.75.237.45:12800',
     changeOrigin: true,
     pathRewrite: path => {
       console.log(path);
index 1645581..9c795e7 100644 (file)
@@ -19,7 +19,7 @@
 import * as d3 from 'd3';
 import d3tip from 'd3-tip';
 export default class TraceMap {
-  constructor(el) {
+  constructor(el, showSpanModal,smax,smin,cmax,cmin) {
     this.type = {
       MQ: '#bf99f8',
       Http: '#72a5fd',
@@ -28,26 +28,34 @@ export default class TraceMap {
       Cache: '#00bcd4',
       RPCFramework: '#ee4395',
     };
+    this.smax = smax;
+    this.smin = smin;
+    this.cmax = cmax;
+    this.cmin = cmin;
+    this.showSpanModal = showSpanModal;
     this.el = el;
+    this.i = 0;
+    this.j = 0;
     this.width = el.clientWidth;
     this.height = el.clientHeight;
+    this.body = d3
+      .select(this.el)
+      .append('svg')
+      .attr('width', this.width)
+      .attr('height', this.height);
+    this.tip = d3tip()
+      .attr('class', 'd3-tip')
+      .offset([10, 0])
+      .html(d => d.data.label);
+    this.timeTip = d3tip()
+      .attr('class', 'd3-tip')
+      .offset([-8, 0])
+      .html(d => d.data.label);
+    this.body.call(this.timeTip);
+    this.body.call(this.tip);
     this.treemap = d3.tree().size([this.height * 0.7, this.width]);
-    this.svg = '';
-    this.timeGroup = '';
-    this.root = '';
-    this.i = 0;
-    this.j = 0;
   }
-  resize() {
-    d3.select(this.el)
-      .select('svg')
-      .remove();
-    this.width = this.el.clientWidth;
-    this.height = this.el.clientHeight;
-    this.draw(this.data, this.row);
-  }
-  draw(data, row, showSpanModal) {
-    this.showSpanModal = showSpanModal;
+  init(data, row) {
     this.row = row;
     this.data = data;
     this.min = d3.min(this.row.map(i => i.startTime));
@@ -57,173 +65,130 @@ export default class TraceMap {
       .scaleSequential()
       .domain([0, this.list.length])
       .interpolator(d3.interpolateCool);
+    this.svg = this.body.append('g').attr('transform', d => `translate(0, ${this.row.length * 10})`).append('g');
+    this.timeGroup = this.body.append('g').attr('class','timeGroup').attr('transform', d => 'translate(5,30)');
+    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;
+  }
+  resize() {
+    this.body
+    .select('.xAxis')
+    .remove();
+    this.body
+      .select('.timeGroup')
+      .remove();
+    this.width = this.el.clientWidth;
+    this.body.attr('width', this.width);
+    this.xScale = d3
+    .scaleLinear()
+    .domain([0, this.max])
+    .range([0, this.width - 10]);
+    this.xAxis = d3.axisTop(this.xScale).tickFormat(d => {
+      if (d === 0) return 0;
+      if (d >= 1000) return d / 1000 + 's';
+      return d + ' ms';
+    });
+    this.body
+    .append('g')
+    .attr('class', 'xAxis')
+    .attr('transform', `translate(5,20)`)
+    .call(this.xAxis);
+    this.timeGroup = this.body.append('g').attr('class','timeGroup').attr('transform', d => 'translate(5,30)');
+    this.updatexAxis(this.root);
+  }
+  draw() {
     this.xScale = d3
       .scaleLinear()
-      .range([0, this.width - 10])
-      .domain([0, this.max]);
+      .domain([0, this.max])
+      .range([0, this.width - 10]);
+      
     this.xAxis = d3.axisTop(this.xScale).tickFormat(d => {
       if (d === 0) return 0;
       if (d >= 1000) return d / 1000 + 's';
       return d + ' ms';
     });
-
-    this.body = d3
-      .select(this.el)
-      .append('svg')
-      .attr('width', this.width)
-      .attr('height', this.height);
-    this.timeGroup = this.body
-      .append('g')
-      .attr('transform', d => 'translate(5,30)');
-    const main = this.body
-      .append('g')
-      .attr('transform', d => 'translate(0,' + this.row.length * 9 + ')');
-    this.svg = main.append('g');
-    this.root = d3.hierarchy(this.data, d => d.children);
-    this.root.x0 = this.height / 2;
-    this.root.y0 = 0;
     this.body
-      .append('g')
-      .attr('transform', `translate(5,20)`)
-      .call(this.xAxis);
-
+    .append('g')
+    .attr('class', 'xAxis')
+    .attr('transform', `translate(5,20)`)
+    .call(this.xAxis);
     this.updatexAxis(this.root);
     this.update(this.root);
   }
-  updatexAxis(source) {
-    const treeData = this.treemap(this.root);
-    const nodes = treeData.descendants(),
-      links = treeData.descendants().slice(1);
-    let index = -1;
-    nodes.forEach(function(d) {
-      d.y = d.depth * 200;
-      d.timeX = ++index * 12;
-    });
-    // time
-    const timeNode = this.timeGroup.selectAll('g.time').data(nodes, d => {
-      return d.id;
-    });
-    this.timeTip = d3tip()
-      .attr('class', 'd3-tip')
-      .offset([-10, 0])
-      .html(function(d) {
-        return d.data.label;
-      });
-    this.body.call(this.timeTip);
-
+  update(source) {
     const that = this;
-    const timeEnter = timeNode
+    const links = this.nodes.slice(1);
+    const node = this.svg.selectAll('g.node').data(this.nodes, d => {
+      return d.id|| (d.id = ++this.i);
+    });
+    // node
+    const nodeEnter = node
       .enter()
       .append('g')
-      .attr('class', 'time')
-      .attr('transform', d => 'translate(' + 0 + ',' + d.timeX + ')')
+      .attr('class', 'node')
+      .attr('transform', `translate(${source.y0},${source.x0})`)
       .on('mouseover', function(d, i) {
-        that.timeTip.show(d, that.timeUpdate._groups[0][i].children[1]);
-        that.tip.show(d, that.nodeUpdate._groups[0][i]);
+        that.tip.show(d, this);
+        const _node = that.timeUpdate._groups[0].filter(group => group.__data__.id === (i+1));
+        if(_node.length){
+          that.timeTip.show(d, _node[0].children[1]);
+        }
       })
       .on('mouseout', function(d, i) {
-        that.timeTip.hide(d, that.timeUpdate._groups[0][i].children[1]);
-        that.tip.hide(d, that.nodeUpdate._groups[0][i]);
+        that.tip.hide(d, this);
+        const _node = that.timeUpdate._groups[0].filter(group => group.__data__.id === (i+1));
+        if(_node.length){
+          that.timeTip.hide(d, _node[0].children[1]);
+        }
       })
       .on('click', (d, i) => {
         this.showSpanModal(
           d.data,
           { width: '100%', top: -10, left: '0' },
-          d3.select(that.timeUpdate._groups[0][i]).append('rect')
+          d3.select(nodeEnter._groups[0][i]).append('rect')
         );
         d3.event.stopPropagation();
       });
-    this.timeEnter = timeEnter;
-    timeEnter
-      .append('rect')
-      .attr('height', 10)
-      .attr('width', this.width)
-      .attr('y', -4)
-      .attr('class', 'time-bg');
-    timeEnter
+    const nodeSelfDur = nodeEnter
+      .append('g')
+      .style('opacity', 0)
+      .attr('class','trace-tree-node-selfdur')
+      .attr('transform', 'translate(0,-39)')
+    nodeSelfDur
       .append('rect')
-      .attr('class', 'time-inner')
-      .attr('height', 8)
-      .attr('width', d => {
-        if (!d.data.endTime || !d.data.startTime) return 0;
-        return this.xScale(d.data.endTime - d.data.startTime) + 1;
-      })
-      .attr('rx', 2)
-      .attr('ry', 2)
-      .attr('x', d =>
-        !d.data.endTime || !d.data.startTime
-          ? 0
-          : this.xScale(d.data.startTime - this.min)
-      )
-      .attr('y', -3)
-      .style(
-        'fill',
-        d =>
-          `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}`
-      );
-    this.timeUpdate = timeEnter.merge(timeNode);
-    this.timeUpdate
-      .transition()
-      .duration(600)
-      .attr('transform', function(d) {
-        return 'translate(' + 0 + ',' + d.timeX + ')';
-      });
-
-    const timeExit = timeNode
-      .exit()
-      .transition()
-      .duration(600)
-      .attr('transform', function(d) {
-        return 'translate(' + 0 + ',' + 10 + ')';
+      .attr('width', 65)
+      .attr('height', 16)
+      .attr('rx', 3)
+      .attr('ry', 3)
+      .attr('fill', '#333')
+    nodeSelfDur
+      .append('text')
+      .attr('dx', 5)
+      .attr('dy', 11)
+      .text(d=> {
+        return d.data.dur + ' ms'
       })
-      .remove();
-  }
-  update(source) {
-    const treeData = this.treemap(this.root);
-    const nodes = treeData.descendants(),
-      links = treeData.descendants().slice(1);
-    let index = -1;
-    nodes.forEach(function(d) {
-      d.y = d.depth * 200;
-      d.timeX = ++index * 10;
-    });
-    this.tip = d3tip()
-      .attr('class', 'd3-tip')
-      .offset([-10, 0])
-      .html(function(d) {
-        return d.data.label;
-      });
-
-    this.body.call(this.getZoomBehavior(this.svg));
-    this.body.call(this.tip);
-    const node = this.svg.selectAll('g.node').data(nodes, d => {
-      return d.id || (d.id = ++this.i);
-    });
-    // node
-    const that = this;
-    const nodeEnter = node
-      .enter()
+      .attr('fill', '#fff');
+    const nodeSelfChild = nodeEnter
       .append('g')
-      .attr('class', 'node')
-      .attr('transform', function(d) {
-        return 'translate(' + source.y0 + ',' + source.x0 + ')';
-      })
-      .on('mouseover', function(d, i) {
-        that.tip.show(d, this);
-        that.timeTip.show(d, that.timeUpdate._groups[0][i].children[1]);
-      })
-      .on('mouseout', function(d, i) {
-        that.tip.hide(d, this);
-        that.timeTip.hide(d, that.timeUpdate._groups[0][i].children[1]);
-      })
-    .on('click', (d, i) => {
-      this.showSpanModal(
-        d.data,
-        { width: '100%', top: -10, left: '0' },
-        d3.select(nodeEnter._groups[0][i]).append('rect')
-      );
-      d3.event.stopPropagation();
-    });
+      .style('opacity', 0)
+      .attr('class','trace-tree-node-selfchild')
+      .attr('transform', 'translate(0,-39)')
+      nodeSelfChild
+      .append('rect')
+      .attr('width', 110)
+      .attr('height', 16)
+      .attr('rx', 3)
+      .attr('ry', 3)
+      .attr('fill', '#333')
+      nodeSelfChild
+      .append('text')
+      .attr('dx', 5)
+      .attr('dy', 11)
+      .text(d=> `children: ${d.data.childrenLength}`)
+      .attr('fill', '#fff') 
     nodeEnter
       .append('rect')
       .attr('class', 'block')
@@ -231,78 +196,47 @@ export default class TraceMap {
       .attr('y', '-20')
       .attr('fill', d => (d.data.isError ? '#ff57221a' : '#f7f7f7'))
       .attr('stroke', d => (d.data.isError ? '#ff5722aa' : '#e4e4e4'));
-
     nodeEnter
       .append('rect')
       .attr('class', 'content')
-      .style('fill', '#fff')
-      .attr('stroke', d => {
-        console.log(d);
-        return d.data.isError ? '#ff5722aa' : '#e4e4e4';
-      });
+      .attr('stroke', d => d.data.isError ? '#ff5722aa' : '#e4e4e4');
     nodeEnter
       .append('rect')
       .attr('class', 'service')
       .attr('x', '-0.5')
       .attr('y', '-20.5')
-      .style(
-        'fill',
-        d =>
-          `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}`
-      );
+      .style('fill', d => `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}`);
     nodeEnter
       .append('text')
       .attr('dy', 13)
       .attr('dx', 10)
       .attr('stroke', '#333')
-      .attr('text-anchor', function(d) {
-        return 'start';
-      })
-      .text(function(d) {
-        return d.data.label.length > 19
-          ? d.data.label.slice(0, 19)
-          : d.data.label;
-      });
+      .attr('text-anchor', 'start')
+      .text(d => d.data.label.length > 19 ? d.data.label.slice(0, 19) : d.data.label);
 
     nodeEnter
       .append('text')
       .attr('dy', -7)
       .attr('dx', 12)
-      .attr('text-anchor', function(d) {
-        return 'start';
-      })
-      .attr('fill', d => {
-        return this.type[d.data.layer];
-      })
-      .attr('stroke', d => {
-        return this.type[d.data.layer];
-      })
-      .text(function(d) {
-        return d.data.layer;
-      });
+      .attr('text-anchor', 'start')
+      .attr('fill', d => this.type[d.data.layer])
+      .attr('stroke', d => this.type[d.data.layer])
+      .text(d => d.data.layer);
 
     nodeEnter
       .append('text')
       .attr('dy', -7)
       .attr('x', 95)
       .attr('stroke', '#333')
-      .attr('text-anchor', function(d) {
-        return 'start';
-      })
-      .text(function(d) {
-        return d.data.endTime
-          ? d.data.endTime - d.data.startTime + ' ms'
-          : d.data.traceId;
-      });
+      .attr('text-anchor', 'start')
+      .text(d => d.data.endTime ? d.data.endTime - d.data.startTime + ' ms' : d.data.traceId);
 
     nodeEnter
       .append('circle')
       .attr('class', 'node')
       .attr('r', 4)
       .attr('cx', '158')
-      .style('fill', function(d) {
-        return d._children ? '#8543e0aa' : '#fff';
-      })
+      .style('fill', d => d._children ? '#8543e0aa' : '#fff')
       .on('click', click);
 
     this.nodeUpdate = nodeEnter.merge(node);
@@ -318,12 +252,10 @@ export default class TraceMap {
       .select('circle.node')
       .attr('r', 4)
       .attr('cx', '158')
-      .style('fill', function(d) {
-        return d._children ? '#8543e0aa' : '#fff';
-      })
+      .style('fill', d => d._children ? '#8543e0aa' : '#fff')
       .attr('cursor', 'pointer');
 
-    var nodeExit = node
+    const nodeExit = node
       .exit()
       .transition()
       .duration(600)
@@ -332,13 +264,8 @@ export default class TraceMap {
       })
       .remove();
 
-    nodeExit.select('circle').attr('r', 0);
-
-    nodeExit.select('text').style('fill-opacity', 0);
-
-    const link = this.svg.selectAll('path.link').data(links, function(d) {
-      return d.id;
-    });
+    // link
+    const link = this.svg.selectAll('path.link').data(links, d => d.id);
 
     const linkEnter = link
       .enter()
@@ -368,19 +295,14 @@ export default class TraceMap {
       })
       .remove();
 
-    nodes.forEach(function(d) {
-      d.x0 = d.x;
-      d.y0 = d.y;
-    });
-
     function diagonal(s, d) {
       return `M ${s.y} ${s.x}
       C ${s.y - 30} ${s.x}, ${d.y + 188} ${d.x},
       ${d.y + 158} ${d.x}`;
     }
     function click(d, i) {
-      that.tip.hide(d, this);
-      that.timeTip.hide(d, that.timeUpdate._groups[0][i]);
+      // that.tip.hide(d, this);
+      // that.timeTip.hide(d, that.timeUpdate._groups[0][i]);
       if (d.children) {
         d._children = d.children;
         d.children = null;
@@ -393,6 +315,134 @@ export default class TraceMap {
       d3.event.stopPropagation();
     }
   }
+  updatexAxis(source) {
+    // time
+    const that = this;
+    this.nodes = this.treemap(this.root).descendants();
+    let index = -1;
+    this.nodes.forEach(function(d) {
+      d.y = d.depth * 200;
+      d.timeX = ++index * 12;
+      d.x0 = d.x;
+      d.y0 = d.y;
+    });
+    const timeNode = this.timeGroup.selectAll('g.time').data(this.nodes, d => {
+      return d.id|| (d.id = ++this.j);
+    });
+    this.timeNode = timeNode;
+    const timeEnter = timeNode
+      .enter()
+      .append('g')
+      .attr('class', 'time')
+      .attr('transform', d => `translate(0,${d.timeX})`)
+      .on('mouseover', function(d, i) {
+        that.timeTip.show(d, this);
+        const _node = that.nodeUpdate._groups[0].filter(group => group.__data__.id === (i+1));
+        if(_node.length){
+          that.tip.show(d, _node[0]);
+        }
+      })
+      .on('mouseout', function(d, i) {
+        that.timeTip.hide(d, this);
+        const _node = that.nodeUpdate._groups[0].filter(group => group.__data__.id === (i+1));
+        if(_node.length){
+          that.tip.hide(d, _node[0]);
+        }
+      })
+      .on('click', (d, i) => {
+        this.showSpanModal(
+          d.data,
+          { width: '100%', top: -10, left: '0' },
+          d3.select(that.timeUpdate._groups[0][i]).append('rect')
+        );
+        d3.event.stopPropagation();
+      });
+    timeEnter
+      .append('rect')
+      .attr('height', 10)
+      .attr('width', this.width)
+      .attr('y', -4)
+      .attr('class', 'time-bg');
+    timeEnter
+      .append('rect')
+      .attr('class', 'time-inner')
+      .attr('height', 8)
+      .attr('width', d => {
+        if (!d.data.endTime || !d.data.startTime) return 0;
+        return this.xScale(d.data.endTime - d.data.startTime) + 1;
+      })
+      .attr('rx', 2)
+      .attr('ry', 2)
+      .attr(
+        'x',
+        d => (!d.data.endTime || !d.data.startTime ? 0 : this.xScale(d.data.startTime - this.min))
+      )
+      .attr('y', -3)
+      .style('fill', d => `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}`);
+    timeEnter
+      .append('rect')
+      .style('opacity',0)
+      .attr('class', 'time-inner-duration')
+      .attr('height', 8)
+      .attr('width', d => {
+        if (!d.data.dur) return 1;
+        return this.xScale(d.data.dur) + 1;
+      })
+      .attr('rx', 2)
+      .attr('ry', 2)
+      .attr(
+        'x',
+        d => (!d.data.endTime || !d.data.startTime ? 0 : this.xScale(d.data.startTime - this.min))
+      )
+      .attr('y', -3)
+      .style('fill', d => `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}`);
+
+    this.timeUpdate = timeEnter.merge(timeNode);
+    this.timeUpdate
+      .transition()
+      .duration(600)
+      .attr('transform', d => `translate(0,${d.timeX})`);
+
+    const timeExit = timeNode
+      .exit()
+      .transition()
+      .duration(600)
+      .attr('transform', 'translate(0 ,10)')
+      .remove();
+  }
+  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 => {
+      d3.select(i).style('opacity', 1);
+    })
+  }
+  topChild() {
+    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 => {
+      d3.select(i).style('opacity', .2);
+      if(i.__data__.data.childrenLength >= this.cmin && i.__data__.data.childrenLength <= this.cmax){
+        d3.select(i).style('opacity', 1);
+      }
+    })
+  }
+  topSlow() {
+    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 => {
+      d3.select(i).style('opacity', .2);
+      if(i.__data__.data.dur >= this.smin && i.__data__.data.dur <= this.smax){
+        d3.select(i).style('opacity', 1);
+      }
+    })
+  }
   getZoomBehavior(g) {
     return d3
       .zoom()
@@ -400,9 +450,7 @@ export default class TraceMap {
       .on('zoom', () => {
         g.attr(
           'transform',
-          `translate(${d3.event.transform.x},${d3.event.transform.y})scale(${
-            d3.event.transform.k
-          })`
+          `translate(${d3.event.transform.x},${d3.event.transform.y})scale(${d3.event.transform.k})`
         );
       });
   }
index 2a495c5..6e740ea 100644 (file)
  */
 
 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.state = {}
+    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() {
@@ -94,23 +108,80 @@ export default class Trace extends Component {
       if(segmentGroup[i].refs.length ===0 )
       this.segmentId.push(segmentGroup[i]);
     }
-    this.tree = new Tree(this.echartsElement)
-    this.tree.draw({label:`${this.traceId}`, children: this.segmentId}, rowData,  propsData.showSpanModal);
+    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: 700,
+      height: 800,
       // ...style,
     };
     return (
-      <div
-        ref={(e) => { this.echartsElement = e; }}
-        style={newStyle}
-        className="trace-tree"
-      />
+      <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>
     )
   }
 }
index 90093ff..5924b85 100644 (file)
@@ -29,7 +29,7 @@
   cursor: pointer;\r
 }\r
 :global(.trace-tree .time-bg) {\r
-  fill: #fff;\r
+  fill: rgba(0, 0, 0, 0);\r
   cursor: pointer;\r
 }\r
 :global(.trace-tree .time:hover) {\r
@@ -49,6 +49,7 @@
 :global(.trace-tree .node .content) {\r
   width: 158px;\r
   height: 20px;\r
+  fill: #fff;\r
 }\r
 :global(.trace-tree .node .service) {\r
   width: 7px;\r