fnordmetric 0.7.5 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. data/doc/V1.0-ROADMAP +97 -0
  2. data/doc/full_example.rb +95 -511
  3. data/doc/legacy_example.rb +640 -0
  4. data/doc/minimal_example.rb +26 -0
  5. data/doc/preview3.png +0 -0
  6. data/fnordmetric.gemspec +3 -2
  7. data/lib/fnordmetric/acceptors/acceptor.rb +29 -0
  8. data/lib/fnordmetric/{inbound_stream.rb → acceptors/tcp_acceptor.rb} +8 -5
  9. data/lib/fnordmetric/{inbound_datagram.rb → acceptors/udp_acceptor.rb} +9 -8
  10. data/lib/fnordmetric/api.rb +2 -2
  11. data/lib/fnordmetric/context.rb +37 -18
  12. data/lib/fnordmetric/defaults.rb +9 -0
  13. data/lib/fnordmetric/ext.rb +72 -0
  14. data/lib/fnordmetric/gauge.rb +37 -10
  15. data/lib/fnordmetric/gauge_calculations.rb +38 -16
  16. data/lib/fnordmetric/gauge_modifiers.rb +67 -0
  17. data/lib/fnordmetric/gauge_rendering.rb +40 -0
  18. data/lib/fnordmetric/gauge_validations.rb +15 -0
  19. data/lib/fnordmetric/gauges/distribution_gauge.rb +85 -0
  20. data/lib/fnordmetric/gauges/timeseries_gauge.rb +143 -0
  21. data/lib/fnordmetric/gauges/toplist_gauge.rb +44 -0
  22. data/lib/fnordmetric/histogram.rb +57 -0
  23. data/lib/fnordmetric/logger.rb +42 -36
  24. data/lib/fnordmetric/namespace.rb +47 -23
  25. data/lib/fnordmetric/session.rb +6 -6
  26. data/lib/fnordmetric/standalone.rb +15 -35
  27. data/lib/fnordmetric/timeseries.rb +79 -0
  28. data/lib/fnordmetric/toplist.rb +61 -0
  29. data/lib/fnordmetric/version.rb +1 -1
  30. data/lib/fnordmetric/web/app.rb +122 -0
  31. data/lib/fnordmetric/web/app_helpers.rb +42 -0
  32. data/lib/fnordmetric/{dashboard.rb → web/dashboard.rb} +4 -0
  33. data/lib/fnordmetric/{event.rb → web/event.rb} +7 -2
  34. data/lib/fnordmetric/web/reactor.rb +87 -0
  35. data/lib/fnordmetric/web/web.rb +53 -0
  36. data/lib/fnordmetric/web/websocket.rb +38 -0
  37. data/lib/fnordmetric/widgets/bars_widget.rb +44 -0
  38. data/lib/fnordmetric/{html_widget.rb → widgets/html_widget.rb} +0 -0
  39. data/lib/fnordmetric/widgets/numbers_widget.rb +56 -0
  40. data/lib/fnordmetric/{pie_widget.rb → widgets/pie_widget.rb} +0 -0
  41. data/lib/fnordmetric/widgets/timeseries_widget.rb +55 -0
  42. data/lib/fnordmetric/widgets/toplist_widget.rb +64 -0
  43. data/lib/fnordmetric/worker.rb +26 -25
  44. data/lib/fnordmetric.rb +85 -115
  45. data/readme.md +362 -0
  46. data/spec/gauge_like_shared.rb +54 -0
  47. data/spec/gauge_spec.rb +2 -36
  48. data/spec/namespace_spec.rb +25 -11
  49. data/spec/spec_helper.rb +4 -0
  50. data/spec/{inbound_stream_spec.rb → tcp_acceptor_spec.rb} +3 -3
  51. data/spec/timeseries_gauge_spec.rb +54 -0
  52. data/spec/{inbound_datagram_spec.rb → udp_acceptor_spec.rb} +3 -3
  53. data/web/fnordmetric.css +786 -0
  54. data/web/haml/app.haml +38 -0
  55. data/web/haml/distribution_gauge.haml +118 -0
  56. data/web/haml/timeseries_gauge.haml +80 -0
  57. data/web/haml/toplist_gauge.haml +194 -0
  58. data/web/img/head.png +0 -0
  59. data/web/img/list.png +0 -0
  60. data/web/img/list_active.png +0 -0
  61. data/web/img/list_hover.png +0 -0
  62. data/web/img/loader_white.gif +0 -0
  63. data/web/img/navbar.png +0 -0
  64. data/web/img/navbar_btn.png +0 -0
  65. data/web/img/picto_gauge.png +0 -0
  66. data/web/js/fnordmetric.bars_widget.js +178 -0
  67. data/web/js/fnordmetric.dashboard_view.js +99 -0
  68. data/web/js/fnordmetric.gauge_view.js +260 -0
  69. data/web/js/fnordmetric.html_widget.js +21 -0
  70. data/web/js/fnordmetric.js +255 -0
  71. data/web/js/fnordmetric.numbers_widget.js +121 -0
  72. data/web/js/fnordmetric.overview_view.js +35 -0
  73. data/web/js/fnordmetric.pie_widget.js +118 -0
  74. data/web/js/fnordmetric.realtime_timeline_widget.js +175 -0
  75. data/web/js/fnordmetric.session_view.js +343 -0
  76. data/web/js/fnordmetric.timeline_widget.js +333 -0
  77. data/web/js/fnordmetric.timeseries_widget.js +388 -0
  78. data/web/js/fnordmetric.toplist_widget.js +112 -0
  79. data/web/js/fnordmetric.ui.js +91 -0
  80. data/web/js/fnordmetric.util.js +244 -0
  81. data/{pub → web}/loader.gif +0 -0
  82. data/web/vendor/d3.v2.js +9382 -0
  83. data/web/vendor/font-awesome/css/font-awesome.css +239 -0
  84. data/web/vendor/font-awesome/font/fontawesome-webfont.eot +0 -0
  85. data/web/vendor/font-awesome/font/fontawesome-webfont.svg +175 -0
  86. data/web/vendor/font-awesome/font/fontawesome-webfont.svgz +0 -0
  87. data/web/vendor/font-awesome/font/fontawesome-webfont.ttf +0 -0
  88. data/web/vendor/font-awesome/font/fontawesome-webfont.woff +0 -0
  89. data/web/vendor/jquery-1.6.2.min.js +18 -0
  90. data/web/vendor/jquery-ui.min.js +413 -0
  91. data/web/vendor/jquery.maskedinput.js +252 -0
  92. data/web/vendor/rickshaw.css +286 -0
  93. data/web/vendor/rickshaw.fnordmetric.js +2676 -0
  94. metadata +129 -79
  95. data/Gemfile +0 -6
  96. data/README.md +0 -404
  97. data/Rakefile +0 -6
  98. data/doc/version +0 -1
  99. data/haml/app.haml +0 -79
  100. data/haml/widget.haml +0 -9
  101. data/lib/fnordmetric/app.rb +0 -163
  102. data/lib/fnordmetric/average_metric.rb +0 -7
  103. data/lib/fnordmetric/bars_widget.rb +0 -26
  104. data/lib/fnordmetric/combine_metric.rb +0 -7
  105. data/lib/fnordmetric/count_metric.rb +0 -13
  106. data/lib/fnordmetric/funnel_widget.rb +0 -2
  107. data/lib/fnordmetric/metric.rb +0 -80
  108. data/lib/fnordmetric/metric_api.rb +0 -37
  109. data/lib/fnordmetric/numbers_widget.rb +0 -26
  110. data/lib/fnordmetric/report.rb +0 -29
  111. data/lib/fnordmetric/sum_metric.rb +0 -13
  112. data/lib/fnordmetric/timeline_widget.rb +0 -30
  113. data/lib/fnordmetric/toplist_widget.rb +0 -25
  114. data/pub/fnordmetric.css +0 -145
  115. data/pub/fnordmetric.js +0 -1179
  116. data/pub/vendor/highcharts.js +0 -170
  117. data/pub/vendor/jquery-1.6.1.min.js +0 -18
