scout_realtime 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- 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);
|