riemann-dash 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>