riemann-dash 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  var util = (function() {
2
+ // Unique IDs
2
3
  var uniqueId = function(bytes) {
3
4
  bytes = bytes || 20;
4
5
  var s = '';
@@ -13,8 +14,38 @@ var util = (function() {
13
14
  return _.extend(_.clone(m1), m2);
14
15
  }
15
16
 
17
+ // Wraps a function in another, which calls f at most once every period
18
+ // milliseconds. Tries to minimize latency.
19
+ var slur = function(period, f) {
20
+ var lastRun = new Date();
21
+ lastRun.setYear(0);
22
+ var queued = false;
23
+ var execute = function(context, args) {
24
+ f.apply(context, args);
25
+ lastRun = new Date();
26
+ queued = false;
27
+ };
28
+
29
+ return function() {
30
+ // If queued, do nothing
31
+ if (queued) {
32
+ return;
33
+ }
34
+
35
+ var dt = (new Date()) - lastRun;
36
+ if (period <= dt) {
37
+ // We're free to go
38
+ execute(this, arguments);
39
+ } else {
40
+ // Too soon, enqueue a new job.
41
+ window.setTimeout(execute, period - dt, this, arguments);
42
+ }
43
+ }
44
+ }
45
+
16
46
  return {
17
47
  merge: merge,
48
+ slur: slur,
18
49
  uniqueId: uniqueId
19
50
  }
20
51
  })();
@@ -10,12 +10,12 @@
10
10
  this.el.append(
11
11
  '<div class="box">' +
12
12
  '<div class="quickfit metric value">?</div>' +
13
- '<div class="quickfit title"></div>' +
13
+ '<h2 class="quickfit"></div>' +
14
14
  '</div>'
15
15
  );
16
16
 
17
17
  this.box = this.el.find('.box');
18
- this.el.find('.title').text(this.title);
18
+ this.el.find('h2').text(this.title);
19
19
 
20
20
  if (this.query) {
21
21
  var reflowed = false;
@@ -63,13 +63,13 @@
63
63
  value.quickfit({min: 6, max: 1000, font_height_scale: 1});
64
64
 
65
65
  // Size title
66
- var title = this.el.find('.title');
66
+ var title = this.el.find('h2');
67
67
  title.quickfit(fitopts);
68
68
  }
69
69
 
70
70
  Gauge.prototype.delete = function() {
71
71
  if (this.sub) {
72
- this.sub.close();
72
+ subs.unsubscribe(this.sub);
73
73
  }
74
74
  view.View.prototype.delete.call(this);
75
75
  }
@@ -11,11 +11,11 @@
11
11
  // Initial display
12
12
  this.el.addClass('grid');
13
13
  this.el.append(
14
- '<div class="title"></div>' +
15
- '<table></table>'
14
+ '<h2></h2>' +
15
+ '<div class="container"><table></table></div>'
16
16
  );
17
17
  this.box = this.el.find('.box');
18
- this.el.find('.title').text(this.title);
18
+ this.el.find('h2').text(this.title);
19
19
 
20
20
  // State
21
21
  this.hosts = [];
@@ -115,7 +115,7 @@
115
115
  }
116
116
 
117
117
  // Rerender the table
118
- Grid.prototype.render = function() {
118
+ Grid.prototype.render = util.slur(200, function() {
119
119
  var table = this.el.find('table');
120
120
  table.empty();
121
121
 
@@ -134,12 +134,12 @@
134
134
  row.find('th').text(host);
135
135
  this.services.forEach(function(service) {
136
136
  var event = this.events[host][service];
137
- var element = $('<td><span class="metric" /><div class="bar" /></td>');
137
+ var element = $('<td><span class="bar"><span class="metric"/></span></td>');
138
138
  this.renderElement(element, event);
139
139
  row.append(element);
140
140
  }, this);
141
141
  }, this);
142
- };
142
+ });
143
143
 
144
144
  // Update cached maxima with a new event. Returns true if maxima changed.
145
145
  Grid.prototype.updateMax = function(event) {
@@ -263,15 +263,15 @@
263
263
  }
264
264
 
265
265
  Grid.prototype.reflow = function() {
266
- this.el.find('table').height(
267
- this.height() -
268
- this.el.find('.title').height()
269
- );
266
+ // this.el.find('table').height(
267
+ // this.height() -
268
+ // this.el.find('h2').height()
269
+ // );
270
270
  }
