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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4c289eed32e6170e10dbe98f328ad9eef4be086
4
- data.tar.gz: 72850ab6107b78532fe7c786aa42d27ec3e3e765
3
+ metadata.gz: 333646d7d876e408d47965ae5a0f27234e6b8821
4
+ data.tar.gz: 286df1ba3416046249397560a6572cff9b87974d
5
5
  SHA512:
6
- metadata.gz: 58bfdfb8cbaff5681efc68a5c3836c47e849b17b9ff80e1895f9e19b31c9364690ae942afee15bde2b8107eec2d2de7326554dd048f4c6165441d897447f79fd
7
- data.tar.gz: 04db465f234ea5d5e55003d719e8c2026393e6da7a5556b4c0acbff432be56a778102f3177c43546b4d6ad72956b3695e37563936d31759969545d9325d27c11
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
- var offset = 0;
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 (clock < t) {
10
- // New local offset
11
- offset = t - (new Date());
12
- // console.log("new offset is", offset);
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
- t1 = new Date();
32
- offset = offset * 0.99;
33
- advance(new Date(t1.valueOf() + offset));
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(t1, t2);
39
- subs.load5(t1, t2);
71
+ subs.load1(now, t2);
72
+ subs.load5(now, t2);
40
73
  }, 1000);
41
74
 
42
75
  return {
43
76
  'clock': clock,
44
- 'offset': offset,
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
- console.log("will open url: " + this.url());
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
- if (event.ttl > 0) { // only expired events have no TTL
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&mdash;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
- if (this.events[row_key] && this.events[row_key][col_key]) {
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.events[row_key])) {
368
- delete this.events[row_key];
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
+
@@ -1,4 +1,4 @@
1
1
  module Riemann; end
2
2
  module Riemann::Dash
3
- VERSION = '0.2.7'
3
+ VERSION = '0.2.8'
4
4
  end
@@ -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.7
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-01-03 00:00:00.000000000 Z
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