You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@skywalking.apache.org by ha...@apache.org on 2018/01/17 07:45:07 UTC
[incubator-skywalking-ui] branch feature/5.0.0 updated: Add trace
stack component
This is an automated email from the ASF dual-hosted git repository.
hanahmily pushed a commit to branch feature/5.0.0
in repository https://gitbox.apache.org/repos/asf/incubator-skywalking-ui.git
The following commit(s) were added to refs/heads/feature/5.0.0 by this push:
new 59b08f7 Add trace stack component
new e395a52 Merge branch 'feature/5.0.0' of https://github.com/apache/incubator-skywalking-ui into feature/5.0.0
59b08f7 is described below
commit 59b08f76ccd9eb793fdfa72ebb54525973c54c3e
Author: hanahmily <ha...@gmail.com>
AuthorDate: Wed Jan 17 15:42:20 2018 +0800
Add trace stack component
---
src/main/frontend/package.json | 1 +
.../frontend/src/components/TraceStack/index.js | 192 +++++++++++++++++++++
.../frontend/src/components/TraceStack/index.less | 51 ++++++
3 files changed, 244 insertions(+)
diff --git a/src/main/frontend/package.json b/src/main/frontend/package.json
index 5e22757..81428d4 100755
--- a/src/main/frontend/package.json
+++ b/src/main/frontend/package.json
@@ -26,6 +26,7 @@
"cytoscape": "^3.2.7",
"cytoscape-cose-bilkent": "^4.0.0",
"cytoscape-node-html-label": "^1.0.3",
+ "d3": "^4.12.2",
"dva": "^2.0.3",
"dva-loading": "^1.0.4",
"lodash": "^4.17.4",
diff --git a/src/main/frontend/src/components/TraceStack/index.js b/src/main/frontend/src/components/TraceStack/index.js
new file mode 100644
index 0000000..5f09634
--- /dev/null
+++ b/src/main/frontend/src/components/TraceStack/index.js
@@ -0,0 +1,192 @@
+import React, { PureComponent } from 'react';
+import { Tag } from 'antd';
+import * as d3 from 'd3';
+import styles from './index.less';
+
+const colors = [
+ '#F2C2CE',
+ '#A7D8F0',
+ '#FADDA2',
+ '#8691C5',
+ '#E8DB62',
+ '#BDC8E7',
+ '#F2A7A8',
+ '#F5E586',
+ '#91C3ED',
+ '#96B87F',
+ '#EE8D87',
+ '#BDDCAB',
+ '#68B9C7',
+ '#93DAD6',
+ '#EEBE84',
+ '#83B085',
+ '#8CCCD2',
+ '#C5DFE8',
+ '#F2B75B',
+ '#C8DC60',
+];
+const height = 36;
+const margin = 10;
+const offX = 15;
+const offY = 6;
+class TraceStack extends PureComponent {
+ state = {
+ nodes: [],
+ idMap: {},
+ colorMap: {},
+ bap: [],
+ }
+ componentWillMount() {
+ const { spans } = this.props;
+ spans.forEach(this.buildNode);
+ const { nodes } = this.state;
+ const minStartTimeNode = nodes.reduce((acc, n) => (acc.startTime > n.startTime ? n : acc));
+ this.state.nodes = nodes.map(n =>
+ ({ ...n, startOffset: n.startTime - minStartTimeNode.startTime }));
+ }
+ componentDidMount() {
+ this.state.width = this.axis.parentNode.clientWidth - 50;
+ this.drawAxis();
+ this.displayData();
+ }
+ buildNode = (span, index) => {
+ const { nodes, colorMap, idMap } = this.state;
+ const node = {};
+ node.applicationCode = span.applicationCode;
+ node.startTime = span.startTime;
+ node.endTime = span.endTime;
+ node.duration = span.endTime - span.startTime;
+ node.content = span.operationName;
+ node.spanSegId = span.spanId;
+ node.parentSpanSegId = span.parentSpanId;
+ nodes.push(node);
+
+ if (!colorMap[span.applicationCode]) {
+ colorMap[span.applicationCode] = colors[index];
+ }
+ idMap[node.spanSegId] = nodes.length - 1;
+ }
+ drawAxis = () => {
+ const { width } = this.state;
+ const { nodes, bap } = this.state;
+ const dataSet = nodes.map(node => node.startOffset + node.duration);
+ const bits = d3.max(dataSet).toString().length;
+ const percentScale = Math.ceil(d3.max(dataSet) / (10 ** (bits - 2)));
+ const axisHeight = 20;
+
+ const svg = d3.select(this.axis).append('svg')
+ .attr('width', width)
+ .attr('height', axisHeight)
+ .attr('style', 'overflow: visible');
+
+ const xScale = d3.scaleLinear()
+ .domain([0, percentScale * (10 ** (bits - 2))])
+ .range([0, width]);
+
+ const axis = d3.axisTop(xScale).ticks(20);
+
+ svg.append('g')
+ .attr('class', styles.axis)
+ .attr('transform', `translate(0, ${axisHeight})`)
+ .call(axis);
+
+ bap.push(bits);
+ bap.push(percentScale);
+ return bap;
+ }
+ displayData = () => {
+ const { nodes, bap, width, colorMap } = this.state;
+ const svgContainer = d3.select(this.duration).append('svg').attr('height', height * nodes.length).attr('style', 'overflow: visible');
+ const positionMap = {};
+ nodes.forEach((node, index) => {
+ const { startOffset: startTime, duration, content,
+ applicationCode, spanSegId, parentSpanSegId } = node;
+
+ const rectWith = ((duration * width) / (bap[1] * (10 ** (bap[0] - 4)))) / 100;
+ const beginX = ((startTime * width) / (bap[1] * (10 ** (bap[0] - 4)))) / 100;
+ const bar = svgContainer.append('g').attr('transform', (d, i) => `translate(0,${i * height})`);
+
+ const beginY = index * height;
+ positionMap[spanSegId] = { x: beginX, y: beginY };
+
+ bar.append('rect').attr('x', beginX).attr('y', beginY).attr('width', rectWith)
+ .attr('height', height - margin)
+ .style('fill', colorMap[applicationCode]);
+
+ bar.append('rect').attr('spanSegId', spanSegId).attr('x', 0).attr('y', beginY)
+ .attr('width', width)
+ .attr('height', height - margin)
+ .style('opacity', '0')
+ .on('click', () => { console.info(spanSegId); });
+
+ bar.append('text')
+ .attr('x', beginX + 5)
+ .attr('y', (index * height) + (height / 2))
+ .attr('class', styles.rectText)
+ .text(content);
+ if (index > 0) {
+ const parentX = positionMap[parentSpanSegId].x;
+ const parentY = positionMap[parentSpanSegId].y;
+
+ const defs = svgContainer.append('defs');
+ const arrowMarker = defs.append('marker')
+ .attr('id', 'arrow')
+ .attr('markerUnits', 'strokeWidth')
+ .attr('markerWidth', 12)
+ .attr('markerHeight', 12)
+ .attr('viewBox', '0 0 12 12')
+ .attr('refX', 6)
+ .attr('refY', 6)
+ .attr('orient', 'auto');
+ arrowMarker.append('path')
+ .attr('d', 'M2,2 L10,6 L2,10 L6,6 L2,2')
+ .attr('fill', '#333');
+
+ const parentLeftBottomX = parentX;
+ const parentLeftBottomY = (Number(parentY) + Number(height)) - Number(margin);
+ const selfMiddleX = beginX;
+ const selfMiddleY = beginY + ((height - margin) / 2);
+ if ((beginX - parentX) < 10) {
+ svgContainer.append('line').attr('x1', parentLeftBottomX - offX).attr('y1', parentLeftBottomY - offY).attr('class', styles.connlines)
+ .attr('x2', parentLeftBottomX)
+ .attr('y2', parentLeftBottomY - offY);
+
+ svgContainer.append('line').attr('x1', parentLeftBottomX - offX).attr('y1', parentLeftBottomY - offY).attr('class', styles.connlines)
+ .attr('x2', parentLeftBottomX - offX)
+ .attr('y2', selfMiddleY);
+
+ svgContainer.append('line').attr('x1', parentLeftBottomX - offX).attr('y1', selfMiddleY).attr('class', styles.connlines)
+ .attr('x2', selfMiddleX)
+ .attr('y2', selfMiddleY)
+ .attr('marker-end', 'url(#arrow)');
+ } else {
+ svgContainer.append('line').attr('x1', parentLeftBottomX).attr('y1', parentLeftBottomY).attr('class', styles.connlines)
+ .attr('x2', parentLeftBottomX)
+ .attr('y2', selfMiddleY);
+
+ svgContainer.append('line').attr('x1', parentLeftBottomX).attr('y1', selfMiddleY).attr('class', styles.connlines)
+ .attr('x2', selfMiddleX)
+ .attr('y2', selfMiddleY)
+ .attr('marker-end', 'url(#arrow)');
+ }
+ }
+ });
+ }
+ showSpanModal = () => {}
+ render() {
+ const { colorMap } = this.state;
+ const legendButtons = Object.keys(colorMap).map(key =>
+ (<Tag color={colorMap[key]}>{key}</Tag>));
+ return (
+ <div className={styles.stack}>
+ <div style={{ 'padding-bottom': 10 }}>
+ { legendButtons }
+ </div>
+ <div ref={(el) => { this.axis = el; }} />
+ <div className={styles.duration} ref={(el) => { this.duration = el; }} />
+ </div>
+ );
+ }
+}
+
+export default TraceStack;
diff --git a/src/main/frontend/src/components/TraceStack/index.less b/src/main/frontend/src/components/TraceStack/index.less
new file mode 100644
index 0000000..7b3cc59
--- /dev/null
+++ b/src/main/frontend/src/components/TraceStack/index.less
@@ -0,0 +1,51 @@
+.axis {
+ position: absolute;
+ right: 0;
+ color: #333;
+ text-align: right;
+ font-size: 12px;
+ path {
+ fill: none;
+ stroke: black;
+ }
+ line {
+ fill: none;
+ stroke: black;
+ }
+ text {
+ font-size: 11px;
+ }
+}
+
+.rectText {
+ font: 10px sans-serif;
+ font-weight: 100;
+ fill: #393939;
+}
+
+.stack {
+ font-family: 'Microsoft Yahei';
+ border-radius: 5px;
+ position: relative;
+ display: block;
+ background: rgba(255, 255, 255, 9);
+}
+
+.duration {
+ padding-top: 25px;
+ margin: 0;
+ width: 100%;
+}
+
+.connlines {
+ stroke: #333;
+ shape-rendering: crispEdges;
+ stroke-width: 1px;
+ stroke-opacity: 1;
+}
+
+.legend {
+ color: #fff;
+ margin-right: 10px;
+ margin-top: 5px;
+}
--
To stop receiving notification emails like this one, please contact
['"commits@skywalking.apache.org" <co...@skywalking.apache.org>'].