271
271
 
272
272
  Grid.prototype.delete = function() {
273
273
  if (this.sub != undefined) {
274
- this.sub.close();
274
+ subs.unsubscribe(this.sub);
275
275
  }
276
276
  this.update = function() {};
277
277
  view.View.prototype.delete.call(this);
@@ -6,7 +6,9 @@
6
6
  this.title = json.title;
7
7
  this.clickFocusable = true;
8
8
  this.el.addClass("title");
9
- this.el.text(this.title);
9
+ this.h2 = $('<h2/>');
10
+ this.el.append(this.h2);
11
+ this.h2.text(this.title);
10
12
  this.reflow();
11
13
  }
12
14
 
@@ -28,6 +30,6 @@
28
30
  }
29
31
 
30
32
  Title.prototype.reflow = function() {
31
- this.el.quickfit(fitopts);
33
+ this.h2.quickfit(fitopts);
32
34
  }
33
35
  })();
@@ -1,4 +1,4 @@
1
1
  module Riemann; end
2
2
  class Riemann::Dash
3
- VERSION = '0.1.1'
3
+ VERSION = '0.2.0'
4
4
  end
@@ -8,7 +8,9 @@ $amber: #FFC712;
8
8
  $light-blue: #2CCCFE;
9
9
  $blue: #2C55FF;
10
10
  $dark-grey: #1F1F1F;
11
- $grey: #777;
11
+ $light-grey: #ccc;
12
+ $off-white: #dedede;
13
+ $grey: #888;
12
14
  $green: #06FA23;
13
15
  $yellow: #FEF206;
14
16
 
@@ -23,36 +25,35 @@ body {
23
25
  height: 100%;
24
26
  margin: 0;
25
27
  padding: 0;
26
- background: #000;
27
- color: #fff;
28
+ background: #fff;
29
+ color: $dark-grey;
28
30
  }
29
31
 
30
32
  html,table {
31
- font-family: Helvetica Nueue, Helvetica, sans;
32
- font-size: 12pt;
33
- }
34
- h1 {
35
- margin-bottom: 0.2em;
36
- }
37
- h2 {
38
- margin-top: 0;
39
- margin-bottom: 0.1em;
33
+ font-family: "HelveticaNeue", "Helvetica Neue", "HelveticaNeueRoman", "HelveticaNeue-Roman", "Helvetica Neue Roman", 'TeXGyreHerosRegular', "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif;
34
+ font-weight:400;
35
+ font-stretch:normal;
36
+ font-size: 14px;
40
37
  }
41
38
 
42
39
  #toolbar {
43
- width: 100%;
44
40
  height: 28px;
45
41
  overflow: hidden;
46
42
  position: absolute;
43
+ top: 3px;
44
+ left: 3px;
45
+ right: 3px;
47
46
  font-size: 12px;
48
47
 
49
48
  input {
50
- background: $dark-grey;
51
- color: #fff;
52
- border: 1px solid $grey;
49
+ background: $off-white;
50
+ border: 1px solid $dark-grey;
53
51
  margin: 3px;
54
52
  padding: 3px 6px;
55
53
  }
54
+ input:hover {
55
+ background: lighten($off-white, 5%);
56
+ }
56
57
 
