riemann-dash 0.2.7 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/riemann/dash/public/clock.js +47 -14
- data/lib/riemann/dash/public/subs.js +15 -14
- data/lib/riemann/dash/public/views/grid.js +24 -14
- data/lib/riemann/dash/public/views/help.js +1 -0
- data/lib/riemann/dash/public/views/log.js +119 -0
- data/lib/riemann/dash/version.rb +1 -1
- data/lib/riemann/dash/views/css.scss +28 -1
- data/lib/riemann/dash/views/index.erubis +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 333646d7d876e408d47965ae5a0f27234e6b8821
|
4
|
+
data.tar.gz: 286df1ba3416046249397560a6572cff9b87974d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71467857647cd99ea04b87956f644897b61405e29e0b950a9dc67501e31128cc249113a27842544c61b385cd32f710da8b8ecf59f77815e91de775fd623a8af6
|
7
|
+
data.tar.gz: c903a2a28580057a35bb83c7dc5e2126012704b5995c7a19b2a10db0ba521634bb5198e6ce754161851668d26656cb84c178f7b4ec3f07cd01b77310f0c6135c
|
@@ -1,47 +1,80 @@
|
|
1
1
|
// Global clock
|
2
2
|
var clock = (function() {
|
3
|
+
// We keep track of two clocks here. The stream clock is the most recent time
|
4
|
+
// from the streaming system. The official clock is what time we *think* it
|
5
|
+
// is.
|
6
|
+
var stream_clock = 0;
|
3
7
|
var clock = new Date();
|
4
|
-
|
8
|
+
|
9
|
+
// If we *stop* receiving updates from the stream clock, we still want time
|
10
|
+
// to advance--but gradually, we want to reconverge on the local system
|
11
|
+
// clock. The last stream clock update is used to track how long it's been
|
12
|
+
// since we last advanced the stream clock.
|
13
|
+
var last_stream_clock_update = new Date(0);
|
14
|
+
|
15
|
+
// Over this many milliseconds, we want to converge on the current time.
|
16
|
+
var convergence_time = 60000;
|
17
|
+
|
18
|
+
// Callback_i is used to uniquely identify each callback so we can
|
19
|
+
// unsubscribe to clock updates.
|
5
20
|
var callback_i = 0;
|
21
|
+
|
22
|
+
// A map of callback indices to callback functions.
|
6
23
|
var callbacks = {};
|
7
24
|
|
25
|
+
// Advance the clock to a new time.
|
8
26
|
var advance = function(t) {
|
9
|
-
if (
|
10
|
-
//
|
11
|
-
|
12
|
-
//
|
13
|
-
|
14
|
-
clock = t;
|
15
|
-
// console.log("Clock advanced to", t / 1000);
|
27
|
+
if (stream_clock < t) {
|
28
|
+
// This is the highest value ever received for the stream clock.
|
29
|
+
stream_clock = t;
|
30
|
+
// console.log("New stream clock:", stream_clock);
|
31
|
+
last_stream_clock_update = new Date();
|
16
32
|
}
|
17
33
|
}
|
18
34
|
|
35
|
+
// Create a new subscription. Returns a subscription key used to unsubscribe.
|
19
36
|
var subscribe = function(f) {
|
20
37
|
callback_i = callback_i + 1;
|
21
38
|
callbacks[callback_i] = f;
|
22
39
|
return callback_i;
|
23
40
|
}
|
24
41
|
|
42
|
+
// Unsubscribes a given subscription, by ID.
|
25
43
|
var unsubscribe = function(i) {
|
26
44
|
delete callbacks[i];
|
27
45
|
}
|
28
46
|
|
29
47
|
// Automatically advance clock.
|
30
48
|
setInterval(function() {
|
31
|
-
|
32
|
-
|
33
|
-
|
49
|
+
// What time is the local system?
|
50
|
+
var now = new Date();
|
51
|
+
|
52
|
+
// What time does the stream clock think it is?
|
53
|
+
var stream_now = stream_clock + (now - last_stream_clock_update);
|
54
|
+
|
55
|
+
// What fraction of the convergence window has elapsed?
|
56
|
+
var convergence_fraction =
|
57
|
+
Math.min(1, (now - last_stream_clock_update) / convergence_time);
|
58
|
+
|
59
|
+
// console.log("Convergence:", convergence_fraction);
|
60
|
+
// console.log("Clock offset:", stream_now - now);
|
61
|
+
|
62
|
+
// The effective clock is the current stream time, plus the delta from the
|
63
|
+
// stream time to the local time, multiplied by the convergence fraction.
|
64
|
+
clock = stream_now + ((now - stream_now) * convergence_fraction);
|
65
|
+
|
34
66
|
$.each(callbacks, function(k, f) {
|
35
67
|
f(clock);
|
36
68
|
});
|
69
|
+
|
37
70
|
t2 = new Date();
|
38
|
-
subs.load1(
|
39
|
-
subs.load5(
|
71
|
+
subs.load1(now, t2);
|
72
|
+
subs.load5(now, t2);
|
40
73
|
}, 1000);
|
41
74
|
|
42
75
|
return {
|
43
76
|
'clock': clock,
|
44
|
-
'
|
77
|
+
'stream_clock': stream_clock,
|
45
78
|
'advance': advance,
|
46
79
|
'subscribe': subscribe,
|
47
80
|
'unsubscribe': unsubscribe
|
@@ -11,7 +11,7 @@ var subs = (function() {
|
|
11
11
|
|
12
12
|
// Subscriptions
|
13
13
|
var subs = {};
|
14
|
-
|
14
|
+
|
15
15
|
// Switch to turn on/off event processing
|
16
16
|
var active = true;
|
17
17
|
|
@@ -58,6 +58,7 @@ var subs = (function() {
|
|
58
58
|
expiry = new Date(bottom);
|
59
59
|
if (expiry < now) {
|
60
60
|
expired = prioqueue.shift();
|
61
|
+
expired.time = expiry;
|
61
62
|
expired.state = 'expired';
|
62
63
|
sub.f(expired);
|
63
64
|
}
|
@@ -77,9 +78,9 @@ var subs = (function() {
|
|
77
78
|
|
78
79
|
isOpen: function() {
|
79
80
|
if (server_type == "ws") {
|
80
|
-
return this.ws && (this.ws.readyState != EventSource.CLOSED)
|
81
|
-
} else {
|
82
81
|
return this.ws && (this.ws.readyState != WebSocket.CLOSED)
|
82
|
+
} else {
|
83
|
+
return this.ws && (this.ws.readyState != EventSource.CLOSED)
|
83
84
|
}
|
84
85
|
},
|
85
86
|
isClosed: function() { return !this.isOpen() },
|
@@ -98,7 +99,8 @@ var subs = (function() {
|
|
98
99
|
|
99
100
|
open: function() {
|
100
101
|
if (this.isOpen()) return this;
|
101
|
-
|
102
|
+
|
103
|
+
console.log("will open url: " + this.url());
|
102
104
|
|
103
105
|
var ws;
|
104
106
|
if (server_type == "sse") {
|
@@ -128,13 +130,17 @@ var subs = (function() {
|
|
128
130
|
var event = JSON.parse(e.data);
|
129
131
|
event.time = Date.parse(event.time);
|
130
132
|
clock.advance(event.time);
|
131
|
-
|
133
|
+
|
134
|
+
// Update local index.
|
135
|
+
if (event.state !== "expired") { // only expired events have no TTL
|
136
|
+
// TODO: get a prioqueue supporting delete so we can delete expired
|
137
|
+
// events.
|
132
138
|
this.prioqueue.update(
|
133
139
|
{host: event.host, service: event.service},
|
134
|
-
event.time + (event.ttl * 1000) // convert TTL to ms
|
140
|
+
event.time + ((event.ttl || 60) * 1000) // convert TTL to ms
|
135
141
|
);
|
142
|
+
this.f(event);
|
136
143
|
}
|
137
|
-
this.f(event);
|
138
144
|
}
|
139
145
|
var t2 = Date.now();
|
140
146
|
load1(t1, t2);
|
@@ -142,7 +148,6 @@ var subs = (function() {
|
|
142
148
|
}, this);
|
143
149
|
|
144
150
|
return this;
|
145
|
-
|
146
151
|
},
|
147
152
|
|
148
153
|
close: function() {
|
@@ -176,9 +181,6 @@ var subs = (function() {
|
|
176
181
|
// Done here.
|
177
182
|
return;
|
178
183
|
}
|
179
|
-
|
180
|
-
// Display reconnection notice
|
181
|
-
toastr.warning(_.size(closed) + " lost connections—check the server field above.");
|
182
184
|
|
183
185
|
// Reopen
|
184
186
|
_.each(closed, function(sub) {
|
@@ -190,9 +192,8 @@ var subs = (function() {
|
|
190
192
|
if (errorQueue.length == 0) {
|
191
193
|
return;
|
192
194
|
}
|
193
|
-
toastr.warning(errorQueue.length + " socket errors");
|
195
|
+
toastr.warning(errorQueue.length + " socket errors; check the server field above.");
|
194
196
|
errorQueue.length = 0;
|
195
|
-
converge();
|
196
197
|
}
|
197
198
|
|
198
199
|
// Periodically notify of errors.
|
@@ -216,7 +217,7 @@ var subs = (function() {
|
|
216
217
|
disable: function() { active = false; console.log("Subs disabled."); },
|
217
218
|
toggle: function() {
|
218
219
|
active = ! active;
|
219
|
-
if (active) {
|
220
|
+
if (active) {
|
220
221
|
console.log("Subs enabled.");
|
221
222
|
} else {
|
222
223
|
console.log("Subs disabled.");
|
@@ -135,7 +135,7 @@
|
|
135
135
|
// Use fn to group maxima
|
136
136
|
var max_key = this.max_fn(event);
|
137
137
|
return this.maxima[this.max_fn(event)] || -1/0;
|
138
|
-
}
|
138
|
+
}
|
139
139
|
};
|
140
140
|
|
141
141
|
// Generates a td cell for an event. Returns a map of the td, bar, and metric
|
@@ -170,6 +170,8 @@
|
|
170
170
|
// Metric
|
171
171
|
if (event.metric != undefined) {
|
172
172
|
e.metric.text(format.float(event.metric));
|
173
|
+
} else if (event.state != undefined) {
|
174
|
+
e.metric.text(event.state);
|
173
175
|
}
|
174
176
|
|
175
177
|
// Bar chart
|
@@ -200,8 +202,8 @@
|
|
200
202
|
this.elCache[rowKey] = {};
|
201
203
|
}
|
202
204
|
this.elCache[rowKey][colKey] = cache;
|
203
|
-
|
204
|
-
// Update table.
|
205
|
+
|
206
|
+
// Update table.
|
205
207
|
var table = this.el.find('table');
|
206
208
|
var rowIndex = this.rows.indexOf(rowKey);
|
207
209
|
var columnIndex = this.cols.indexOf(colKey);
|
@@ -230,7 +232,7 @@
|
|
230
232
|
res = s.substring(fieldLen);
|
231
233
|
}
|
232
234
|
return res;
|
233
|
-
};
|
235
|
+
};
|
234
236
|
|
235
237
|
var shortColNames = _.map(this.cols, _.partial(shortener, colPrefixLen));
|
236
238
|
var shortRowNames = _.map(this.rows, _.partial(shortener, rowPrefixLen));
|
@@ -254,12 +256,12 @@
|
|
254
256
|
if (!cache) {
|
255
257
|
// Not cached; generate and render.
|
256
258
|
cache = this.newTdCache();
|
257
|
-
|
259
|
+
|
258
260
|
// Cache element
|
259
261
|
if (!this.elCache[name]) {
|
260
262
|
this.elCache[name] = {};
|
261
263
|
}
|
262
|
-
this.elCache[name][subName] = cache;
|
264
|
+
this.elCache[name][subName] = cache;
|
263
265
|
|
264
266
|
// Render element
|
265
267
|
this.renderElement(cache, event);
|
@@ -343,6 +345,7 @@
|
|
343
345
|
|
344
346
|
// Add an event.
|
345
347
|
Grid.prototype.add = function(e) {
|
348
|
+
console.log("Adding", e);
|
346
349
|
var newEvent = this.saveEvent(e);
|
347
350
|
var newMax = this.updateMax(e);
|
348
351
|
|
@@ -357,20 +360,22 @@
|
|
357
360
|
Grid.prototype.remove = function(e) {
|
358
361
|
var row_key = this.row_fn(e);
|
359
362
|
var col_key = this.col_fn(e);
|
360
|
-
|
363
|
+
|
364
|
+
// Remove from events table.
|
365
|
+
if (this.events[row_key]) {
|
361
366
|
delete this.events[row_key][col_key];
|
367
|
+
if (_.isEmpty(this.events[row_key])) {
|
368
|
+
delete this.events[row_key];
|
369
|
+
}
|
362
370
|
}
|
363
371
|
|
364
372
|
// Wipe element cache
|
365
373
|
if (this.elCache[row_key]) {
|
366
374
|
delete this.elCache[row_key][col_key];
|
367
|
-
if (_.isEmpty(this.
|
368
|
-
delete this.
|
375
|
+
if (_.isEmpty(this.elCache[row_key])) {
|
376
|
+
delete this.elCache[row_key];
|
369
377
|
}
|
370
378
|
}
|
371
|
-
if (_.isEmpty(this.elCache[row_key])) {
|
372
|
-
delete this.events[row_key];
|
373
|
-
}
|
374
379
|
|
375
380
|
// Recompute rows
|
376
381
|
this.rows = _.keys(this.events).sort();
|
@@ -387,20 +392,25 @@
|
|
387
392
|
this.updateMax();
|
388
393
|
this.render();
|
389
394
|
};
|
390
|
-
|
395
|
+
|
391
396
|
// Accept an event.
|
392
397
|
Grid.prototype.update = function(e) {
|
398
|
+
console.log("Update", e);
|
393
399
|
if (e.state === "expired") {
|
394
400
|
this.remove(e);
|
395
401
|
} else {
|
396
402
|
this.add(e);
|
397
403
|
}
|
404
|
+
console.log("elCache", this.elCache);
|
405
|
+
console.log("events", this.events);
|
406
|
+
console.log("rows", this.rows);
|
407
|
+
console.log("cols", this.cols);
|
398
408
|
};
|
399
409
|
|
400
410
|
Grid.prototype.reflow = function() {
|
401
411
|
// this.el.find('table').height(
|
402
412
|
// this.height() -
|
403
|
-
// this.el.find('h2').height()
|
413
|
+
// this.el.find('h2').height()
|
404
414
|
// );
|
405
415
|
};
|
406
416
|
|
@@ -5,6 +5,7 @@
|
|
5
5
|
this.el.addClass("help");
|
6
6
|
this.el.append('<div class="box">' +
|
7
7
|
"<p>Welcome to Riemann-Dash.</p>" +
|
8
|
+
"<p>Need a refresher on the query language? See the <a href=\"https://github.com/aphyr/riemann/blob/master/test/riemann/query_test.clj\">query tests</a> for examples, or read the <a href=\"https://github.com/aphyr/riemann/blob/master/src/riemann/Query.g\">spec</a>.</p>" +
|
8
9
|
"<p>Click to select a view. Escape unfocuses. Use the arrow keys to move a view. Use Control+arrow to <i>split</i> a view in the given direction.</p>" +
|
9
10
|
"<p>To edit a view, hit e. Use enter, or click 'apply', to apply your changes. Escape cancels.</p>" +
|
10
11
|
"<p>To save your changes to the server, press s. You can refresh the page, or press r to reload.</p>" +
|
@@ -0,0 +1,119 @@
|
|
1
|
+
(function() {
|
2
|
+
// Reify a log view from JSON
|
3
|
+
var LogView = function(json) {
|
4
|
+
// Extract state from JSON
|
5
|
+
view.View.call(this, json);
|
6
|
+
this.query = json.query;
|
7
|
+
this.title = json.title;
|
8
|
+
this.lines = json.lines || 1000;
|
9
|
+
|
10
|
+
var self = this;
|
11
|
+
|
12
|
+
// Set up HTML
|
13
|
+
this.el.addClass('log');
|
14
|
+
this.el.append('<h2></h2>' +
|
15
|
+
'<div class="scroll"><table><thead><tr>' +
|
16
|
+
'<th>host</th>' +
|
17
|
+
'<th>service</th>' +
|
18
|
+
'<th>state</th>' +
|
19
|
+
'<th>metric</th>' +
|
20
|
+
'<th>description</th>' +
|
21
|
+
'</tr></thead><tbody></tbody></table></div>');
|
22
|
+
this.el.find('h2').text(this.title);
|
23
|
+
this.scroll = this.el.find('.scroll');
|
24
|
+
this.log = this.el.find('tbody');
|
25
|
+
|
26
|
+
// Line template
|
27
|
+
this.lineTemplate = _.template("<tr><td>{{host}}</td><td>{{service}}</td><td>{{state}}</td><td>{{metric}}</td><td>{{description}}</td></tr>");
|
28
|
+
|
29
|
+
// When scrolling occurs, toggle tracking state.
|
30
|
+
this.scroll.scroll(function(e) {
|
31
|
+
if (self.scroll.scrollTop() >
|
32
|
+
(self.log.height() - self.scroll.height())) {
|
33
|
+
tracking = true;
|
34
|
+
} else {
|
35
|
+
tracking = false;
|
36
|
+
}
|
37
|
+
});
|
38
|
+
|
39
|
+
// Are we currently following the bottom of the log?
|
40
|
+
this.tracking = true;
|
41
|
+
|
42
|
+
// This view can be clicked to focus on it.
|
43
|
+
this.clickFocusable = true;
|
44
|
+
|
45
|
+
if (this.query) {
|
46
|
+
// Subscribe to our query
|
47
|
+
this.sub = subs.subscribe(
|
48
|
+
this.query,
|
49
|
+
function(e) {
|
50
|
+
self.update(e);
|
51
|
+
}
|
52
|
+
);
|
53
|
+
}
|
54
|
+
};
|
55
|
+
|
56
|
+
// Set up our LogView class and register it with the view system
|
57
|
+
view.inherit(view.View, LogView);
|
58
|
+
view.Log = LogView;
|
59
|
+
view.types.Log = LogView;
|
60
|
+
|
61
|
+
// Scroll to bottom of log.
|
62
|
+
LogView.prototype.scrollToBottom = function() {
|
63
|
+
this.scroll.stop().animate({
|
64
|
+
scrollTop: (this.log.height() - this.scroll.height() + 20)
|
65
|
+
}, 1000, "swing");
|
66
|
+
};
|
67
|
+
|
68
|
+
// Are we at the bottom of the log?
|
69
|
+
LogView.prototype.atBottom = function() {
|
70
|
+
console.log(this.scroll.scrollTop());
|
71
|
+
console.log(this.log.height() - this.scroll.height());
|
72
|
+
return (this.scroll.scrollTop() >
|
73
|
+
(this.log.height() - this.scroll.height()));
|
74
|
+
};
|
75
|
+
|
76
|
+
// Accept events from a subscription and update the log.
|
77
|
+
LogView.prototype.update = function(event) {
|
78
|
+
this.log.append(this.lineTemplate(event));
|
79
|
+
if (this.tracking) { this.scrollToBottom(); };
|
80
|
+
};
|
81
|
+
|
82
|
+
// Serialize current state to JSON
|
83
|
+
LogView.prototype.json = function() {
|
84
|
+
return $.extend(view.View.prototype.json.call(this), {
|
85
|
+
type: 'Log',
|
86
|
+
title: this.title,
|
87
|
+
query: this.query,
|
88
|
+
lines: this.lines
|
89
|
+
});
|
90
|
+
};
|
91
|
+
|
92
|
+
// HTML template used to edit this view
|
93
|
+
var editTemplate = _.template(
|
94
|
+
'<label for="title">title</label>' +
|
95
|
+
'<input type="text" name="title" value="{{title}}" /><br />' +
|
96
|
+
'<label for="query">query</label>' +
|
97
|
+
'<textarea type="text" class="query" name="query">{{ query }}</textarea><br />' +
|
98
|
+
'<label for="lines">Maximum lines (number of events)</label>' +
|
99
|
+
'<input type="text" name="lines" value="{{lines}}" />'
|
100
|
+
);
|
101
|
+
|
102
|
+
// Returns the edit form
|
103
|
+
LogView.prototype.editForm = function() {
|
104
|
+
return editTemplate(this);
|
105
|
+
};
|
106
|
+
|
107
|
+
// Called when our parent needs to resize us
|
108
|
+
LogView.prototype.reflow = function() {
|
109
|
+
};
|
110
|
+
|
111
|
+
// When the view is deleted, remove our subscription
|
112
|
+
LogView.prototype.delete = function() {
|
113
|
+
if (this.sub) {
|
114
|
+
subs.unsubscribe(this.sub);
|
115
|
+
}
|
116
|
+
view.View.prototype.delete.call(this);
|
117
|
+
};
|
118
|
+
})();
|
119
|
+
|
data/lib/riemann/dash/version.rb
CHANGED
@@ -442,7 +442,7 @@ h2 {
|
|
442
442
|
color: #000;
|
443
443
|
opacity: 1;
|
444
444
|
}
|
445
|
-
|
445
|
+
|
446
446
|
h2 {
|
447
447
|
height: 28px;
|
448
448
|
font-size: 24px;
|
@@ -456,3 +456,30 @@ h2 {
|
|
456
456
|
bottom: 0;
|
457
457
|
}
|
458
458
|
}
|
459
|
+
|
460
|
+
.log.view {
|
461
|
+
.scroll {
|
462
|
+
overflow-x: hidden;
|
463
|
+
overflow-y: hidden;
|
464
|
+
position: absolute;
|
465
|
+
top: 28px;
|
466
|
+
left: 0;
|
467
|
+
right: 0;
|
468
|
+
bottom: 0;
|
469
|
+
}
|
470
|
+
|
471
|
+
table {
|
472
|
+
width: 100%;
|
473
|
+
border-collapse: collapse;
|
474
|
+
}
|
475
|
+
|
476
|
+
thead {
|
477
|
+
text-align: left;
|
478
|
+
}
|
479
|
+
|
480
|
+
td, th {
|
481
|
+
padding: 2px;
|
482
|
+
vertical-align: top;
|
483
|
+
border-bottom: 1px solid #eee;
|
484
|
+
}
|
485
|
+
}
|
@@ -47,6 +47,7 @@
|
|
47
47
|
<script src="views/timeseries.js"></script>
|
48
48
|
<script src="views/flot.js"></script>
|
49
49
|
<script src="views/title.js"></script>
|
50
|
+
<script src="views/log.js"></script>
|
50
51
|
<script src="views/help.js"></script>
|
51
52
|
<script src="views/gauge.js"></script>
|
52
53
|
<script src="views/grid.js"></script>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: riemann-dash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kyle Kingsbury
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: riemann-client
|
@@ -172,6 +172,7 @@ files:
|
|
172
172
|
- lib/riemann/dash/public/views/gauge.js
|
173
173
|
- lib/riemann/dash/public/views/grid.js
|
174
174
|
- lib/riemann/dash/public/views/help.js
|
175
|
+
- lib/riemann/dash/public/views/log.js
|
175
176
|
- lib/riemann/dash/public/views/timeseries.js
|
176
177
|
- lib/riemann/dash/public/views/title.js
|
177
178
|
- lib/riemann/dash/public/x.png
|