HTRACE-121. Details page: Graph parents and children

Project: http://git-wip-us.apache.org/repos/asf/incubator-htrace/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-htrace/commit/61738b5a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-htrace/tree/61738b5a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-htrace/diff/61738b5a

Branch: refs/heads/master
Commit: 61738b5abddc8a046a535ff703dfa2c3ceb56b52
Parents: a9a4f41
Author: Abraham Elmahrek <[email protected]>
Authored: Sat Feb 28 16:30:30 2015 -0800
Committer: Abraham Elmahrek <[email protected]>
Committed: Mon Mar 16 15:39:55 2015 -0700

----------------------------------------------------------------------
 htrace-core/src/web/app/setup.js             |   19 +-
 htrace-core/src/web/app/views/graph/graph.js |  203 +
 htrace-core/src/web/index.html               |   62 +-
 htrace-core/src/web/lib/css/main.css         |   12 +
 htrace-core/src/web/lib/js/d3-3.5.5.js       | 9504 +++++++++++++++++++++
 5 files changed, 9761 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/61738b5a/htrace-core/src/web/app/setup.js
----------------------------------------------------------------------
diff --git a/htrace-core/src/web/app/setup.js b/htrace-core/src/web/app/setup.js
index ddeec5e..a0c1b07 100644
--- a/htrace-core/src/web/app/setup.js
+++ b/htrace-core/src/web/app/setup.js
@@ -77,12 +77,25 @@ var Router = Backbone.Marionette.AppRouter.extend({
   },
 
   "span": function(id) {
+    var span = this.spansCollection.findWhere({
+      "spanId": id
+    });
+
+    if (!span) {
+      Backbone.history.navigate("!/search", {"trigger": true});
+      return;
+    }
+
     var top = new app.DetailsView();
     app.root.app.show(top);
     top.span.show(new app.SpanDetailsView({
-      "model": this.spansCollection.findWhere({
-        "spanId": id
-      })
+      "model": span
+    }));
+    top.content.show(new app.GraphView({
+      "collection": this.spansCollection,
+      "spanId": id,
+      "el": top.content.$el[0],
+      "id": "span-" + id
     }));
   }
 });

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/61738b5a/htrace-core/src/web/app/views/graph/graph.js
----------------------------------------------------------------------
diff --git a/htrace-core/src/web/app/views/graph/graph.js 
b/htrace-core/src/web/app/views/graph/graph.js
new file mode 100644
index 0000000..def16d3
--- /dev/null
+++ b/htrace-core/src/web/app/views/graph/graph.js
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+app.GraphView = Backbone.View.extend({
+  "MAX_NODE_SIZE": 100,
+  "MIN_NODE_SIZE": 30,
+
+  initialize: function(options) {
+    options = options || {};
+    _.bindAll(this, "render");
+    this.collection.bind('change', this.render);
+
+    if (!options.id) {
+      console.error("GraphView requires argument 'id' to uniquely identify 
this graph.");
+      return;
+    }
+    if (!options.el) {
+      console.error("GraphView requires argument 'el' to bind the graph to.");
+      return;
+    }
+    if (!options.spanId) {
+      console.error("GraphView requires argument 'spanId' as a start point.");
+      return;
+    }
+
+    this.spanId = options.spanId;
+
+    window.force = this.force
+        = d3.layout.force().size([1000, 1000])
+                           .alpha(0.1)
+                           .linkDistance(this.MAX_NODE_SIZE*2)
+                           .charge(-600)
+                           .linkDistance(150)
+                           .linkStrength(1)
+                           .friction(0.2)
+                           // .gravity(0.1)
+                           ;
+
+    force.on("tick", function(e) {
+      var root = d3.select("#" + options.id);
+
+      // Push sources up and targets down to form a weak tree.
+      var k = 10 * e.alpha;
+      force.links().forEach(function(d, i) {
+        d.source.y -= k;
+        d.target.y += k;
+      });
+
+      // set selected node in the middle.
+      // var selectedNode = force.nodes()[0];
+      // selectedNode.x = 500;
+      // selectedNode.y = 500;
+
+      var nodes = root.selectAll(".node").data(force.nodes());
+      nodes.select("circle")
+        .attr("cx", function(d) { return d.x; })
+        .attr("cy", function(d) { return d.y; });
+      nodes.select("text")
+        .attr("x", function(d) { return d.x; })
+        .attr("y", function(d) { return d.y; });
+      root.selectAll(".link").data(force.links())
+        .attr("x1", function(d) { return d.source.x; })
+        .attr("y1", function(d) { return d.source.y; })
+        .attr("x2", function(d) { return d.target.x; })
+        .attr("y2", function(d) { return d.target.y; });
+    });
+  },
+
+  parents: function(span) {
+    var collection = this.collection;
+    return _(span.get("parents")).map(function(parentSpanId) {
+      collection.findWhere({
+        "spanId": parentSpanId
+      });
+    });
+  },
+
+  children: function(span) {
+    var spanId = span.get("spanId");
+    return this.collection.filter(function(model) {
+      return _(model.get("parents")).contains(spanId);
+    });
+  },
+
+  linksAndNodes: function() {
+    var links = [],
+        nodes = [];
+    var selectedSpan = this.collection.findWhere({
+      "spanId": this.spanId
+    });
+    var parents = this.parents(selectedSpan);
+    var children = this.children(selectedSpan);
+
+    var group = 0;
+    var spanToNode = (function() {
+      var xmap = {};
+      return function(span, level) {
+        return {
+          "name": span.get("description"),
+          "span": span
+        };
+      }
+    })();
+    var createLink = function(source, target) {
+      return {
+        "source": source,
+        "target": target
+      };
+    };
+
+    var selectedSpanNode = spanToNode(selectedSpan, 1);
+    nodes.push(selectedSpanNode);
+
+    _(parents).each(function(span) {
+      var node = spanToNode(span, 0);
+      nodes.push(node);
+      links.push(createLink(node, selectedSpanNode));
+    });
+
+    _(children).each(function(span) {
+      var node = spanToNode(span, 2);
+      nodes.push(node);
+      links.push(createLink(selectedSpanNode, node));
+    });
+
+    return {
+      "links": links,
+      "nodes": nodes
+    };
+  },
+
+  renderLinks: function(selection) {
+    selection.append("line")
+        .attr("class", "link");
+    return selection;
+  },
+
+  renderNodes: function(selection) {
+    var MAX_NODE_SIZE = this.MAX_NODE_SIZE,
+        MIN_NODE_SIZE = this.MIN_NODE_SIZE;
+    var g = selection.append("g").attr("class", "node");
+    g.append("circle")
+      .attr("r", function(d) {
+        var reduced = Math.log(d.span.duration());
+         
+        if (reduced > MAX_NODE_SIZE) {
+          return MAX_NODE_SIZE;
+        }
+
+        if (reduced < MIN_NODE_SIZE) {
+          return MIN_NODE_SIZE;
+        }
+
+        return reduced;
+      });
+    var text = g.append("text").text(function(d) {
+      return d.name;
+    });
+    
+    return selection;
+  },
+
+  render: function() {
+    var svg = d3.select(this.$el[0]).append("svg")
+      .attr("height", 1000)
+      .attr("width", 1000)
+      .attr("id", this.id);
+    var data = this.linksAndNodes();
+
+    this.force
+      .nodes(data.nodes)
+      .links(data.links)
+      .start();
+
+    var link = this.renderLinks(
+      svg.selectAll(".link")
+        .data(this.force.links())
+        .enter());
+
+    var node = this.renderNodes(
+      svg.selectAll(".node")
+        .data(this.force.nodes())
+        .enter());
+
+    return this;
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/61738b5a/htrace-core/src/web/index.html
----------------------------------------------------------------------
diff --git a/htrace-core/src/web/index.html b/htrace-core/src/web/index.html
index c57bc4c..ecb0d7e 100644
--- a/htrace-core/src/web/index.html
+++ b/htrace-core/src/web/index.html
@@ -131,6 +131,8 @@
         <div class="col-md-12" role="main"></div>
       </div>
 
+      <hr>
+
       <div class="row">
         <div class="col-md-12" role="complementary"></div>
       </div>
@@ -138,44 +140,32 @@
     </script>
 
     <script id="span-details-template" type="text/html">
-      <div class="page-header">
-        <h1><%- span.description %></h1>
-      </div>
-      <div class="page-contents">
-        <div class="row">
-          <div class="col-md-2">
-            <h4>Span id</h4>
-          </div>
-          <div class="col-md-10"><%- span.spanId %></div>
-        </div>
-        <div class="row">
-          <div class="col-md-2">
-            <h4>Process id</h4>
-          </div>
-          <div class="col-md-10"><%- span.processId %></div>
-        </div>
-        <div class="row">
-          <div class="col-md-2">
-            <h4>Begin time</h4>
-          </div>
-          <div class="col-md-10"><%- span.beginTime %></div>
-        </div>
-        <div class="row">
-          <div class="col-md-2">
-            <h4>End time</h4>
-          </div>
-          <div class="col-md-10"><%- span.stopTime %></div>
-        </div>
-        <div class="row">
-          <div class="col-md-2">
-            <h4>Duration</h4>
-          </div>
-          <div class="col-md-10"><%- span.duration %></div>
-        </div>
-      </div>
+    <table class="table table-condensed">
+      <thead>
+        <tr>
+          <th>Description</th>
+          <th>Span ID</th>
+          <th>Process ID</th>
+          <th>Start time</th>
+          <th>End time</th>
+          <th>Duration</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><%- span.description %></td>
+          <td><%- span.spanId %></td>
+          <td><%- span.processId %></td>
+          <td><%- span.beginTime %></td>
+          <td><%- span.stopTime %></td>
+          <td><%- span.duration %></td>
+        </tr>
+      </tbody>
+    </table>
     </script>
 
     <script src="lib/js/jquery-2.1.3.min.js" type="text/javascript"></script>
+    <script src="lib/js/d3-3.5.5.js" type="text/javascript"></script>
     <script src="lib/bootstrap-3.3.1/js/bootstrap.min.js" 
type="text/javascript"></script>
     <script src="lib/js/underscore-1.7.0.js" type="text/javascript"></script>
     <script src="lib/js/backbone-1.1.2.js" type="text/javascript"></script>
@@ -188,7 +178,7 @@
 
     <script src="app/app.js" type="text/javascript"></script>
     <script src="app/models/span.js" type="text/javascript"></script>
-
+    <script src="app/views/graph/graph.js" type="text/javascript"></script>
     <script src="app/views/search/field.js" type="text/javascript"></script>
     <script src="app/views/search/search.js" type="text/javascript"></script>
     <script src="app/views/details/details.js" type="text/javascript"></script>

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/61738b5a/htrace-core/src/web/lib/css/main.css
----------------------------------------------------------------------
diff --git a/htrace-core/src/web/lib/css/main.css 
b/htrace-core/src/web/lib/css/main.css
index d5fd84f..c0a8f3e 100644
--- a/htrace-core/src/web/lib/css/main.css
+++ b/htrace-core/src/web/lib/css/main.css
@@ -26,3 +26,15 @@ li.span {
   margin: 0px;
   list-style: none;
 }
+
+/* Graph */
+svg .node circle {
+  stroke: #006600;
+  stroke-width: 3;
+  fill: #00CC00;
+}
+
+svg .link {
+  stroke: black;
+  stroke-width: 3;
+}
\ No newline at end of file

Reply via email to