57
58
  .server {
58
59
  float: right;
@@ -62,14 +63,15 @@ h2 {
62
63
  position: relative;
63
64
  margin: 3px;
64
65
  float: right;
65
- border: 1px solid $grey;
66
66
  width: 100px;
67
- padding: 3px 6px;
67
+ padding: 4px 6px 3px 6px;
68
+ border: 1px solid $dark-grey;
68
69
  span {
69
70
  z-index: -1;
70
71
  }
71
72
  .bar {
72
- background: rgba(255,255,255,0.25);
73
+ border-radius: 0;
74
+ background: rgba(0,0,0,0.25);
73
75
  width: 1%;
74
76
  }
75
77
  }
@@ -78,11 +80,13 @@ h2 {
78
80
  display: inline-block;
79
81
  margin: 3px;
80
82
  padding: 3px 6px;
81
- border: 1px solid $grey;
82
- color: #fff;
83
- background: $dark-grey;
83
+ border: 1px solid $light-grey;
84
+ background: $off-white;
84
85
  cursor: pointer;
85
86
  }
87
+ .button:hover {
88
+ background: lighten($off-white, 5%);
89
+ }
86
90
 
87
91
  .pager {
88
92
  margin: 0;
@@ -103,11 +107,11 @@ h2 {
103
107
  }
104
108
 
105
109
  li:hover {
106
- background: $grey;
110
+ background: lighten($off-white, 10%);
107
111
  }
108
112
 
109
113
  li.current {
110
- border-color: white;
114
+ border-color: $dark-grey;
111
115
 
112
116
  .delete {
113
117
  display: inline;
@@ -118,10 +122,11 @@ h2 {
118
122
  }
119
123
 
120
124
  #view {
121
- width: 100%;
122
125
  position: absolute;
126
+ left: 3px;
127
+ right: 3px;
123
128
  top: 28px;
124
- bottom: 0px;
129
+ bottom: 3px;
125
130
  }
126
131
 
127
132
  .box {
@@ -134,7 +139,7 @@ h2 {
134
139
  right: 0;
135
140
  margin: 3px;
136
141
  padding: 3px;
137
- background: $dark-grey;
142
+ background: $light-grey;
138
143
  border-radius: 3px;
139
144
  }
140
145
 
@@ -144,7 +149,8 @@ h2 {
144
149
  top: 0;
145
150
  bottom: 0;
146
151
  height: 100%;
147
- background: rgba(0, 0, 0, 0.3);
152
+ background: rgba(0, 0, 0, 0.2);
153
+ border-radius: 3px;
148
154
  }
149
155
 
150
156
  .state.ok, .bar.ok {
@@ -243,7 +249,7 @@ h2 {
243
249
  position: absolute;
244
250
  z-index: 1000;
245
251
  border-radius: 5px;
246
- background: rgba(255,251,180,0.5);
252
+ background: rgba(0,0,0,0.5);
247
253
  }
248
254
 
249
255
  #simplemodal-overlay {
@@ -269,15 +275,16 @@ h2 {
269
275
  cursor:pointer;
270
276
  }
271
277
 
272
-
273
278
  .quickfit {
274
279
  display: block;
275
280
  white-space: nowrap;
276
281
  width: 100%;
277
282
  }
278
283
 
279
- .title {
280
- text-align: right;
284
+ h2 {
285
+ display: block;
286
+ padding: 3px;
287
+ margin: 0;
281
288
  }
282
289
 
283
290
  .metric {
@@ -285,8 +292,16 @@ h2 {
285
292
  text-align: right;
286
293
  }
287
294
 
295
+ .title h2 {
296
+ position: absolute;
297
+ left: 3px;
298
+ right: 3px;
299
+ top: 3px;
300
+ bottom: 3px;
301
+ }
302
+
288
303
  .gauge {
289
- .title {
304
+ h2 {
290
305
  position: absolute;
291
306
  height: 25%;
292
307
  width: 75%;
@@ -306,46 +321,57 @@ h2 {
306
321
  }
307
322
 
308
323
  .grid {
309
- .title {
310
- height: 32px;
324
+ h2 {
325
+ height: 28px;
311
326
  font-size: 24px;
312
- text-align: right;
313
327
  }
314
328
 
315
- table {
329
+ .container {
316
330
  position: absolute;
317
- width: 100%;
318
- border-spacing: 3px;
331
+ top: 28px;
332
+ left: 0;
333
+ right: 0;
334
+ bottom: 0;
335
+ }
319
336
 
320
- tr > *:first-child {
321
- width: 1px;
322
- }
337
+ .bar {
338
+ position: static;
339
+ display: block;
340
+ height: 100%;
341
+ }
323
342
 
324
- th {
343
+ .box {
344
+ margin: 0;
345
+ padding: 0;
346
+ position: static;
347
+
348
+ .metric {
349
+ padding: 3px;
325
350
  font-weight: normal;
326
- vertical-align: top;
327
- text-align: right;
328
- padding: 0 3px;
329
351
  }
352
+ }
353
+
354
+ table {
355
+ font-size: 12px;
356
+ position: absolute;
357
+ width: 100%;
358
+ border-spacing: 3px;
330
359
 
331
360
  thead {
332
361
  margin: 0;
333
362
  padding: 0;
334
363
 
335
- tr {
336
- padding: 0;
337
- }
338
-
339
364
  th {
340
- margin: 0;
365
+ // Work around https://bugs.webkit.org/show_bug.cgi?id=20040
366
+ position: relative;
367
+ top: 3px;
341
368
  vertical-align: bottom;
369
+ text-align: right;
342
370
  }
343
371
  }
344
-
345
- .box {
346
- position: relative;
347
- vertical-align: top;
348
- text-align: right;
349
- }
372
+ }
373
+
374
+ tr > *:first-child {
375
+ width: 1px;
350
376
  }
351
377
  }
@@ -1,8 +1,202 @@
1
- <h2>Problems</h2>
2
- <div class="box"><%= state_list query('state != "ok"') %></div>
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Riemann</title>
6
+ <link rel="stylesheet" href="/css" type="text/css" />
7
+ <link rel="stylesheet" href="/toastr.css" type="text/css" />
8
+ </head>
9
+ <body>
10
+ <div id="toolbar"></div>
11
+ <div id="view"></div>
3
12
 
4
- <div class="box">
5
- <%= state_chart query('service = "cpu" or service = "memory" or service =~ "disk%" or service = "load"'), :title => "Health" %>
6
- </div>
13
+ <script type="text/javascript" src="/underscore-min.js"></script>
14
+ <script type="text/javascript" src="/jquery-1.7.2.min.js"></script>
15
+ <script type="text/javascript" src="/jquery-ui-1.9.0.custom.min.js"></script>
16
+ <script type="text/javascript" src="/jquery.json-2.2.min.js"></script>
17
+ <script type="text/javascript" src="/jquery.simplemodal.1.4.3.min.js"></script>
18
+ <script type="text/javascript" src="/toastr.js"></script>
19
+ <script type="text/javascript" src="/jquery.quickfit.js"></script>
20
+ <script type="text/javascript" src="/util.js"></script>
21
+ <script type="text/javascript" src="/mustache.js"></script>
22
+ <script src="http://d3js.org/d3.v2.js"></script>
23
+ <script type="text/javascript" src="/profile.js"></script>
24
+ <script type="text/javascript" src="/clock.js"></script>
25
+ <script type="text/javascript" src="/persistence.js"></script>
26
+ <script type="text/javascript" src="/keys.js"></script>
27
+ <script type="text/javascript" src="/subs.js"></script>
28
+ <script type="text/javascript" src="/format.js"></script>
29
+ <script type="text/javascript" src="/toolbar.js"></script>
30
+ <script type="text/javascript" src="/view.js"></script>
31
+ <script type="text/javascript" src="/views/title.js"></script>
32
+ <script type="text/javascript" src="/views/help.js"></script>
33
+ <script type="text/javascript" src="/views/gauge.js"></script>
34
+ <script type="text/javascript" src="/views/grid.js"></script>
35
+ <script type="text/javascript" src="/dash.js"></script>
7
36
 
8
- <div class="box"><%= state_chart query('true'), :title => "Everything" %></div>
37
+ <script type="text/javascript">
38
+ function logTable(container, query) {
39
+ var $container = $(container);
40
+ $container.addClass("log-table");
41
+ $container.append('<input type="text" class="query"></input>');
42
+ var prompt = $container.find('input');
43
+ $container.append('<div class="scroll"><table><thead><tr>' +
44
+ '<th>host</th>' +
45
+ '<th>service</th>' +
46
+ '<th>state</th>' +
47
+ '<th>metric</th>' +
48
+ '<th>description</th>' +
49
+ '</tr></thead><tbody></tbody></table></div>');
50
+ var scroll = $container.find('.scroll');
51
+ var log = $container.find('tbody');
52
+ var sub = null;
53
+ var tracking = true;
54
+
55
+ var template = "<tr><td>{{host}}</td><td>{{service}}</td><td>{{state}}</td><td>{{{metric}}}</td><td>{{description}}</td></tr>";
56
+
57
+ // Are we following the bottom of the log?
58
+ scroll.scroll(function(e) {
59
+ if (scroll.scrollTop() > (log.height() - scroll.height())) {
60
+ tracking = true;
61
+ } else {
62
+ tracking = false;
63
+ }
64
+ });
65
+
66
+ var scrollToBottom = function() {
67
+ scroll.stop().animate({
68
+ scrollTop: (log.height() - scroll.height() + 20)
69
+ }, 1000, "swing");
70
+ }
71
+
72
+ var atBottom = function() {
73
+ console.log(scroll.scrollTop());
74
+ console.log(log.height() - scroll.height());
75
+ return (scroll.scrollTop() > (log.height() - scroll.height()));
76
+ }
77
+
78
+ // Add an event
79
+ var append = function(e) {
80
+ $.extend(e, {
81
+ metric: format.metric(e)
82
+ });
83
+ log.append(Mustache.render(template, e));
84
+ if (tracking) { scrollToBottom() };
85
+ }
86
+
87
+ // Set up our query
88
+ var startQuery = function(query) {
89
+ // Cancel existing sub
90
+ if (sub != null) { sub.close(); }
91
+
92
+ // Subscribe
93
+ sub = subs.subscribe(query, function(e) {
94
+ e.time = d3.time.format.iso.parse(e.time);
95
+ clock.advance(e.time);
96
+ append(e);
97
+ });
98
+ }
99
+
100
+ // Initial subscription
101
+ if (query) { prompt[0].value = query; startQuery(query); }
102
+
103
+ // Prompt entry
104
+ prompt.change(function() { startQuery(this.value) });
105
+ }
106
+
107
+
108
+ function timeSeries(container) {
109
+ // Container
110
+ var $container = $(container);
111
+ var container = d3.select(container);
112
+ $container.append('<input type=\"text\" style="display: block"></input>');
113
+ var prompt = $container.children().last();
114
+
115
+ // Data structures
116
+ var data = [];
117
+ var n = 60;
118
+ var sub = null;
119
+
120
+ // Scale
121
+ var w = 20;
122
+ var h = 80;
123
+
124
+ var x = d3.time.scale()
125
+ .domain([0,1])
126
+ .range([0, w * n]);
127
+ var y = d3.scale.linear()
128
+ .domain([0,100])
129
+ .rangeRound([0,h]);
130
+
131
+ var updateTime = function(max) {
132
+ var min = d3.time.minute.offset(max, -1);
133
+ x.domain([min, max]);
134
+ }
135
+ updateTime(new Date());
136
+
137
+ // Chart itself
138
+ var chart = container.append("svg")
139
+ .attr("class", "timeSeries")
140
+ .attr("width", w * (n - 1))
141
+ .attr("height", h);
142
+
143
+ // Baseline
144
+ chart.append("line")
145
+ .attr("x1", 0)
146
+ .attr("x2", w * n)
147
+ .attr("y1", h - .5)
148
+ .attr("y2", h - .5)
149
+ .style("stroke", "#000");
150
+
151
+ // Subscribe
152
+ prompt.change(function() {
153
+ if (sub != null) { sub.close(); }
154
+
155
+ sub = subscribe(this.value, function(e) {
156
+ // Move time
157
+ e.time = d3.time.format.iso.parse(e.time);
158
+ clock.advance(e.time);
159
+
160
+ // Append to window
161
+ if (data.length >= n) {
162
+ data.shift();
163
+ }
164
+ data.push(e);
165
+
166
+ console.log(e.time, "maps to", x(e.time), "in", x.domain(), x.range());
167
+ });
168
+ });
169
+
170
+ var clockSub = clock.register(function(t) {
171
+ updateTime(t);
172
+
173
+ // Move events
174
+ var rect = chart.selectAll("rect")
175
+ .data(data, function(e) { if (e) { return e.time } });
176
+
177
+ rect.enter().insert("rect", "line")
178
+ .attr("x", function(e, i) {
179
+ return x(e.time) - .5; })
180
+ .attr("y", function(e) { return h - y(e.metric) - .5 })
181
+ .attr("width", w)
182
+ .attr("height", function(e) { return y(e.metric); })
183
+ .transition()
184
+ .duration(1000)
185
+ .attr("x", function(e, i) { return x(e.time) - .5 });
186
+
187
+ rect.transition()
188
+ .duration(1000)
189
+ .attr("x", function(e) { return x(e.time) - .5; });
190
+
191
+ rect.exit().transition()
192
+ .duration(1000)
193
+ .attr("x", function(e) { return x(e.time) - .5 })
194
+ .remove();
195
+ });
196
+
197
+ }
198
+
199
+ dash.reload();
200
+ </script>
201
+ </body>
202
+ </html>