| /**</template> |
| * 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 * as d3 from 'd3'; |
| import d3tip from 'd3-tip'; |
| /* eslint-disable */ |
| const type = { |
| MQ: '#bf99f8', |
| Http: '#72a5fd', |
| Database: '#ff6732', |
| Unknown: '#ffc107', |
| Cache: '#00bcd4', |
| RPCFramework: '#ee4395', |
| }; |
| export default class Trace { |
| constructor(el, show) { |
| this.barHeight = 48; |
| this.show = show; |
| this.el = el; |
| this.i = 0; |
| this.width = el.clientWidth; |
| this.height = el.clientHeight; |
| this.svg = d3 |
| .select(this.el) |
| .append('svg') |
| .attr('width', this.width) |
| .attr('height', this.height); |
| this.treemap = d3.tree().size([this.height * 0.7, this.width]); |
| this.tip = d3tip() |
| .attr('class', 'd3-tip') |
| .offset([-8, 0]) |
| .html(d => ` |
| <div class="mb-5">${d.data.label}</div> |
| ${d.data.dur?'<div class="sm">SelfDuration: ' + d.data.dur + 'ms</div>' : ''} |
| ${(d.data.endTime - d.data.startTime)?'<div class="sm">TotalDuration: ' + (d.data.endTime - d.data.startTime) + 'ms</div>' : ''} |
| `); |
| this.svg.call(this.tip); |
| } |
| diagonal(d) { |
| return `M ${d.source.y} ${d.source.x + 5} |
| L ${d.source.y} ${d.target.x - 30} |
| L${d.target.y} ${d.target.x - 20} |
| L${d.target.y} ${d.target.x - 5}`; |
| } |
| init(data, row) { |
| d3.select('.trace-xaxis').remove(); |
| this.row = row; |
| this.data = data; |
| this.min = d3.min(this.row.map(i => i.startTime)); |
| this.max = d3.max(this.row.map(i => i.endTime - this.min)); |
| this.list = Array.from(new Set(this.row.map(i => i.serviceCode))); |
| this.xScale = d3 |
| .scaleLinear() |
| .range([0, this.width * 0.387]) |
| .domain([0, this.max]); |
| this.xAxis = d3.axisTop(this.xScale).tickFormat(d => { |
| if(d === 0) return 0; |
| if(d>=1000) return d/1000 + 's'; |
| return d; |
| }); |
| this.svg.attr('height', (this.row.length+1) * this.barHeight); |
| this.svg |
| .append('g') |
| .attr('class','trace-xaxis') |
| .attr('transform', `translate(${this.width * 0.618 -20 },${30})`) |
| .call(this.xAxis); |
| this.sequentialScale = d3 |
| .scaleSequential() |
| .domain([0, this.list.length + 1]) |
| .interpolator(d3.interpolateCool); |
| this.root = d3.hierarchy(this.data, d => d.children); |
| this.root.x0 = 0; |
| this.root.y0 = 0; |
| } |
| draw(callback) { |
| this.update(this.root, callback); |
| } |
| click(d, scope) { |
| if (!d.data.type) return; |
| if (d.children) { |
| d._children = d.children; |
| d.children = null; |
| } else { |
| d.children = d._children; |
| d._children = null; |
| } |
| scope.update(d); |
| } |
| update(source, callback) { |
| const that = this; |
| const nodes = this.root.descendants(); |
| let index = -1; |
| this.root.eachBefore(n => { |
| n.x = ++index * this.barHeight + 24; |
| n.y = n.depth * 12; |
| }); |
| const node = this.svg |
| .selectAll('.trace-node') |
| .data(nodes, d => d.id || (d.id = ++this.i)); |
| const nodeEnter = node |
| .enter() |
| .append('g') |
| .attr('transform', `translate(${source.y0},${source.x0})`) |
| .attr('class', 'trace-node') |
| .style('opacity', 0) |
| .on('mouseover', function(d, i) { |
| that.tip.show(d, this); |
| }) |
| .on('mouseout', function(d, i) { |
| that.tip.hide(d, this); |
| }) |
| .on('click', function(d) { |
| that.show.handleSelectSpan(d); |
| }); |
| nodeEnter |
| .append('rect') |
| .attr('height', 42) |
| .attr('ry',2) |
| .attr('rx',2) |
| .attr('y', -22) |
| .attr('x', 20) |
| .attr('width', '100%'); |
| nodeEnter |
| .append('text') |
| .attr('x', 13) |
| .attr('y', 5) |
| .attr('fill', '#E54C17') |
| .html(d => d.data.isError?'◉': '') |
| nodeEnter |
| .append('text') |
| .attr('class','node-text') |
| .attr('x', 35) |
| .attr('y', -6) |
| .attr('fill', '#333') |
| .text( d => |
| { |
| if(d.data.label === 'TRACE_ROOT') { |
| return ''; |
| } |
| return d.data.label.length > 40 |
| ? `${d.data.label.slice(0, 40)}...` |
| : `${d.data.label}` |
| } |
| ); |
| nodeEnter |
| .append('text') |
| .attr('class','node-text') |
| .attr('x', 35) |
| .attr('y', 12) |
| .attr('fill', '#ccc') |
| .style('font-size', '11px') |
| .text( |
| d => |
| `${d.data.layer || ''} ${ |
| d.data.component ? '- ' + d.data.component : d.data.component || '' |
| }` |
| ); |
| nodeEnter |
| .append('rect') |
| .attr('rx', 2) |
| .attr('ry', 2) |
| .attr('height', 4) |
| .attr('width', d => { |
| if (!d.data.endTime || !d.data.startTime) return 0; |
| return this.xScale(d.data.endTime- d.data.startTime)+1 || 0; |
| }) |
| .attr('x', d => |
| !d.data.endTime || !d.data.startTime |
| ? 0 |
| : (this.width * 0.618 - |
| 20 - |
| d.y + |
| this.xScale(d.data.startTime - this.min)) || 0 |
| ) |
| .attr('y', -2) |
| .style( |
| 'fill', |
| d => `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}` |
| ); |
| nodeEnter |
| .transition() |
| .duration(400) |
| .attr('transform', d => `translate(${d.y},${d.x})`) |
| .style('opacity', 1); |
| nodeEnter |
| .append('circle') |
| .attr('r', 3) |
| .style('cursor', 'pointer') |
| .attr('stroke-width', 2.5) |
| .attr('fill', d => |
| d._children |
| ? `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}` |
| : '' |
| ) |
| .style( |
| 'stroke', |
| d => d.data.label === 'TRACE_ROOT'?'':`${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}` |
| ) |
| .on('click', d => { |
| this.click(d, this) |
| d3.event.stopPropagation(); |
| |
| }); |
| node |
| .transition() |
| .duration(400) |
| .attr('transform', d => `translate(${d.y},${d.x})`) |
| .style('opacity', 1) |
| .select('circle') |
| .attr('fill', d => |
| d._children |
| ? `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}` |
| : '' |
| ); |
| |
| // Transition exiting nodes to the parent's new position. |
| node |
| .exit() |
| .transition() |
| .duration(400) |
| .attr('transform', `translate(${source.y},${source.x})`) |
| .style('opacity', 0) |
| .remove(); |
| const link = this.svg |
| .selectAll('.trace-link') |
| .data(this.root.links(), function(d) { |
| return d.target.id; |
| }); |
| |
| link |
| .enter() |
| .insert('path', 'g') |
| .attr('class', 'trace-link') |
| .attr('d', d => { |
| const o = { x: source.x0 + 35, y: source.y0 }; |
| return this.diagonal({ source: o, target: o }); |
| }) |
| .transition() |
| .duration(400) |
| .attr('d', this.diagonal); |
| |
| link |
| .transition() |
| .duration(400) |
| .attr('d', this.diagonal); |
| |
| link |
| .exit() |
| .transition() |
| .duration(400) |
| .attr('d', d => { |
| const o = { x: source.x + 35, y: source.y }; |
| return this.diagonal({ source: o, target: o }); |
| }) |
| .remove(); |
| this.root.each(function(d) { |
| d.x0 = d.x; |
| d.y0 = d.y; |
| }); |
| if (callback) { |
| callback() |
| } |
| } |
| } |