riemann-dash 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +31 -9
- data/README.markdown +18 -2
- data/example/config.rb +5 -0
- data/lib/riemann/dash.rb +1 -0
- data/lib/riemann/dash/app.rb +1 -0
- data/lib/riemann/dash/browser_config.rb +72 -0
- data/lib/riemann/dash/browser_config/file.rb +39 -0
- data/lib/riemann/dash/browser_config/s3.rb +30 -0
- data/lib/riemann/dash/config.rb +21 -41
- data/lib/riemann/dash/controller/index.rb +4 -4
- data/lib/riemann/dash/public/dash.js +4 -1
- data/lib/riemann/dash/public/eventPane.js +63 -0
- data/lib/riemann/dash/public/keys.js +1 -1
- data/lib/riemann/dash/public/strings.js +14 -2
- data/lib/riemann/dash/public/subs.js +13 -11
- data/lib/riemann/dash/public/util.js +41 -3
- data/lib/riemann/dash/public/view.js +56 -32
- data/lib/riemann/dash/public/views/flot.js +6 -1
- data/lib/riemann/dash/public/views/gauge.js +14 -1
- data/lib/riemann/dash/public/views/grid.js +155 -235
- data/lib/riemann/dash/public/views/help.js +1 -1
- data/lib/riemann/dash/public/views/list.js +73 -0
- data/lib/riemann/dash/public/views/log.js +3 -0
- data/lib/riemann/dash/version.rb +1 -1
- data/lib/riemann/dash/views/css.scss +60 -1
- data/lib/riemann/dash/views/index.erubis +3 -0
- data/riemann-dash.gemspec +2 -3
- metadata +15 -24
@@ -39,10 +39,22 @@ var strings = (function() {
|
|
39
39
|
return match[1];
|
40
40
|
}
|
41
41
|
return '';
|
42
|
-
}
|
42
|
+
}
|
43
|
+
|
44
|
+
// Shortens a list of strings by removing common prefixes.
|
45
|
+
var shorten = function(prefixFn, strings) {
|
46
|
+
var prefix = prefixFn(strings);
|
47
|
+
return _.map(strings, function(s) {
|
48
|
+
if (s && s.length !== prefix.length) {
|
49
|
+
return s.substring(prefix.length);
|
50
|
+
}
|
51
|
+
return s;
|
52
|
+
});
|
53
|
+
};
|
43
54
|
|
44
55
|
return {
|
45
56
|
commonPrefix: commonPrefix,
|
46
|
-
longestCommonPrefix: longestCommonPrefix
|
57
|
+
longestCommonPrefix: longestCommonPrefix,
|
58
|
+
shorten: shorten
|
47
59
|
}
|
48
60
|
})();
|
@@ -49,20 +49,22 @@ var subs = (function() {
|
|
49
49
|
return sub.open();
|
50
50
|
}
|
51
51
|
|
52
|
-
// Emit
|
52
|
+
// Emit expired events from the prioqueue.
|
53
53
|
var expire = function(sub, now) {
|
54
54
|
prioqueue = subs[sub.id].prioqueue;
|
55
|
-
if (prioqueue) {
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
sub.f(expired);
|
64
|
-
}
|
55
|
+
if (! prioqueue) {
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
|
59
|
+
while (bottom = prioqueue.bottomPriority()) {
|
60
|
+
expiry = new Date(bottom);
|
61
|
+
if (now < expiry) {
|
62
|
+
return;
|
65
63
|
}
|
64
|
+
expired = prioqueue.shift();
|
65
|
+
expired.time = expiry;
|
66
|
+
expired.state = 'expired';
|
67
|
+
sub.f(expired);
|
66
68
|
}
|
67
69
|
}
|
68
70
|
|
@@ -11,13 +11,51 @@ var util = (function() {
|
|
11
11
|
// A string key uniquely identifying an event by host and service.
|
12
12
|
eventKey: function(e) {
|
13
13
|
return nullableKey(e.host) + "\ufffe" + nullableKey(e.service);
|
14
|
-
},
|
15
|
-
|
14
|
+
},
|
15
|
+
|
16
|
+
// Takes a string and returns a function that extracts a value from an
|
17
|
+
// event.
|
18
|
+
extract_fn: function(str) {
|
19
|
+
// When null/undefined, stay null/undefined.
|
20
|
+
if (! str) {
|
21
|
+
return str;
|
22
|
+
}
|
23
|
+
|
24
|
+
// Probably the worst hack ever. I'm not documenting this because it's so
|
25
|
+
// evil--though tremendously useful.
|
26
|
+
if (str.match(/^fn /)) {
|
27
|
+
// Grab the rest of the string, turn it into an anonymous fn taking a
|
28
|
+
// single arg `e`.
|
29
|
+
return Function.apply(null, ['e', str.substring(3)]);
|
30
|
+
}
|
31
|
+
|
32
|
+
// Property access
|
33
|
+
return function(e) {
|
34
|
+
return e[str];
|
35
|
+
};
|
36
|
+
},
|
37
|
+
|
38
|
+
// Takes a string and returns either:
|
39
|
+
// - a function which extracts a maximum value from an event.
|
40
|
+
// - a number to be used as the constant maximum.
|
41
|
+
max_fn: function(str) {
|
42
|
+
if ((!str) || str === "all") {
|
43
|
+
// Always the same value: global maxima
|
44
|
+
return function(e) { return "all"; };
|
45
|
+
}
|
46
|
+
if (isNaN(parseFloat(str))) {
|
47
|
+
// Not a number. Extract a field.
|
48
|
+
return function(e) { return e[str]; };
|
49
|
+
}
|
50
|
+
// Return a constant number.
|
51
|
+
return parseFloat(str);
|
52
|
+
},
|
53
|
+
|
16
54
|
// Merge two maps nondestructively.
|
17
55
|
merge: function(m1, m2) {
|
18
56
|
return _.extend({}, m1, m2)
|
19
57
|
},
|
20
|
-
|
58
|
+
|
21
59
|
// Wraps a function in another, which calls f at most once every period
|
22
60
|
// milliseconds. Tries to minimize latency.
|
23
61
|
slur: function(period, f) {
|
@@ -3,7 +3,7 @@ var view = (function() {
|
|
3
3
|
var focused = null;
|
4
4
|
var focusOverlay = $('<div class="focusOverlay"></div>');
|
5
5
|
$('body').append(focusOverlay);
|
6
|
-
|
6
|
+
|
7
7
|
// Unfocus all views.
|
8
8
|
var unfocus = function() {
|
9
9
|
if (focused) {
|
@@ -19,8 +19,8 @@ var view = (function() {
|
|
19
19
|
}
|
20
20
|
|
21
21
|
function inherit(sup, sub) {
|
22
|
-
var newSubPrototype = createObject(sup.prototype);
|
23
|
-
newSubPrototype.constructor = sub;
|
22
|
+
var newSubPrototype = createObject(sup.prototype);
|
23
|
+
newSubPrototype.constructor = sub;
|
24
24
|
sub.prototype = newSubPrototype;
|
25
25
|
}
|
26
26
|
|
@@ -29,12 +29,12 @@ var view = (function() {
|
|
29
29
|
var t = types[json.type];
|
30
30
|
return(new t(json));
|
31
31
|
}
|
32
|
-
|
32
|
+
|
33
33
|
// Initialize keybindings
|
34
34
|
function setKeyBindings() {
|
35
35
|
var focusedBindings = {
|
36
36
|
// left
|
37
|
-
37: function(ev) {
|
37
|
+
37: function(ev) {
|
38
38
|
if (ev.ctrlKey === true) {
|
39
39
|
focused.split('HStack', -1);
|
40
40
|
} else {
|
@@ -49,8 +49,8 @@ var view = (function() {
|
|
49
49
|
} else {
|
50
50
|
focused.moveVertical(-1);
|
51
51
|
}
|
52
|
-
},
|
53
|
-
|
52
|
+
},
|
53
|
+
|
54
54
|
// right
|
55
55
|
39: function(ev) {
|
56
56
|
if (ev.ctrlKey === true) {
|
@@ -58,17 +58,17 @@ var view = (function() {
|
|
58
58
|
} else {
|
59
59
|
focused.moveHorizontal(1);
|
60
60
|
}
|
61
|
-
},
|
62
|
-
|
61
|
+
},
|
62
|
+
|
63
63
|
// down
|
64
|
-
40: function(ev) {
|
64
|
+
40: function(ev) {
|
65
65
|
if (ev.ctrlKey === true) {
|
66
66
|
focused.split('VStack', 1);
|
67
67
|
} else {
|
68
68
|
focused.moveVertical(1);
|
69
69
|
}
|
70
70
|
},
|
71
|
-
|
71
|
+
|
72
72
|
27: function() { focused.unfocus() }, // escape
|
73
73
|
33: function() { if (focused.parent) { // pgup
|
74
74
|
focused.parent.focus();
|
@@ -84,7 +84,7 @@ var view = (function() {
|
|
84
84
|
189: function() { focused.shrink(); }, // -
|
85
85
|
107: function() { focused.grow(); }, // + (NumPad)
|
86
86
|
109: function() { focused.shrink(); } // - (NumPad)
|
87
|
-
|
87
|
+
|
88
88
|
};
|
89
89
|
|
90
90
|
var bindings = {};
|
@@ -112,6 +112,8 @@ var view = (function() {
|
|
112
112
|
// View ////////////////////////////////////////////////////////////////////
|
113
113
|
|
114
114
|
var View = function(json) {
|
115
|
+
this.id = json.id || util.uniqueId();
|
116
|
+
this.version = json.version || 0;
|
115
117
|
this.type = json.type;
|
116
118
|
this.el = $('<div class="view">');
|
117
119
|
this.weight = json.weight || 1;
|
@@ -122,10 +124,13 @@ var view = (function() {
|
|
122
124
|
|
123
125
|
var self = this;
|
124
126
|
this.clickFocusable = true;
|
125
|
-
this.el.click(function() {
|
126
|
-
|
127
|
-
|
128
|
-
|
127
|
+
this.el.click(function(e) {
|
128
|
+
if (!e) var e = windows.event;
|
129
|
+
if (e.ctrlKey || e.metaKey) {
|
130
|
+
if (self.clickFocusable) {
|
131
|
+
self.focus();
|
132
|
+
}
|
133
|
+
}
|
129
134
|
});
|
130
135
|
};
|
131
136
|
types.View = View;
|
@@ -162,19 +167,28 @@ var view = (function() {
|
|
162
167
|
}
|
163
168
|
};
|
164
169
|
|
170
|
+
View.prototype.bumpVersion = function() {
|
171
|
+
this.version = (this.version + 1) || 1;
|
172
|
+
if (this.parent) {
|
173
|
+
this.parent.bumpVersion();
|
174
|
+
}
|
175
|
+
};
|
176
|
+
|
165
177
|
View.prototype.reflow = function() {
|
166
178
|
};
|
167
179
|
|
168
180
|
View.prototype.grow = function() {
|
169
181
|
this.weight *= 2;
|
170
|
-
this.parent.reflow();
|
182
|
+
this.parent.reflow();
|
171
183
|
focused.refocus();
|
184
|
+
this.bumpVersion();
|
172
185
|
};
|
173
186
|
|
174
187
|
View.prototype.shrink = function() {
|
175
188
|
this.weight *= 0.5;
|
176
189
|
this.parent.reflow();
|
177
190
|
focused.refocus();
|
191
|
+
this.bumpVersion();
|
178
192
|
};
|
179
193
|
|
180
194
|
// Replace this view with a different one. Returns replacement.
|
@@ -186,7 +200,7 @@ var view = (function() {
|
|
186
200
|
if (p == null) {
|
187
201
|
throw "Sorry, can't replace top-level views.";
|
188
202
|
}
|
189
|
-
|
203
|
+
|
190
204
|
if (p.replaceChild == null) {
|
191
205
|
throw "Sorry, can't replace unless parent can replace child.";
|
192
206
|
}
|
@@ -226,7 +240,7 @@ var view = (function() {
|
|
226
240
|
// new view before us) or +1 (place the new view after us).
|
227
241
|
View.prototype.splitParentStack = function(i) {
|
228
242
|
console.log("Split parent");
|
229
|
-
if (i === null) { i = 0 }
|
243
|
+
if (i === null) { i = 0 }
|
230
244
|
var index = this.parent.indexOf(this) - Math.min(i, 0);
|
231
245
|
console.log(this.parent.indexOf(this), index);
|
232
246
|
this.parent.insertChild(
|
@@ -251,7 +265,7 @@ var view = (function() {
|
|
251
265
|
weight: this.weight
|
252
266
|
});
|
253
267
|
parent.replaceChild(this, stack);
|
254
|
-
|
268
|
+
|
255
269
|
// Add self to stack
|
256
270
|
this.weight = 1;
|
257
271
|
if (i === -1) {
|
@@ -318,7 +332,7 @@ var view = (function() {
|
|
318
332
|
return null;
|
319
333
|
}
|
320
334
|
}
|
321
|
-
|
335
|
+
|
322
336
|
// Returns the nearest parent vstack
|
323
337
|
View.prototype.enclosingVStack = function() {
|
324
338
|
try {
|
@@ -363,6 +377,7 @@ var view = (function() {
|
|
363
377
|
neighbor.addChild(this);
|
364
378
|
neighbor.reflow();
|
365
379
|
this.focus();
|
380
|
+
this.bumpVersion();
|
366
381
|
return;
|
367
382
|
}
|
368
383
|
}
|
@@ -374,17 +389,18 @@ var view = (function() {
|
|
374
389
|
// left. Nothing *needs* to happen, so we return immediately.
|
375
390
|
return;
|
376
391
|
}
|
377
|
-
|
392
|
+
|
378
393
|
// We're moving to a new position inside the enclosing stack.
|
379
394
|
this.removeFromParent();
|
380
395
|
stack.insertChild(newI, this);
|
381
396
|
stack.reflow();
|
382
397
|
this.focus();
|
398
|
+
this.bumpVersion();
|
383
399
|
} else {
|
384
400
|
console.log("Sorry, not yet");
|
385
401
|
}
|
386
402
|
}
|
387
|
-
|
403
|
+
|
388
404
|
View.prototype.moveHorizontal = function(delta) {
|
389
405
|
this.move('enclosingHStack', delta);
|
390
406
|
}
|
@@ -406,7 +422,7 @@ var view = (function() {
|
|
406
422
|
'<button name="apply">Apply</button>' +
|
407
423
|
'</form></div>');
|
408
424
|
dialog.find('h1').text("Edit " + this.type);
|
409
|
-
|
425
|
+
|
410
426
|
// The serialized representation of a view that we're editing.
|
411
427
|
// Carried between various view types; when Apply is clicked, projected
|
412
428
|
// into an actual view.
|
@@ -446,7 +462,7 @@ var view = (function() {
|
|
446
462
|
});
|
447
463
|
|
448
464
|
// Apply button.
|
449
|
-
var me = this;
|
465
|
+
var me = this;
|
450
466
|
dialog.find('button[name=apply]').click(function(e) {
|
451
467
|
// Don't submit the form.
|
452
468
|
e.preventDefault();
|
@@ -457,7 +473,12 @@ var view = (function() {
|
|
457
473
|
// Replace view.
|
458
474
|
delete replacementJson.virtual;
|
459
475
|
replacement = me.replace(reify(replacementJson));
|
476
|
+
replacement.bumpVersion();
|
477
|
+
|
478
|
+
// Reflow
|
460
479
|
replacement.parent.reflow();
|
480
|
+
|
481
|
+
// Clean up view
|
461
482
|
me.delete();
|
462
483
|
replacement.focus();
|
463
484
|
|
@@ -467,7 +488,7 @@ var view = (function() {
|
|
467
488
|
|
468
489
|
// Show dialog.
|
469
490
|
keys.disable();
|
470
|
-
dialog.modal({onClose: function() {
|
491
|
+
dialog.modal({onClose: function() {
|
471
492
|
keys.enable();
|
472
493
|
$.modal.close();
|
473
494
|
}});
|
@@ -475,7 +496,10 @@ var view = (function() {
|
|
475
496
|
|
476
497
|
// Serialize this view to JSON.
|
477
498
|
View.prototype.json = function() {
|
478
|
-
return {type:
|
499
|
+
return {type: 'View',
|
500
|
+
weight: this.weight,
|
501
|
+
id: this.id,
|
502
|
+
version: this.version};
|
479
503
|
}
|
480
504
|
|
481
505
|
// Balloon /////////////////////////////////////////////////////////////////
|
@@ -486,7 +510,7 @@ var view = (function() {
|
|
486
510
|
this.clickFocusable = false;
|
487
511
|
this.el.detach();
|
488
512
|
this.el.appendTo(this.container);
|
489
|
-
|
513
|
+
|
490
514
|
this.child = reify(json.child);
|
491
515
|
this.child.parent = this;
|
492
516
|
this.el.append(this.child.el);
|
@@ -504,7 +528,7 @@ var view = (function() {
|
|
504
528
|
Balloon.prototype.replaceChild = function(v1, v2) {
|
505
529
|
this.child.parent = null;
|
506
530
|
this.child.el.detach();
|
507
|
-
|
531
|
+
|
508
532
|
this.child = v2;
|
509
533
|
v2.parent = this;
|
510
534
|
this.el.append(this.child.el);
|
@@ -668,13 +692,13 @@ var view = (function() {
|
|
668
692
|
};
|
669
693
|
inherit(Stack, VStack);
|
670
694
|
types.VStack = VStack;
|
671
|
-
|
695
|
+
|
672
696
|
VStack.prototype.json = function() {
|
673
697
|
return $.extend(Stack.prototype.json.call(this), {type: 'VStack'});
|
674
698
|
}
|
675
|
-
|
699
|
+
|
676
700
|
VStack.prototype.isVStack = true;
|
677
|
-
|
701
|
+
|
678
702
|
VStack.prototype.reflow = function() {
|
679
703
|
if (this.el === null) {
|
680
704
|
// We're gone.
|
@@ -5,6 +5,7 @@
|
|
5
5
|
view.View.call(this, json);
|
6
6
|
this.query = json.query;
|
7
7
|
this.title = json.title;
|
8
|
+
this.max = json.max || null;
|
8
9
|
this.graphType = json.graphType || 'line';
|
9
10
|
this.stackMode = json.stackMode || 'false';
|
10
11
|
this.lineWidth = json.lineWidth || 1;
|
@@ -94,6 +95,7 @@
|
|
94
95
|
yaxis: {
|
95
96
|
font: this.font,
|
96
97
|
min: 0,
|
98
|
+
max: this.max
|
97
99
|
},
|
98
100
|
xaxis: {
|
99
101
|
font: this.font,
|
@@ -239,6 +241,7 @@
|
|
239
241
|
type: 'Flot',
|
240
242
|
title: this.title,
|
241
243
|
query: this.query,
|
244
|
+
max: this.max,
|
242
245
|
timeRange: this.timeRange / 1000,
|
243
246
|
graphType: this.graphType,
|
244
247
|
stackMode: this.stackMode
|
@@ -262,7 +265,9 @@
|
|
262
265
|
'<label for="query">query</label>' +
|
263
266
|
'<textarea type="text" class="query" name="query">{{ query }}</textarea><br />' +
|
264
267
|
'<label for="timeRange">Time range (s)</label>' +
|
265
|
-
'<input type="text" name="timeRange" value="{{timeRange / 1000}}" />'
|
268
|
+
'<input type="text" name="timeRange" value="{{timeRange / 1000}}" />' +
|
269
|
+
'<label for="max">Max</label>' +
|
270
|
+
'<input type="text" name="max" value="{{max}}" />'
|
266
271
|
);
|
267
272
|
|
268
273
|
// Returns the edit form
|
@@ -2,11 +2,19 @@
|
|
2
2
|
var fitopts = {min: 6, max: 1000};
|
3
3
|
|
4
4
|
var Gauge = function(json) {
|
5
|
+
// Init
|
5
6
|
view.View.call(this, json);
|
7
|
+
this.clickFocusable = true;
|
8
|
+
|
9
|
+
// Config
|
6
10
|
this.query = json.query;
|
7
11
|
this.title = json.title;
|
8
12
|
this.commaSeparateThousands = json.commaSeparateThousands;
|
9
|
-
|
13
|
+
|
14
|
+
// State
|
15
|
+
this.currentEvent = null;
|
16
|
+
|
17
|
+
// HTML
|
10
18
|
this.el.addClass('gauge');
|
11
19
|
this.el.append(
|
12
20
|
'<div class="box">' +
|
@@ -18,11 +26,16 @@
|
|
18
26
|
this.box = this.el.find('.box');
|
19
27
|
this.el.find('h2').text(this.title);
|
20
28
|
|
29
|
+
// When clicked, display event
|
30
|
+
var self = this;
|
31
|
+
this.box.click(function() { eventPane.show(self.currentEvent) });
|
32
|
+
|
21
33
|
if (this.query) {
|
22
34
|
var reflowed = false;
|
23
35
|
var me = this;
|
24
36
|
var value = this.el.find('.value');
|
25
37
|
this.sub = subs.subscribe(this.query, function(e) {
|
38
|
+
self.currentEvent = e;
|
26
39
|
me.box.attr('class', 'box state ' + e.state);
|
27
40
|
value.text(format.float(e.metric, 2, me.commaSeparateThousands));
|
28
41
|
value.attr('title', e.description);
|