scout_realtime 0.5.3 → 0.5.4
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.
- data/CHANGELOG.md +4 -0
- data/lib/scout_realtime/models/processes.rb +1 -1
- data/lib/scout_realtime/version.rb +1 -1
- data/lib/scout_realtime/web/images/github.png +0 -0
- data/lib/scout_realtime/web/images/linux.png +0 -0
- data/lib/scout_realtime/web/images/logo.png +0 -0
- data/lib/scout_realtime/web/images/scout.png +0 -0
- data/lib/scout_realtime/web/javascripts/application.js +70 -28
- data/lib/scout_realtime/web/javascripts/d3.linechart.js +65 -17
- data/lib/scout_realtime/web/javascripts/jquery.debouncedresize.js +47 -0
- data/lib/scout_realtime/web/stylesheets/styles.css +245 -99
- data/lib/scout_realtime/web/views/graph.erb +0 -2
- data/lib/scout_realtime/web/views/header.erb +7 -7
- data/lib/scout_realtime/web/views/index.erb +22 -16
- data/lib/scout_realtime/web/views/layout.erb +5 -7
- data/lib/scout_realtime/web/views/overview_charts.erb +8 -12
- data/lib/scout_realtime/web/views/processes.erb +6 -6
- data/lib/scout_realtime/web/views/sidebar.erb +7 -7
- data/lib/scout_realtime/web/views/sidebar_bottom.erb +3 -0
- data/scout_realtime.gemspec +1 -1
- metadata +10 -9
- data/lib/scout_realtime/web/javascripts/bootstrap.min.js +0 -7
- data/lib/scout_realtime/web/javascripts/charts.js +0 -189
- data/lib/scout_realtime/web/javascripts/d3.barchart.js +0 -210
- data/lib/scout_realtime/web/stylesheets/bootstrap-theme.min.css +0 -7
- data/lib/scout_realtime/web/stylesheets/bootstrap.min.css +0 -7
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Scout::Realtime::Processes < Scout::Realtime::Metric
|
2
2
|
include Scout::Realtime::MultiAggregator
|
3
3
|
|
4
|
-
FIELDS = { :cpu => {'label'=>'CPU usage', 'units'=>'', 'precision'=>2},
|
4
|
+
FIELDS = { :cpu => {'label'=>'CPU usage', 'units'=>'%', 'precision'=>2},
|
5
5
|
:memory => {'units'=>'MB', 'precision'=>1},
|
6
6
|
:count => {'units'=>'', 'precision'=>0}
|
7
7
|
}
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,31 +1,30 @@
|
|
1
1
|
var refreshInterval = 1000;
|
2
2
|
var processIteration = 0;
|
3
|
+
var firstShift = true;
|
4
|
+
var overviewChartMargin = 30; // pulled from lineChart overview margin
|
3
5
|
function d(s){console.debug(s)}
|
4
6
|
|
5
|
-
|
6
|
-
$(function(){ setChartSizes()})
|
7
|
-
|
8
7
|
window.refresher = null;
|
9
8
|
function toggleData() {
|
10
9
|
if (window.refresher) {
|
11
10
|
clearInterval(window.refresher);
|
12
11
|
window.refresher = null;
|
13
|
-
$("#toggle .
|
14
|
-
$("#toggle .
|
12
|
+
$("#toggle #on.button").addClass("disabled");
|
13
|
+
$("#toggle #off.button").removeClass("disabled");
|
15
14
|
} else {
|
16
15
|
window.refresher = setInterval(refresh, refreshInterval);
|
17
|
-
$("#toggle .
|
18
|
-
$("#toggle .
|
16
|
+
$("#toggle #on.button").removeClass("disabled");
|
17
|
+
$("#toggle #off.button").addClass("disabled");
|
19
18
|
}
|
20
19
|
}
|
21
20
|
|
22
21
|
var graphs = {};
|
23
22
|
|
24
23
|
var Processes={
|
25
|
-
index:{},
|
26
|
-
element:null,
|
27
|
-
tableBody:null,
|
28
|
-
tableRowTemplate:null,
|
24
|
+
index:{}, // processName: {count:chart, cpu:chart, memory:chart, row:jQueryObject}
|
25
|
+
element:null, // passed on init
|
26
|
+
tableBody:null, // jQuery object, saved here for fast access
|
27
|
+
tableRowTemplate:null, // a handlebars template - is compiled on init
|
29
28
|
sortBy:"memory",
|
30
29
|
|
31
30
|
init:function(opts){
|
@@ -33,7 +32,16 @@ var Processes={
|
|
33
32
|
Processes.element=opts.element
|
34
33
|
Processes.tableBody=$(Processes.element).find("tbody");
|
35
34
|
Processes.tableRowTemplate = Handlebars.compile($("#process-template").html());
|
36
|
-
|
35
|
+
|
36
|
+
// process sorting
|
37
|
+
$('#processes th[data-sort-by="memory"] .sort-down').show();
|
38
|
+
$(Processes.element).find("th").hover().click(function(e){
|
39
|
+
$this=$(this);
|
40
|
+
$(Processes.element).find('.sort-down, .sort-up').hide();
|
41
|
+
$this.find('.sort-down').show();
|
42
|
+
Processes.setSort($this.data('sort-by'));
|
43
|
+
e.stopPropagation();
|
44
|
+
})
|
37
45
|
},
|
38
46
|
|
39
47
|
update:function(raw_processes){
|
@@ -59,8 +67,6 @@ var Processes={
|
|
59
67
|
}
|
60
68
|
}
|
61
69
|
}
|
62
|
-
|
63
|
-
this.initialUpdateCompleted = true;
|
64
70
|
|
65
71
|
// 3. add data and update charts
|
66
72
|
for(cmd in Processes.index) {
|
@@ -76,12 +82,12 @@ var Processes={
|
|
76
82
|
});
|
77
83
|
|
78
84
|
if(data) {
|
79
|
-
Processes.index[cmd]['row'].data({'memory':data['memory'],'cpu':data['cpu']}); // raw numbers for sorting
|
85
|
+
Processes.index[cmd]['row'].data({'memory':data['memory'],'cpu':data['cpu'],'count':data['count']}); // raw numbers for sorting
|
80
86
|
}
|
81
87
|
}
|
82
88
|
|
83
89
|
if(processIteration % 10 == 0) {
|
84
|
-
//
|
90
|
+
// 4. reorder table rows as needed
|
85
91
|
rows = Processes.tableBody.children('tr').get();
|
86
92
|
rows.sort(function(a, b) {
|
87
93
|
var compA = $(a).data(Processes.sortBy);
|
@@ -119,8 +125,7 @@ var Processes={
|
|
119
125
|
$.each(bufferedData, function(data_index, value) {
|
120
126
|
data.push({time: startTime + data_index * 1000, value: [value]})
|
121
127
|
});
|
122
|
-
|
123
|
-
chart = new lineChart({ element: $(this).get(0), data: data, metadata: meta.processes[metric] });
|
128
|
+
chart = new lineChart({ element: $(this).get(0), data: data, metadata: meta.processes[metric], type: 'process' });
|
124
129
|
chart.draw();
|
125
130
|
Processes.index[cmd][metric] = chart;
|
126
131
|
});
|
@@ -129,6 +134,13 @@ var Processes={
|
|
129
134
|
remove:function(cmd){
|
130
135
|
Processes.index[cmd]['row'].remove();
|
131
136
|
delete Processes.index[cmd];
|
137
|
+
},
|
138
|
+
setSort:function(sortBy){
|
139
|
+
if (sortBy != Processes.sortBy) {
|
140
|
+
Processes.sortBy = sortBy;
|
141
|
+
processIteration = 0;
|
142
|
+
Processes.update(metrics['processes']);
|
143
|
+
}
|
132
144
|
}
|
133
145
|
}
|
134
146
|
|
@@ -137,8 +149,13 @@ var Processes={
|
|
137
149
|
$(document).ready(init);
|
138
150
|
|
139
151
|
function init() {
|
140
|
-
|
141
|
-
|
152
|
+
Processes.init({element:document.getElementById('processes')});
|
153
|
+
setOverviewChartWidths();
|
154
|
+
|
155
|
+
$(window).on("debouncedresize", function( event ) {
|
156
|
+
setOverviewChartWidths();
|
157
|
+
});
|
158
|
+
|
142
159
|
$(".overview_chart").each(function (i) {
|
143
160
|
var $chart = $(this);
|
144
161
|
var collector = $chart.data("collector");
|
@@ -172,8 +189,7 @@ function init() {
|
|
172
189
|
|
173
190
|
data.push({time: startTime + data_index * 1000, value: [valueSum], valueBreakdown: valueBreakdown})
|
174
191
|
});
|
175
|
-
|
176
|
-
chart = new lineChart({ element: $chart.get(0), data: data, yScaleMax: yScaleMax, metadata: meta[collector][chartMetrics[0]] });
|
192
|
+
chart = new lineChart({ element: $chart.get(0), data: data, yScaleMax: yScaleMax, metadata: (chartMetrics.length > 1 ? meta[collector] : meta[collector][chartMetrics[0]]), type: 'overview' });
|
177
193
|
chart.draw();
|
178
194
|
graphs[id] = chart;
|
179
195
|
} else {
|
@@ -181,7 +197,7 @@ function init() {
|
|
181
197
|
}
|
182
198
|
});
|
183
199
|
|
184
|
-
$('#toggle').on('click', function() {
|
200
|
+
$('#toggle .button').on('click', function() {
|
185
201
|
toggleData();
|
186
202
|
});
|
187
203
|
|
@@ -189,9 +205,15 @@ function init() {
|
|
189
205
|
toggleData(); // uncomment to get continuous data
|
190
206
|
// setTimeout(updateGraphData, 1000) // uncomment to get ONE update in a second (for development)
|
191
207
|
// normally, lines 1 and 2 above will be uncommented
|
208
|
+
|
209
|
+
// in chrome, the initial chart size is larger than what the parent container is. calling it here resets things.
|
210
|
+
// still need to call it before drawing charts as FF won't draw it full width.
|
211
|
+
setOverviewChartWidths()
|
212
|
+
|
192
213
|
}
|
193
214
|
|
194
215
|
function updateGraphData() {
|
216
|
+
|
195
217
|
Processes.update(metrics['processes']);
|
196
218
|
|
197
219
|
$(".overview_chart").each(function (i) {
|
@@ -218,15 +240,20 @@ function updateGraphData() {
|
|
218
240
|
});
|
219
241
|
|
220
242
|
graph.data.push({time: new Date().getTime(), value: [valueSum], valueBreakdown: valueBreakdown});
|
221
|
-
|
243
|
+
if(firstShift) {
|
244
|
+
// only don't shift off the data on the first iteration. leave it a tail so the graph does not clip along choppily
|
245
|
+
firstShift = false;
|
246
|
+
} else {
|
247
|
+
graph.data.shift();
|
248
|
+
}
|
222
249
|
}
|
223
250
|
});
|
224
251
|
|
225
252
|
$('#report-time').html(formatTime(new Date));
|
226
253
|
$('#memory-used').html(parseInt(metrics.memory.used));
|
227
|
-
$('#memory-
|
254
|
+
$('#memory-size').html(parseInt(metrics.memory.size));
|
228
255
|
$('#disk-used').html(parseInt(metrics.disk[Object.keys(metrics.disk)[0]].used));
|
229
|
-
$('#disk-
|
256
|
+
$('#disk-size').html(parseInt(metrics.disk[Object.keys(metrics.disk)[0]].size));
|
230
257
|
}
|
231
258
|
|
232
259
|
function formatTime(timestamp) {
|
@@ -256,10 +283,25 @@ function refresh() {
|
|
256
283
|
|
257
284
|
/*
|
258
285
|
Called on initial load. Just sets the svg element to the size of its parent. This is needed on firefox.
|
286
|
+
|
287
|
+
Why don't we just use % widths for containers?
|
288
|
+
|
289
|
+
The chart SVGs have a 30px left margin to handle the y-axis. We need to shift the charts over 30px to the left
|
290
|
+
to handle this. We also need to add 30px to the width of the chart so it ends up using the entire container width
|
291
|
+
after the 30px left shift.
|
292
|
+
|
259
293
|
*/
|
260
|
-
function
|
294
|
+
function setOverviewChartWidths(){
|
295
|
+
$overview_charts_td = $('#overview_charts');
|
296
|
+
totalWidth = $overview_charts_td.width(); // width of the td container for the 4 overview charts
|
297
|
+
padding = parseInt($overview_charts_td.css('padding-left'));
|
298
|
+
containerWidth = (totalWidth-2*padding-5)/2; // why -5? chrome overlaps at -4, ff at -5. don't know why.
|
299
|
+
$('.overview_chart_container').each(function(i,e) {
|
300
|
+
$(e).width(containerWidth).css('margin-right',padding)
|
301
|
+
});
|
302
|
+
|
261
303
|
$('.overview_chart').each(function(i,e){
|
262
304
|
$svg=$(e);
|
263
|
-
$svg.width(
|
305
|
+
$svg.width(containerWidth+overviewChartMargin);
|
264
306
|
});
|
265
307
|
}
|
@@ -3,15 +3,24 @@ function lineChart(params) {
|
|
3
3
|
this.yScaleMax = params.yScaleMax;
|
4
4
|
this.element = params.element;
|
5
5
|
this.metadata = params.metadata;
|
6
|
-
|
6
|
+
// charts can plot multiple metrics. if so, we assume units are the same. fetch that data from the first metric.
|
7
|
+
this.shared_metadata = ('units' in this.metadata ? this.metadata : this.metadata[Object.keys(this.metadata)[0]]);
|
8
|
+
|
9
|
+
var line, path, x, y, barWidth, height, selection, chartInfo, latestValue, chartId, xAxis, yAxis;
|
10
|
+
var type = params.type;
|
11
|
+
if(type == 'overview') {
|
12
|
+
var margin = { top: 20, right: 0, bottom: 20, left: 30 };
|
13
|
+
} else {
|
14
|
+
var margin = { top: 0, right: 0, bottom: 0, left: 0 };
|
15
|
+
}
|
7
16
|
|
8
17
|
this.draw = function() {
|
9
18
|
var _this = this;
|
10
19
|
selection = d3.select(this.element);
|
11
20
|
chartId = $(selection[0]).data('collector') + $(selection[0]).data('instance-name');
|
12
21
|
// get the width and height form the selection
|
13
|
-
var width = selection.node().clientWidth || parseInt($(selection.node()).css('width'));
|
14
|
-
|
22
|
+
var width = (selection.node().clientWidth || parseInt($(selection.node()).css('width'))) - margin.right; // don't take left margin into account for smoothness' sake
|
23
|
+
height = (selection.node().clientHeight || parseInt($(selection.node()).css('height'))) - margin.bottom - margin.top;
|
15
24
|
y = d3.scale.linear().range([height, 0]);
|
16
25
|
x = d3.time.scale().range([0, width]);
|
17
26
|
now = new Date();
|
@@ -32,7 +41,8 @@ function lineChart(params) {
|
|
32
41
|
.attr("class", "clip-path")
|
33
42
|
.append("rect")
|
34
43
|
.attr("width", width - (barWidth * 2)) // new data points are drawn outside of clip area
|
35
|
-
.attr("height", height)
|
44
|
+
.attr("height", height)
|
45
|
+
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
36
46
|
|
37
47
|
line = d3.svg.line()
|
38
48
|
.interpolate("basis")
|
@@ -40,6 +50,18 @@ function lineChart(params) {
|
|
40
50
|
.y(function(d, i) { return y(d.value[0]); });
|
41
51
|
|
42
52
|
|
53
|
+
if(type == 'overview') {
|
54
|
+
xAxis = selection.append("g")
|
55
|
+
.attr("class", 'x axis')
|
56
|
+
.attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
|
57
|
+
.call(x.axis = d3.svg.axis().scale(x).ticks(d3.time.seconds, 15).tickFormat(d3.time.format("%I:%M:%S")).orient("bottom"));
|
58
|
+
|
59
|
+
yAxis = selection.append("g")
|
60
|
+
.attr("class", 'y axis')
|
61
|
+
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
|
62
|
+
.call(y.axis = d3.svg.axis().scale(y).ticks(1).orient("right"));
|
63
|
+
}
|
64
|
+
|
43
65
|
chartInfo = $(selection[0]).parent().siblings('.chart_info');
|
44
66
|
latestValue = $(selection[0]).parent().siblings('.latest_value');
|
45
67
|
latestValue.html(this.format(this.data[this.data.length - 1].value[0]));
|
@@ -52,13 +74,22 @@ function lineChart(params) {
|
|
52
74
|
.attr("class", "chart_section")
|
53
75
|
.attr("width", barWidth)
|
54
76
|
.attr("height", height)
|
55
|
-
.attr("transform", function(d, i) { return "translate(" + (x(d.time) - barWidth) + ",
|
77
|
+
.attr("transform", function(d, i) { return "translate(" + ((x(d.time) - barWidth) + margin.left) + "," + margin.top + ")"; })
|
78
|
+
// for overview charts, the latest value stays visible and the mouseover values appear beneath the chart.
|
79
|
+
// because of space constraints, the latest value is hidden in process charts and the mouseover value takes its place.
|
56
80
|
.on("mouseover", function(d) {
|
57
|
-
|
58
|
-
|
81
|
+
chartInfo.html(_this.tooltip(d))
|
82
|
+
if (chartInfo.css('display') == 'none') {
|
83
|
+
latestValue.hide();
|
84
|
+
chartInfo.show();
|
85
|
+
}
|
59
86
|
})
|
60
87
|
.on("mouseout", function(d) {
|
61
|
-
chartInfo.html("")
|
88
|
+
chartInfo.html("")
|
89
|
+
if (latestValue.css('display') == 'none') {
|
90
|
+
latestValue.show();
|
91
|
+
chartInfo.hide();
|
92
|
+
}
|
62
93
|
});
|
63
94
|
|
64
95
|
path = selection.append("g")
|
@@ -87,19 +118,31 @@ function lineChart(params) {
|
|
87
118
|
|
88
119
|
latestValue.html(this.format(this.data[this.data.length - 1].value[0]));
|
89
120
|
|
121
|
+
var translateTarget = x(x.domain()[0] - refreshInterval) + margin.left;
|
122
|
+
|
90
123
|
path
|
91
124
|
// update the line - this isn't part of the transition. new points will be off to the right.
|
92
125
|
.attr("d", line)
|
93
|
-
.attr("transform",
|
126
|
+
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
|
94
127
|
// slide the path to the left 1 second
|
95
128
|
.transition()
|
96
129
|
.duration(refreshInterval - 50) // duration is the same as the update interval...if it equals the update interval, it gets canceled?
|
97
130
|
.ease("linear")
|
98
|
-
.attr("transform", "translate(" +
|
131
|
+
.attr("transform", "translate(" + translateTarget + "," + margin.top + ")")
|
99
132
|
.each("end", function() {
|
100
133
|
_this.refresh();
|
101
134
|
});
|
102
135
|
|
136
|
+
if(type == 'overview') {
|
137
|
+
xAxis.transition()
|
138
|
+
.duration(refreshInterval - 50)
|
139
|
+
.ease("linear")
|
140
|
+
.call(x.axis);
|
141
|
+
|
142
|
+
yAxis.transition()
|
143
|
+
.call(y.axis);
|
144
|
+
}
|
145
|
+
|
103
146
|
chartSection = selection.selectAll("rect")
|
104
147
|
.data(this.data);
|
105
148
|
|
@@ -108,7 +151,7 @@ function lineChart(params) {
|
|
108
151
|
.attr("class", "chart_section")
|
109
152
|
.attr("width", barWidth)
|
110
153
|
.attr("height", height)
|
111
|
-
.attr("transform", function(d, i) { return "translate(" + (x(d.time) - barWidth) + ",
|
154
|
+
.attr("transform", function(d, i) { return "translate(" + ((x(d.time) - barWidth) + margin.left) + "," + margin.top + ")"; })
|
112
155
|
.on("mouseover", function(d) {
|
113
156
|
chartInfo.html(_this.tooltip(d));
|
114
157
|
})
|
@@ -117,7 +160,7 @@ function lineChart(params) {
|
|
117
160
|
})
|
118
161
|
.transition()
|
119
162
|
.duration(0)
|
120
|
-
.attr("transform", function(d, i) { return "translate(" + (x(d.time) - barWidth) + ",
|
163
|
+
.attr("transform", function(d, i) { return "translate(" + ((x(d.time) - barWidth) + margin.left) + "," + margin.top + ")"; })
|
121
164
|
} else {
|
122
165
|
setTimeout(function() { _this.refresh() }, refreshInterval);
|
123
166
|
}
|
@@ -148,18 +191,23 @@ function lineChart(params) {
|
|
148
191
|
}
|
149
192
|
|
150
193
|
this.format = function(value) {
|
151
|
-
var formattedUnits = ( this.
|
152
|
-
return(value.toFixed(this.
|
194
|
+
var formattedUnits = ( this.shared_metadata.units == '%' ? '%' : ' ' + this.shared_metadata.units )
|
195
|
+
return(value.toFixed(this.shared_metadata.precision) + formattedUnits);
|
153
196
|
}
|
154
197
|
|
155
198
|
this.tooltip = function(data) {
|
156
199
|
var tooltipStr = '';
|
157
|
-
|
200
|
+
// if multiple values (ex: io wait, system, user), display the metric label + value. otherwise,
|
201
|
+
// just display the value.
|
202
|
+
if(data.valueBreakdown && Object.keys(data.valueBreakdown).length > 1) {
|
158
203
|
for(var key in data.valueBreakdown) {
|
159
|
-
|
204
|
+
var label = ('units' in this.metadata ? this.metadata.label : this.metadata[key].label);
|
205
|
+
if (!label) { label = key };
|
206
|
+
// metrics aren't guarenteed to have a label. use it if they do.
|
207
|
+
tooltipStr += '<span>'+label + '<span>' + this.format(data.valueBreakdown[key]) + '</span></span>';
|
160
208
|
}
|
161
209
|
} else {
|
162
|
-
tooltipStr = this.format(data.value[0]);
|
210
|
+
tooltipStr = '<span><span>'+this.format(data.value[0])+'</span></span>';
|
163
211
|
}
|
164
212
|
return tooltipStr;
|
165
213
|
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
/*
|
2
|
+
* debouncedresize: special jQuery event that happens once after a window resize
|
3
|
+
*
|
4
|
+
* latest version and complete README available on Github:
|
5
|
+
* https://github.com/louisremi/jquery-smartresize
|
6
|
+
*
|
7
|
+
* Copyright 2012 @louis_remi
|
8
|
+
* Licensed under the MIT license.
|
9
|
+
*
|
10
|
+
* This saved you an hour of work?
|
11
|
+
* Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON
|
12
|
+
*/
|
13
|
+
(function($) {
|
14
|
+
|
15
|
+
var $event = $.event,
|
16
|
+
$special,
|
17
|
+
resizeTimeout;
|
18
|
+
|
19
|
+
$special = $event.special.debouncedresize = {
|
20
|
+
setup: function() {
|
21
|
+
$( this ).on( "resize", $special.handler );
|
22
|
+
},
|
23
|
+
teardown: function() {
|
24
|
+
$( this ).off( "resize", $special.handler );
|
25
|
+
},
|
26
|
+
handler: function( event, execAsap ) {
|
27
|
+
// Save the context
|
28
|
+
var context = this,
|
29
|
+
args = arguments,
|
30
|
+
dispatch = function() {
|
31
|
+
// set correct event type
|
32
|
+
event.type = "debouncedresize";
|
33
|
+
$event.dispatch.apply( context, args );
|
34
|
+
};
|
35
|
+
|
36
|
+
if ( resizeTimeout ) {
|
37
|
+
clearTimeout( resizeTimeout );
|
38
|
+
}
|
39
|
+
|
40
|
+
execAsap ?
|
41
|
+
dispatch() :
|
42
|
+
resizeTimeout = setTimeout( dispatch, $special.threshold );
|
43
|
+
},
|
44
|
+
threshold: 150
|
45
|
+
};
|
46
|
+
|
47
|
+
})(jQuery);
|