riemann-dash 0.2.7 → 0.2.8

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 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