data/pub/fnordmetric.js DELETED
@@ -1,1179 +0,0 @@
1
- var FnordMetric = (function(){
2
-
3
- var canvasElem = false;
4
-
5
- var currentNamespace = false;
6
- var currentView = false;
7
- var currentWidgetUID=23;
8
-
9
- function decPrint(val){
10
- return (val < 10 ? '0'+val : val);
11
- }
12
-
13
- function formatTimeOfDay(_time){
14
- if(_time === null) {
15
- return "";
16
- } else {
17
- var time = new Date();
18
- time.setTime(_time*1000);
19
- return decPrint(time.getHours()) + ':' +
20
- decPrint(time.getMinutes()) + ':' +
21
- decPrint(time.getSeconds());
22
- }
23
- }
24
-
25
- function formatTimeRange(range){
26
- if (range < 60){
27
- return parseInt(range) + ' sec';
28
- } else if(range<3600){
29
- return parseInt(range/60) + ' min';
30
- } else if(range==3600){
31
- return '1 hour';
32
- } else if(range<(3600*24)){
33
- return parseInt(range/3600) + ' hours';
34
- } else if(range==(3600*24)){
35
- return '1 day';
36
- } else {
37
- return parseInt(range/(3600*24)) + ' days';
38
- }
39
- }
40
-
41
- function formatTimeSince(time){
42
- var now = new Date().getTime()/1000;
43
- var since = now - time;
44
- return formatTimeRange(since);
45
- }
46
-
47
- function formatOffset(offset, next_offset){
48
- if((offset == 0) && (next_offset==(3600*24))){
49
- return 'today';
50
- } if((offset == 0) && (next_offset==3600)){
51
- return 'this hour';
52
- } else if(offset == 0){
53
- return 'last ' + formatTimeRange(next_offset||0);
54
- } else if(offset==(3600*24)){
55
- return 'yesterday';
56
- } else if(offset==3600){
57
- return 'last hour';
58
- } else {
59
- return formatTimeRange(offset) + ' ago';
60
- }
61
- }
62
-
63
- function formatValue(value){
64
- if(value < 10){
65
- return value.toFixed(2);
66
- } else if(value > 1000){
67
- return (value/1000.0).toFixed(1) + "k";
68
- } else {
69
- return value.toFixed(0);
70
- }
71
- }
72
-
73
- function formatTimeValue(value){
74
- if (value < 60){
75
- return parseFloat(value).toFixed(1) + 's';
76
- } else if(value<3600){
77
- return parseFloat(value/60).toFixed(1) + 'm';
78
- } else if(value<(3600*24)){
79
- return parseFloat(value/3600).toFixed(1) + 'h';
80
- } else {
81
- return parseFloat(value/(3600*24)).toFixed(1) + 'd';
82
- }
83
- }
84
-
85
- function formatPercentValue(value){
86
- return value + '%';
87
- }
88
-
89
- function formatGaugeValue(gauge_key, value){
90
- if(gauge_key.slice(0,8) === '__time__'){
91
- return formatTimeValue(value);
92
- } else if(gauge_key.slice(0,11) === '__percent__'){
93
- return formatPercentValue(value);
94
- } else {
95
- return formatValue(value);
96
- }
97
- }
98
-
99
- function getNextWidgetUID(){
100
- return (currentWidgetUID += 1);
101
- }
102
-
103
- var toplistWidget = function(){
104
-
105
- function render(opts){
106
-
107
- var current_gauge = false;
108
-
109
- var headbar = $('<div class="headbar"></div>').append(
110
- $('<h2></h2>').html(opts.title)
111
- );
112
-
113
- opts.elem.append(headbar).css({
114
- 'marginBottom': 20,
115
- 'overflow': 'hidden'
116
- }).append(
117
- $('<div class="toplist_inner"></div>')
118
- );
119
-
120
- var first = true;
121
- for(k in opts.gauges){
122
- headbar.append(
123
- $('<div></div>')
124
- .attr('class', 'button mr')
125
- .attr('rel', k)
126
- .append(
127
- $('<span></span>').html(opts.gauges[k].title)
128
- ).click(function(){
129
- loadGauge($(this).attr('rel'));
130
- }
131
- )
132
- );
133
- if(first){
134
- first = false;
135
- loadGauge(k);
136
- }
137
- }
138
-
139
- if(opts.autoupdate){
140
- var secs = parseInt(opts.autoupdate);
141
- if(secs > 0){
142
-
143
- var autoupdate_interval = window.setInterval(function(){
144
- loadGauge(false, true);
145
- }, secs*1000);
146
-
147
- $('body').bind('fm_dashboard_close', function(){
148
- window.clearInterval(autoupdate_interval);
149
- });
150
- }
151
- };
152
-
153
- function loadGauge(gkey, silent){
154
- if(!gkey){ gkey = current_gauge; }
155
- current_gauge = gkey;
156
- if(!silent){ $('.toplist_inner', opts.elem).addClass('loading'); }
157
- var _url = FnordMetric.p + '/' + currentNamespace + '/gauge/' + gkey;
158
- $.get(_url, function(_resp){
159
- var resp = JSON.parse(_resp);
160
- renderGauge(gkey, resp);
161
- })
162
- }
163
-
164
- function renderGauge(gkey, gdata){
165
- var _elem = $('.toplist_inner', opts.elem).removeClass('loading').html('');
166
- $(gdata.values).each(function(n, _gd){
167
- var _perc = (parseInt(gdata.values[n][1]) / parseFloat(gdata.count))*100;
168
- var _item = $('<div class="toplist_item"><div class="title"></div><div class="value"></div><div class="percent"></div></div>');
169
- $('.title', _item).html(gdata.values[n][0]);
170
- $('.value', _item).html(formatGaugeValue(gkey, parseInt(gdata.values[n][1])));
171
- $('.percent', _item).html(_perc.toFixed(1) + '%');
172
- _elem.append(_item);
173
- });
174
- }
175
-
176
- }
177
-
178
-
179
- return {
180
- render: render
181
- };
182
-
183
- };
184
-
185
- var htmlWidget = function(){
186
- function render(opts){
187
-
188
- opts.elem.append(
189
- $('<div class="headbar small"></div>').html(opts.title)
190
- ).css({
191
- 'marginBottom': 20,
192
- 'overflow': 'hidden'
193
- });
194
-
195
- var container = $('<div></div>')
196
- .addClass('html_container')
197
- .html(opts.html);
198
-
199
- opts.elem.append(container);
200
- }
201
-
202
- function updateValues(opts){}
203
- function updatedisplay(opts, diff_factor){}
204
-
205
- return {
206
- render: render
207
- };
208
- }
209
-
210
- var numbersWidget = function(){
211
-
212
-
213
- function render(opts){
214
-
215
- opts.elem.append(
216
- $('<div class="headbar small"></div>').html(opts.title)
217
- ).css({
218
- 'marginBottom': 20,
219
- 'overflow': 'hidden'
220
- });
221
-
222
- for(k in opts.gauges){
223
- var gtick = parseInt(opts.gauges[k].tick);
224
- var gtitle = opts.gauges[k].title;
225
-
226
- var container = $('<div></div>')
227
- .addClass('numbers_container')
228
- .addClass('size_'+opts.offsets.length)
229
- .attr('rel', k)
230
- .append(
231
- $('<div></div>')
232
- .addClass('title')
233
- .html(gtitle)
234
- );
235
-
236
-
237
- $(opts.offsets).each(function(n, offset){
238
- var _off, _nextoff, _sum;
239
- if (offset[0]=="s"){
240
- _off = 0;
241
- _sum = _nextoff = (gtick * parseInt(offset.slice(1)));
242
- } else {
243
- _sum = 0;
244
- _off = offset*gtick;
245
- _nextoff = gtick;
246
- }
247
- container.append(
248
- $('<div></div>')
249
- .addClass('number')
250
- .attr('rel', k)
251
- .attr('data-offset', _off)
252
- .attr('data-sum', _sum)
253
- .attr('data',0)
254
- .append(
255
- $('<span></span>').addClass('desc').html(formatOffset(_off, _nextoff))
256
- )
257
- .append(
258
- $('<span></span>').addClass('value').html(0)
259
- )
260
- );
261
- });
262
-
263
- opts.elem.append(container);
264
- }
265
-
266
- if(opts.autoupdate){
267
- var secs = parseInt(opts.autoupdate);
268
- if(secs > 0){
269
-
270
- var autoupdate_interval = window.setInterval(function(){
271
- updateValues(opts);
272
- }, secs*1000);
273
-
274
- $('body').bind('fm_dashboard_close', function(){
275
- window.clearInterval(autoupdate_interval);
276
- });
277
-
278
- }
279
-
280
- };
281
-
282
- updateValues(opts);
283
-
284
- }
285
-
286
- function updateValues(opts){
287
- var values = $('.number', $(opts.elem));
288
- var values_pending = values.length;
289
- values.each(function(){
290
- var _sum = parseInt($(this).attr('data-sum'));
291
- var num = this;
292
- var at = parseInt(new Date().getTime()/1000);
293
- var url = FnordMetric.p + '/' + currentNamespace + '/gauge/' + $(this).attr('rel');
294
- if(_sum > 0){
295
- url += '?at='+(at-_sum)+'-'+at+'&sum=true';
296
- } else {
297
- at -= parseInt($(this).attr('data-offset'));
298
- url += '?at='+at;
299
- }
300
-
301
- $.get(url, function(_resp){
302
- var resp = JSON.parse(_resp);
303
- for(_k in resp){
304
- $(num).attr('data', (resp[_k]||0));
305
- }
306
- if((values_pending -= 1)==0){
307
- updateDisplay(opts, 4);
308
- }
309
- });
310
- });
311
- }
312
-
313
- function updateDisplay(opts, diff_factor){
314
- var still_running = false;
315
- $('.number', $(opts.elem)).each(function(){
316
- var target_val = parseFloat($(this).attr('data'));
317
- var current_val = parseFloat($(this).attr('data-current'));
318
- if(!current_val){ current_val=0; }
319
- var diff = (target_val-current_val)/diff_factor;
320
- if(diff < 1){ diff=1; }
321
- if(target_val > current_val){
322
- still_running = true;
323
- var new_val = current_val+diff;
324
- if(new_val > target_val){ new_val = target_val; }
325
- $(this).attr('data-current', new_val);
326
- $('.value', this).html(formatGaugeValue($(this).attr('rel'), new_val));
327
- }
328
- });
329
- if(still_running){
330
- (function(df){
331
- window.setTimeout(function(){ updateDisplay(opts, df); }, 30);
332
- })(diff_factor);
333
- }
334
- }
335
-
336
- return {
337
- render: render
338
- };
339
-
340
- };
341
-
342
- var timelineWidget = function(){
343
-
344
- function render(opts){
345
-
346
- var widget_uid = getNextWidgetUID();
347
- var chart=false;
348
- var max_y=0;
349
-
350
- function redrawWithRange(first_time, silent){
351
- if(!silent){ $(opts.elem).css('opacity', 0.5); }
352
- redrawDatepicker();
353
- var _query = '?at='+opts.start_timestamp+'-'+opts.end_timestamp;
354
- //chart.series = [];
355
- max_y=0;
356
- //metrics_completed = 0;
357
- $(opts.gauges).each(function(i,gauge){
358
- $.ajax({
359
- url: FnordMetric.p + '/' + currentNamespace +'/gauge/'+gauge+_query,
360
- success: redrawGauge(first_time, gauge)
361
- });
362
- });
363
- }
364
-
365
- function redrawGauge(first_time, gauge){
366
- return (function(json){
367
- var raw_data = JSON.parse(json);
368
- var series_data = [];
369
-
370
- for(p in raw_data){
371
- series_data.push([parseInt(p)*1000, raw_data[p]||0]);
372
- max_y = Math.max(max_y, raw_data[p]);
373
- }
374
-
375
- if(!first_time){
376
- chart.get('series-'+gauge).setData(series_data);
377
- } else {
378
- chart.addSeries({
379
- name: opts.gauge_titles[gauge],
380
- data: series_data,
381
- id: 'series-'+gauge
382
- });
383
- }
384
-
385
- chart.yAxis[0].setExtremes(0,max_y);
386
- chart.redraw();
387
-
388
- // shown on the *first* gauge load
389
- $(opts.elem).css('opacity', 1);
390
- });
391
- }
392
-
393
- function redrawDatepicker(){
394
- $('.datepicker', opts.elem).html(
395
- Highcharts.dateFormat('%d.%m.%y %H:%M', parseInt(opts.start_timestamp)*1000) +
396
- '&nbsp;&dash;&nbsp;' +
397
- Highcharts.dateFormat('%d.%m.%y %H:%M', parseInt(opts.end_timestamp)*1000)
398
- );
399
- }
400
-
401
- function moveRange(direction){
402
- v = opts.tick*direction*8;
403
- opts.start_timestamp += v;
404
- opts.end_timestamp += v;
405
- redrawWithRange();
406
- }
407
-
408
- function drawLayout(){
409
- $(opts.elem).append( $('<div></div>').attr('class', 'headbar').append(
410
- $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('refresh')).click(
411
- function(){ redrawWithRange(); }
412
- )
413
- ).append(
414
- $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('&rarr;')).click(
415
- function(){ moveRange(1); }
416
- )
417
- ).append(
418
- $('<div></div>').attr('class', 'datepicker')
419
- ).append(
420
- $('<div></div>').attr('class', 'button').append($('<span></span>').html('&larr;')).click(
421
- function(){ moveRange(-1); }
422
- )
423
- ).append(
424
- $('<h2></h2>').html(opts.title)
425
- ) ).append(
426
- $('<div></div>').attr('id', 'container-'+widget_uid).css({
427
- height: 256,
428
- marginBottom: 20,
429
- overflow: 'hidden'
430
- })
431
- );
432
- }
433
-
434
- function drawChart(){
435
- chart = new Highcharts.Chart({
436
- chart: {
437
- renderTo: 'container-'+widget_uid,
438
- defaultSeriesType: opts.plot_style,
439
- height: 270
440
- },
441
- series: [],
442
- title: { text: '' },
443
- xAxis: {
444
- type: 'datetime',
445
- tickInterval: opts.tick * 1000,
446
- title: (opts.x_title||''),
447
- labels: { step: 2 }
448
- },
449
- yAxis: {
450
- title: (opts.y_title||''),
451
- min: 0,
452
- max: 1000
453
- },
454
- legend: {
455
- layout: 'horizontal',
456
- align: 'top',
457
- verticalAlign: 'top',
458
- x: -5,
459
- y: -3,
460
- margin: 25,
461
- borderWidth: 0
462
- },
463
- plotOptions: {
464
- line: {
465
- shadow: false,
466
- lineWidth: 3
467
- }
468
- }
469
- });
470
- }
471
-
472
- drawLayout();
473
- drawChart();
474
-
475
- redrawWithRange(true);
476
-
477
- if(opts.autoupdate){
478
- var secs = parseInt(opts.autoupdate);
479
- if(secs > 0){
480
-
481
- var autoupdate_interval = window.setInterval(function(){
482
- if(
483
- (parseInt(new Date().getTime()/1000) - opts.end_timestamp) >
484
- (opts.include_current ? 0 : opts.tick)
485
- ){
486
- opts.end_timestamp += opts.tick;
487
- opts.start_timestamp += opts.tick;
488
- }
489
-
490
- redrawWithRange(false, true);
491
- }, secs*1000);
492
-
493
- $('body').bind('fm_dashboard_close', function(){
494
- window.clearInterval(autoupdate_interval);
495
- });
496
-
497
- }
498
- };
499
-
500
- }
501
-
502
- return {
503
- render: render
504
- };
505
-
506
- };
507
-
508
-
509
- var barsWidget = function(){
510
-
511
- function render(opts){
512
-
513
- var widget_uid = getNextWidgetUID();
514
- var chart=false;
515
- var max_y=0;
516
-
517
- function redraw(first_time, silent){
518
- if(!silent){ $(opts.elem).css('opacity', 0.5); }
519
- max_y=0;
520
- $(opts.gauges).each(function(i,gauge){
521
- $.ajax({
522
- url: FnordMetric.p + '/' + currentNamespace +'/gauge/'+gauge,
523
- success: redrawGauge(first_time, gauge)
524
- });
525
- });
526
- }
527
-
528
- function redrawGauge(first_time, gauge){
529
- return (function(json){
530
- var raw_data = JSON.parse(json);
531
- var series_data = [];
532
- var series_type;
533
- var label_data = [];
534
-
535
- if(opts.plot_style == 'horizontal'){
536
- series_type = 'bar';
537
- } else {
538
- series_type = 'column';
539
- }
540
-
541
- if(opts.order_by == 'field'){
542
- raw_data.values.sort(function(a,b){
543
- if(a[0] == b[0]){
544
- return 0;
545
- }else if(a[0] > b[0]){
546
- return 1;
547
- } else {
548
- return -1;
549
- }
550
- });
551
- }
552
-
553
- for(p in raw_data.values){
554
- label_data.push(raw_data.values[p][0]||'?');
555
- series_data.push(parseInt(raw_data.values[p][1]||0));
556
- //max_y = Math.max(max_y, raw_data[p]);
557
- }
558
-
559
- chart = new Highcharts.Chart({
560
- chart: {
561
- renderTo: 'container-'+widget_uid,
562
- defaultSeriesType: series_type,
563
- height: 270
564
- },
565
- title: { text: '' },
566
- xAxis: {
567
- categories: label_data
568
- },
569
- yAxis: {
570
- title: { text: '' }
571
- },
572
- legend: {
573
- layout: 'horizontal',
574
- align: 'top',
575
- verticalAlign: 'top',
576
- x: -5,
577
- y: -3,
578
- margin: 25,
579
- borderWidth: 0
580
- },
581
- plotOptions: {
582
- column: {
583
- animation: first_time,
584
- }
585
- },
586
- series: [
587
- {
588
- name: opts.gauge_titles[gauge],
589
- data: series_data
590
- }
591
- ]
592
- });
593
-
594
- //chart.redraw();
595
- $(opts.elem).css('opacity', 1);
596
- });
597
- }
598
-
599
- function drawLayout(){
600
- $(opts.elem).append( $('<div></div>').attr('class', 'headbar').append(
601
- $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('refresh')).click(
602
- function(){ redraw(); }
603
- )
604
- ).append(
605
- $('<h2></h2>').html(opts.title)
606
- ) ).append(
607
- $('<div></div>').attr('id', 'container-'+widget_uid).css({
608
- height: 256,
609
- marginBottom: 20,
610
- overflow: 'hidden'
611
- })
612
- );
613
- }
614
-
615
- drawLayout();
616
- redraw(true);
617
-
618
- if(opts.autoupdate){
619
- var autoupdate_interval = window.setInterval(function(){
620
- redraw(false, true);
621
- }, opts.autoupdate*1000);
622
-
623
- $('body').bind('fm_dashboard_close', function(){
624
- window.clearInterval(autoupdate_interval);
625
- });
626
- }
627
-
628
- }
629
-
630
- return {
631
- render: render
632
- };
633
-
634
- };
635
-
636
- var pieWidget = function(){
637
-
638
- function render(opts){
639
-
640
- var widget_uid = getNextWidgetUID();
641
- var chart=false;
642
-
643
- function redraw(first_time, silent){
644
- if(!silent){ $(opts.elem).css('opacity', 0.5); }
645
- var gauge_values = {};
646
- var gauges_left = opts.gauges.length;
647
- var at = parseInt(new Date().getTime()/1000);
648
- $(opts.gauges).each(function(i,gauge){
649
- $.ajax({
650
- url: FnordMetric.p + '/' + currentNamespace+'/gauge/'+gauge+'?at='+at,
651
- success: function(_resp){
652
- var resp = JSON.parse(_resp);
653
- gauges_left -= 1;
654
- for(_tk in resp){
655
- gauge_values[gauge] = parseInt(resp[_tk]||0);
656
- }
657
- if(gauges_left==0){
658
- redrawChart(first_time, gauge_values);
659
- }
660
- }
661
- });
662
- });
663
- }
664
-
665
- function redrawChart(first_time, gauge_values){
666
-
667
- var series_data = [];
668
-
669
- for(_gkey in gauge_values){
670
- series_data.push([
671
- opts.gauge_titles[_gkey],
672
- gauge_values[_gkey]
673
- ]);
674
- }
675
-
676
- chart = new Highcharts.Chart({
677
- chart: {
678
- renderTo: 'container-'+widget_uid,
679
- defaultSeriesType: 'pie',
680
- height: 270,
681
- spacingTop: 5,
682
- spacingBottom: 30
683
- },
684
- credits: {
685
- enabled: false
686
- },
687
- title: { text: '' },
688
- legend: {
689
- layout: 'horizontal',
690
- align: 'top',
691
- verticalAlign: 'top',
692
- margin: 25,
693
- borderWidth: 0
694
- },
695
- tooltip: {
696
- formatter: function() {
697
- return '<b>'+ this.point.name +'</b>: '+ this.y + ' (' + this.percentage.toFixed(1) + '%)';
698
- }
699
- },
700
- plotOptions: {
701
- pie: {
702
- animation: first_time,
703
- dataLabels: {
704
- formatter: function() {
705
- return '<b>'+ this.point.name +'</b><br />'+ this.percentage.toFixed(1) +' %';
706
- }
707
- }
708
- }
709
- },
710
- series: [
711
- {
712
- name: opts.title,
713
- data: series_data
714
- }
715
- ]
716
- });
717
-
718
- //chart.redraw();
719
- $(opts.elem).css('opacity', 1);
720
- }
721
-
722
- function drawLayout(){
723
- $(opts.elem).append( $('<div></div>').attr('class', 'headbar small').append(
724
- $('<span></span>').html(opts.title)
725
- ) ).append(
726
- $('<div></div>').attr('id', 'container-'+widget_uid).css({
727
- height: 270
728
- })
729
- );
730
- }
731
-
732
- drawLayout();
733
- redraw(true);
734
-
735
- if(opts.autoupdate){
736
- var autoupdate_interval = window.setInterval(function(){
737
- redraw(false, true);
738
- }, opts.autoupdate*1000);
739
-
740
- $('body').bind('fm_dashboard_close', function(){
741
- window.clearInterval(autoupdate_interval);
742
- });
743
- }
744
-
745
- }
746
-
747
- return {
748
- render: render
749
- };
750
-
751
- };
752
-
753
-
754
- var sessionView = (function(){
755
-
756
- var listElem = $('<ul class="session_list"></ul>');
757
- var feedInnerElem = $('<ul class="feed_inner"></ul>');
758
- var typeListElem = $('<ul class="event_type_list"></ul>');
759
- var filterElem = $('<div class="events_sidebar"></div>').html(
760
- $('<div class="headbar small"></div>').html('Event Types')
761
- ).append(typeListElem);
762
- var feedElem = $('<div class="sessions_feed"></div>').html(
763
- $('<div class="headbar small"></div>').html('Event Feed')
764
- ).append(feedInnerElem);
765
- var sideElem = $('<div class="sessions_sidebar"></div>').html(
766
- $('<div class="headbar small"></div>').html('Active Users')
767
- ).append(listElem);
768
-
769
- var eventsPolledUntil = false;
770
- var eventsFilter = {uncheckedTypes: [], checkedSessions: []};
771
- var sessionData = {};
772
- var pollRunning = true;
773
-
774
- function load(elem){
775
- eventsPolledUntil = parseInt(new Date().getTime()/10000);
776
- elem.html('')
777
- .append(filterElem)
778
- .append(feedElem)
779
- .append(sideElem);
780
- startPoll();
781
- loadEventTypes();
782
- };
783
-
784
- function resize(_width, _height){
785
- $('.sessions_feed').width(_width-452);
786
- };
787
-
788
- function startPoll(){
789
- (doSessionPoll())();
790
- (doEventsPoll())();
791
- sessionView.session_poll = window.setInterval(doSessionPoll(), 1000);
792
- };
793
-
794
- function stopPoll(){
795
- pollRunning = false;
796
- window.clearInterval(sessionView.session_poll);
797
- }
798
-
799
- function doSessionPoll(){
800
- return (function(){
801
- $.ajax({
802
- url: FnordMetric.p + '/' + currentNamespace+'/sessions',
803
- success: callbackSessionPoll()
804
- });
805
- });
806
- };
807
-
808
- function loadEventHistory(params){
809
- feedInnerElem.html('');
810
- $.ajax({
811
- url: FnordMetric.p + '/' + currentNamespace+'/events',
812
- data: params,
813
- success: function(_data, _status){
814
- var data = JSON.parse(_data).events;
815
- for(var n=data.length; n >= 0; n--){
816
- if(data[n]){ renderEvent(data[n]); }
817
- }
818
- }
819
- });
820
- }
821
-
822
- function callbackSessionPoll(){
823
- return (function(_data, _status){
824
- $.each(JSON.parse(_data).sessions, function(i,v){
825
- updateSession(v);
826
- });
827
- sortSessions();
828
- });
829
- };
830
-
831
- function loadEventTypes(){
832
- $.ajax({
833
- url: FnordMetric.p + '/' + currentNamespace+'/event_types',
834
- success: function(_data){
835
- var data = JSON.parse(_data);
836
- $(data.types).each(function(i,v){
837
- if((v.length > 0) && (v.slice(0,5)!='_set_')){
838
- addEventType(v,v);
839
- }
840
- });
841
- }
842
- });
843
- };
844
-
845
- function setCheckboxesCheckedState(types_state, sessions_state) {
846
- $('.event_type_list .event_type input').attr('checked', types_state);
847
- $('.session_list .session input').attr('checked', sessions_state);
848
- }
849
-
850
- function addEventType(type, display){
851
- typeListElem.append(
852
- $('<li class="event_type"></li>').append(
853
- $('<span class="history"></span>').html('history')
854
- .click(function(){
855
- setCheckboxesCheckedState(true, true);
856
- $('input', $(this).parent()).attr('checked', true);
857
- updateEventFilter(); loadEventHistory({type: type});
858
- })
859
- ).append(
860
- $('<input type="checkbox" />').attr('checked', true)
861
- .click(function(){ updateEventFilter(); })
862
- ).append(
863
- $('<span></span>').html(display)
864
- ).attr('rel', type)
865
- );
866
- }
867
-
868
- function updateEventFilter(){
869
- var _unchecked_types = [];
870
- $('ul.event_type_list li.event_type').each(function(i,v){
871
- if(!$('input', v).attr('checked')){
872
- _unchecked_types.push($(v).attr('rel'));
873
- }
874
- });
875
- eventsFilter.uncheckedTypes = _unchecked_types;
876
-
877
- var _checked_sessions = [];
878
- $('ul.session_list li.session').each(function(i,v){
879
- if($('input', v).attr('checked')){
880
- _checked_sessions.push($(v).data().session);
881
- }
882
- });
883
- eventsFilter.checkedSessions = _checked_sessions;
884
- }
885
-
886
- function doEventsPoll(){
887
- return (function(){
888
- $.ajax({
889
- url: FnordMetric.p + '/' + currentNamespace+'/events?since='+eventsPolledUntil,
890
- success: callbackEventsPoll()
891
- });
892
- });
893
- };
894
-
895
- function callbackEventsPoll(){
896
- return (function(_data, _status){
897
- var data = JSON.parse(_data)
898
- var events = data.events;
899
- var timeout = 1000;
900
- var maxevents = 200;
901
- var passesFiltering = function(event_data) {
902
- var passes_type_filtering = false;
903
- var passes_session_filtering = false;
904
- if(eventsFilter.uncheckedTypes.indexOf(event_data._type) == -1) {
905
- if(parseInt(v._time)<=eventsPolledUntil) {
906
- passes_type_filtering = true;
907
- }
908
- }
909
- if(!passes_type_filtering) return false;
910
-
911
- if(eventsFilter.checkedSessions.length == 0){
912
- return true; // No filter set - show all events
913
- } else {
914
- if(event_data._session_key){
915
- if(eventsFilter.checkedSessions.indexOf(event_data._session_key) >= 0){
916
- return true; // Filter set and match
917
- } else {
918
- return false; // Filter set but no match
919
- }
920
- } else {
921
- return false; // Filter set but event is not associated with session
922
- }
923
- }
924
- }
925
-
926
- if(events.length > 0){
927
- eventsPolledUntil = parseInt(events[0]._time)-1;
928
- }
929
- for(var n=events.length-1; n >= 0; n--){
930
- var v = events[n];
931
- if(passesFiltering(v)) {
932
- renderEvent(v);
933
- }
934
- };
935
- var elems = $("p", feedInnerElem);
936
- for(var n=maxevents; n < elems.length; n++){
937
- $(elems[n]).remove();
938
- }
939
- if(pollRunning){
940
- window.setTimeout(doEventsPoll(), timeout);
941
- }
942
- });
943
- };
944
-
945
- function updateSession(session_data){
946
- var session_key = session_data.session_key;
947
- if(!sessionData[session_key]){
948
- updateEventFilter()
949
- }
950
- sessionData[session_key] = session_data;
951
- renderSession(session_data);
952
- }
953
-
954
- function sortSessions(){
955
- console.log("fixme: sort and splice to 100");
956
- }
957
-
958
- function renderSession(session_data){
959
-
960
- var session_name = session_data["_name"];
961
- var session_time = formatTimeSince(session_data["_updated_at"]);
962
- var session_elem = $('li[data-session='+session_data["session_key"]+']:first');
963
-
964
- if(session_elem.length>0){
965
-
966
- if(session_data["_picture"] && (session_data["_picture"].length > 1)){
967
- $('.picture img', session_elem).attr('src', session_data["_picture"])
968
- }
969
-
970
- if(session_name){
971
- $('.name', session_elem).html(session_name);
972
- }
973
-
974
- $('.time', session_elem).html(session_time);
975
-
976
- } else {
977
-
978
- var session_picture = $('<img width="25" />');
979
-
980
- if(!session_name){
981
- session_name = session_data["session_key"].substr(0,15)
982
- };
983
-
984
- if(session_data["_picture"]){
985
- session_picture.attr('src', session_data["_picture"]);
986
- };
987
-
988
- listElem.append(
989
- $('<li class="session"></li>').append(
990
- $('<div class="picture"></div>').html(session_picture)
991
- ).append(
992
- $('<span class="name"></span>').html(session_name)
993
- ).append(
994
- $('<span class="time"></span>').html(session_time)
995
- ).append(
996
- $('<span class="history"></span>').html('history')
997
- .click(function(){
998
- setCheckboxesCheckedState(true, false);
999
- updateEventFilter();
1000
- loadEventHistory({session_key: session_data["session_key"]});
1001
- })
1002
- ).attr('data-session', session_data["session_key"])
1003
- );
1004
-
1005
- }
1006
- };
1007
-
1008
- function renderEvent(event_data){
1009
- var event_time = $('<span class="time"></span>');
1010
- var event_message = $('<span class="message"></span>');
1011
- var event_props = $('<span class="properties"></span>');
1012
- var event_picture = $('<div class="picture"></picture>');
1013
-
1014
- var event_type = event_data._type;
1015
-
1016
- if(!event_type){ return true; }
1017
-
1018
- if(event_data._message){
1019
- event_message.html(event_data._message);
1020
- } else if(event_type=="_pageview"){
1021
- event_message.html("Pageview: " + event_data.url);
1022
- } else if(event_type.substr(0,5) == '_set_'){
1023
- return true; /* dont render */
1024
- } else {
1025
- event_message.html(event_type);
1026
- }
1027
-
1028
- event_time.html(formatTimeOfDay(event_data._time));
1029
-
1030
- if(event_data._session_key && event_data._session_key.length > 0){
1031
- var __session_key = event_data._session_key;
1032
- var load_usersession = (function(){
1033
- loadEventHistory({session_key: __session_key});
1034
- });
1035
- if(session_data=sessionData[event_data._session_key]){
1036
- if(session_data._name){
1037
- event_props.append(
1038
- $('<strong></strong>').html(session_data._name).css({
1039
- 'cursor': 'pointer'
1040
- }).click(load_usersession)
1041
- );
1042
- }
1043
- if(session_data._picture){
1044
- event_picture.append(
1045
- $('<img width="40" />').attr('src', session_data._picture)
1046
- ).click(load_usersession);
1047
- }
1048
- }
1049
- }
1050
-
1051
- feedInnerElem.prepend(
1052
- $('<li class="feed_event"></li>')
1053
- .append(event_time)
1054
- .append(event_picture)
1055
- .append(event_message)
1056
- .append(event_props)
1057
- );
1058
- }
1059
-
1060
- function close(){
1061
- stopPoll();
1062
- };
1063
-
1064
- return {
1065
- load: load,
1066
- resize: resize,
1067
- close: close
1068
- };
1069
-
1070
- });
1071
-
1072
-
1073
- var dashboardView = (function(dashboard_name){
1074
-
1075
- var widgets = [];
1076
- var viewport = null;
1077
-
1078
- function load(_viewport){
1079
- viewport = _viewport.html('');
1080
- $.ajax({
1081
- url: FnordMetric.p + '/' + currentNamespace+'/dashboard/'+dashboard_name,
1082
- success: function(resp, status){
1083
- var conf = JSON.parse(resp);
1084
- renderWidgets(conf.widgets);
1085
- }
1086
- });
1087
- };
1088
-
1089
- function renderWidgets(_widgets){
1090
- for(wkey in _widgets){
1091
- var widget = _widgets[wkey];
1092
- widget["elem"] = $('<div class="widget"></div>');
1093
- widgets[wkey] = widget;
1094
- viewport.append(widget.elem);
1095
- resizeWidget(wkey);
1096
- renderWidget(wkey);
1097
- };
1098
- resize();
1099
- };
1100
-
1101
- function renderWidget(wkey){
1102
- var widget = widgets[wkey];
1103
- /* argh... */
1104
- if(widget.klass=='TimelineWidget'){ timelineWidget().render(widget); }
1105
- if(widget.klass=='BarsWidget'){ barsWidget().render(widget); }
1106
- if(widget.klass=='NumbersWidget'){ numbersWidget().render(widget); }
1107
- if(widget.klass=='ToplistWidget'){ toplistWidget().render(widget); }
1108
- if(widget.klass=='PieWidget'){ pieWidget().render(widget); }
1109
- if(widget.klass=="HtmlWidget") { htmlWidget().render(widget); }
1110
- };
1111
-
1112
- function resizeWidget(wkey){
1113
- var widget = widgets[wkey];
1114
- var wwperc = widgets[wkey].width;
1115
- if(!wwperc){ wwperc = 100; }
1116
- var wwidth = viewport.width() * (wwperc/100.0);
1117
- if(wwperc==100){
1118
- widgets[wkey].elem.addClass('full_width');
1119
- } else { wwidth -= 1; }
1120
- widget.elem.width(wwidth);
1121
- }
1122
-
1123
- function resize(){
1124
- for(wkey in widgets){
1125
- resizeWidget(wkey);
1126
- };
1127
- };
1128
-
1129
- function close(){
1130
- $('body').trigger('fm_dashboard_close');
1131
- };
1132
-
1133
- return {
1134
- load: load,
1135
- resize: resize,
1136
- close: close
1137
- };
1138
-
1139
- });
1140
-
1141
-
1142
- function renderDashboard(_dash){
1143
- loadView(dashboardView(_dash));
1144
- };
1145
-
1146
- function renderSessionView(){
1147
- loadView(sessionView());
1148
- }
1149
-
1150
- function loadView(_view){
1151
- if(currentView){ currentView.close(); }
1152
- canvasElem.html('loading!');
1153
- currentView = _view;
1154
- currentView.load(canvasElem);
1155
- resizeView();
1156
- };
1157
-
1158
- function resizeView(){
1159
- currentView.resize(
1160
- canvasElem.innerWidth(),
1161
- canvasElem.innerHeight()
1162
- );
1163
- };
1164
-
1165
- function init(_namespace, _canvasElem){
1166
- canvasElem = _canvasElem;
1167
- currentNamespace = _namespace;
1168
- loadView(sessionView());
1169
- };
1170
-
1171
- return {
1172
- p: '',
1173
- renderDashboard: renderDashboard,
1174
- renderSessionView: renderSessionView,
1175
- resizeView: resizeView,
1176
- init: init
1177
- };
1178
-
1179
- })();