riemann-dash 0.2.8 → 0.2.9
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.
- 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);
|