Matthias keeps asking "where are the pretty charts?" So here they are.

This commit is contained in:
Simon MacMullen 2012-11-16 13:27:31 +00:00
parent e8841ba530
commit 9051356e7a
13 changed files with 181 additions and 49 deletions

View File

@ -39,8 +39,10 @@ div.box, div.section, div.section-hidden { overflow: auto; width: 100%; }
.right { float: right; }
.clear { clear: both; }
.help { color: #888; cursor: pointer; }
.help:hover { color: #444; }
.help, .rate-options { color: #888; cursor: pointer; }
.help:hover, .rate-options:hover { color: #444; }
.rate-options-p { padding-top: 10px; clear: both; }
table { border-collapse: collapse; }
table th { font-weight: normal; color: black; }
@ -56,10 +58,6 @@ table.list th a.sort .arrow { color: #888; }
table.list td p { margin: 0; padding: 1px 0 0 0; }
table.list td p.warning { margin: 0; padding: 5px; }
table.list-with-total { border-bottom: none; }
table.list tr.total td { border: none; border-top: 1px solid #bbb; }
table.list tr.total th { border: none; border-top: 1px solid #bbb; text-align: right; vertical-align: middle; }
div.section table.list, div.section-hidden table.list { margin-bottom: 0; }
div.memory-bar { margin: 10px 0 5px 0; border-radius: 5px; border: 1px solid #ddd; float: left; }
@ -122,7 +120,7 @@ p.status-error th { background: white; }
p.warning, div.form-popup-warn { background: #ff8; border: 1px solid #bb8; }
div.form-popup-info { background: #8f8; border: 1px solid #4b4; }
div.form-popup-help { text-align: left !important; background: #f8f8f8; border: 1px solid #ccc; }
div.form-popup-warn, div.form-popup-info, div.form-popup-help { margin: 20px; padding: 15px; border-radius: 10px; -moz-border-radius: 10px; text-align: center; max-width: 600px; }
div.form-popup-warn, div.form-popup-info, div.form-popup-help { margin: 20px; padding: 15px; border-radius: 10px; -moz-border-radius: 10px; text-align: center; max-width: 600px; z-index: 1; }
p.status-error, p.warning { margin: 20px; padding: 15px; border-radius: 10px; -moz-border-radius: 10px; text-align: center; font-weight: bold; }
@ -130,6 +128,11 @@ p.status-error, p.warning { margin: 20px; padding: 15px; border-radius: 10px; -m
.highlight strong { font-size: 2em; display: block; color: #444; font-weight: normal; }
.highlight, .micro-highlight { float: left; }
.chart { margin: 20px 0; }
.chart-small { width: 600px; height: 200px; }
.chart-medium { width: 800px; height: 300px; }
.chart-large { width: 1200px; height: 400px; }
.mini-highlight { font-size: 150%; padding:10px; background-color: #ddd; color: #888; border-radius: 10px; -moz-border-radius: 10px; line-height: 300%; }
.micro-highlight { min-width: 120px; font-size: 100%; text-align:center; padding:10px; background-color: #ddd; margin: 0 20px 0 0; color: #888; border-radius: 10px; -moz-border-radius: 10px; }

View File

@ -3,6 +3,7 @@
<title>RabbitMQ Management</title>
<script src="js/ejs.js" type="text/javascript"></script>
<script src="js/jquery-1.6.4.min.js" type="text/javascript"></script>
<script src="js/jquery.flot.min.js" type="text/javascript"></script>
<script src="js/sammy-0.6.0.min.js" type="text/javascript"></script>
<script src="js/json2.js" type="text/javascript"></script>
<script src="js/base64.js" type="text/javascript"></script>
@ -11,11 +12,13 @@
<script src="js/prefs.js" type="text/javascript"></script>
<script src="js/help.js" type="text/javascript"></script>
<script src="js/formatters.js" type="text/javascript"></script>
<script src="js/charts.js" type="text/javascript"></script>
<link href="css/main.css" rel="stylesheet" type="text/css"/>
<link href="favicon.ico" rel="shortcut icon" type="image/x-icon"/>
<!--[if lte IE 8]>
<script src="js/excanvas.min.js" type="text/javascript"></script>
<link href="css/evil.css" rel="stylesheet" type="text/css"/>
<![endif]-->
</head>

View File

@ -0,0 +1,38 @@
function render_charts() {
$('.chart').map(function() {
render_chart($(this));
});
}
function render_chart(div) {
var chrome = {
series: { lines: { show: true } },
grid: { borderWidth: 2, borderColor: "#aaa" },
xaxis: { tickColor: "#fff", mode: "time" },
yaxis: { tickColor: "#eee", min: 0 },
legend: { position: 'se', backgroundOpacity: 0.5 }
};
var data = [];
for (var name in chart_data) {
var samples = chart_data[name].samples;
var d = [];
for (var i = 1; i < samples.length; i++) {
var x = samples[i].timestamp;
var y = (samples[i - 1].sample - samples[i].sample) * 1000 /
(samples[i - 1].timestamp - samples[i].timestamp);
d.push([x, y]);
}
data.push({label: name + " (" + chart_data[name].rate + " msg/s)",
data: d});
}
chart_data = {};
$.plot(div, data, chrome);
}
function update_rate_options(sammy) {
store_pref('rate-mode', sammy.params['mode']);
store_pref('chart-size', sammy.params['size']);
partial_update();
}

View File

@ -178,4 +178,7 @@ dispatcher_add(function(sammy) {
sammy.get('#/import-succeeded', function() {
render({}, 'import-succeeded', '#/overview');
});
sammy.put('#/rate-options', function() {
update_rate_options(this);
});
});

View File

@ -536,8 +536,9 @@ function _link_to(name, url) {
return '<a href="' + url + '">' + name + '</a>';
}
function message_rates(stats) {
var res = "";
function message_rates(stats, map) {
var res = '';
if (keys(stats).length > 0) {
var items = [['Publish', 'publish'], ['Confirm', 'confirm'],
['Deliver', 'deliver'],
@ -548,14 +549,18 @@ function message_rates(stats) {
['Return', 'return_unroutable']];
for (var i in items) {
var name = items[i][0];
var key = items[i][1] + '_details';
if (key in stats) {
res += '<div class="highlight">' + name;
res += '<strong>' + fmt_rate_num(stats[key].rate) + '</strong>';
res += 'msg/s</div>';
if (map != undefined && name in map) {
items[i][0] = map[name];
}
}
var res;
var mode = get_pref('rate-mode');
if (mode == 'chart') {
res = message_rates_chart(items, stats);
}
else {
res = message_rates_text(items, stats, mode);
}
if (res == "") {
res = '<p>Waiting for message rates...</p>';
}
@ -564,6 +569,36 @@ function message_rates(stats) {
res = '<p>Currently idle</p>';
}
return res + '<p class="rate-options-p"><span class="rate-options">[Options...]</span></p>';
}
function message_rates_chart(items, stats) {
var size = get_pref('chart-size');
var show = false;
for (var i in items) {
var name = items[i][0];
var key = items[i][1] + '_details';
if (key in stats) {
chart_data[name] = stats[key];
show = true;
}
}
return show ? '<div class="chart chart-' + size + '"></div>' : '';
}
function message_rates_text(items, stats, mode) {
var res = '';
for (var i in items) {
var name = items[i][0];
var key = items[i][1] + '_details';
if (key in stats) {
var num = mode == 'avg' ? stats[key].avg_rate : stats[key].rate;
res += '<div class="highlight">' + name;
res += '<strong>' + fmt_rate_num(num) + '</strong>';
res += 'msg/s';
res += '</div>';
}
}
return res;
}
@ -614,6 +649,12 @@ function fmt_permissions(obj, permissions, lookup, show, warning) {
return res.length == 0 ? warning : res.join(', ');
}
function fmt_option(name, value, current) {
return '<option value="' + value + '"' +
((value == current) ? ' selected="selected"' : '') +
'>' + name + '</option>';
}
function properties_size(obj) {
var count = 0;
for (k in obj) {

View File

@ -127,3 +127,6 @@ var last_successful_connect;
// TODO: maybe we don't need this any more?
var update_counter = 0;
// Used to hold chart data in between writing the div in an ejs and
// rendering the chart.
var chart_data = {};

View File

@ -194,6 +194,7 @@ function update() {
replace_content('main', html);
postprocess();
postprocess_partial();
render_charts();
maybe_scroll();
reset_timer();
});
@ -219,6 +220,7 @@ function partial_update() {
}
replace_content('scratch', '');
postprocess_partial();
render_charts();
});
}
}
@ -432,9 +434,15 @@ function postprocess() {
$('.help').die().live('click', function() {
help($(this).attr('id'))
});
$('.rate-options').die().live('click', function() {
show_popup('help', format('rate-options', {}));
});
$('input, select').live('focus', function() {
update_counter = 0; // If there's interaction, reset the counter.
});
$('form.auto-submit select, form.auto-submit input').live('click', function(){
$(this).parents('form').submit();
});
if (! user_administrator) {
$('.administrator-only').remove();
}

View File

@ -1,8 +1,16 @@
<h1>Channel: <b><%= fmt_escape_html(channel.name) %></b></h1>
<div class="section">
<h2>Details</h2>
<h2>Overview</h2>
<div class="hider updatable">
<% if (statistics_level == 'fine') { %>
<h3>Message rates</h3>
<div class="box">
<%= message_rates(channel.message_stats) %>
</div>
<% } %>
<h3>Details</h3>
<table class="facts">
<tr>
<th>Connection</th>
@ -75,7 +83,7 @@
<% if (statistics_level == 'fine') { %>
<div class="section">
<h2>Message rates</h2>
<h2>Message rates breakdown</h2>
<div class="hider updatable">
<table class="two-col-layout">
<tr>
@ -83,14 +91,12 @@
<%= format('msg-detail-publishes',
{'mode': 'channel',
'object': channel.publishes,
'label': 'Publishes',
'totals': channel.message_stats}) %>
'label': 'Publishes'}) %>
</td>
<td>
<%= format('msg-detail-deliveries',
{'mode': 'channel',
'object': channel.deliveries,
'totals': channel.message_stats}) %>
'object': channel.deliveries}) %>
</td>
</tr>
</table>

View File

@ -2,6 +2,15 @@
<div class="section">
<h2>Overview</h2>
<% if (statistics_level == 'fine') { %>
<h3>Message rates</h3>
<div class="box">
<%= message_rates(exchange.message_stats_in, {'Publish': 'Publish in'}) %>
<%= message_rates(exchange.message_stats_out, {'Publish': 'Publish out'}) %>
</div>
<% } %>
<h3>Details</h3>
<div class="hider updatable">
<table class="facts">
<tr>
@ -28,7 +37,7 @@
<% if (statistics_level == 'fine') { %>
<div class="section-hidden">
<h2>Message rates</h2>
<h2>Message rates breakdown</h2>
<div class="hider updatable">
<table class="two-col-layout">
<tr>
@ -36,15 +45,13 @@
<%= format('msg-detail-publishes',
{'mode': 'exchange-incoming',
'object': exchange.incoming,
'label': 'Incoming <span class="help" id="exchange-rates-incoming"></span>',
'totals': exchange.message_stats_in}) %>
'label': 'Incoming <span class="help" id="exchange-rates-incoming"></span>'}) %>
</td>
<td>
<%= format('msg-detail-publishes',
{'mode': 'exchange-outgoing',
'object': exchange.outgoing,
'label': 'Outgoing <span class="help" id="exchange-rates-outgoing"></span>',
'totals': exchange.message_stats_out}) %>
'label': 'Outgoing <span class="help" id="exchange-rates-outgoing"></span>'}) %>
</td>
</tr>
</table>

View File

@ -3,7 +3,7 @@
<%
var col_redeliver = !is_col_empty(object, 'redeliver', function(o) {return o.stats;});
%>
<table class="list list-with-total">
<table class="list">
<tr>
<% if (mode == 'queue') { %>
<th>Channel</th>
@ -32,11 +32,6 @@
<td class="r"><%= fmt_rate(del.stats, 'ack') %></td>
</tr>
<% } %>
<tr class="total">
<th>Total:</th>
<td class="r"><%= fmt_deliver_rate(totals, col_redeliver, 'mini-highlight') %></td>
<td class="r"><%= fmt_rate(totals, 'ack', false, 'mini-highlight') %></td>
</tr>
</table>
<% } else { %>
<p> ... no deliveries ...</p>

View File

@ -1,9 +1,9 @@
<h3><%= label %></h3>
<%
var col_return_unroutable = !is_stat_empty(totals, 'return_unroutable');
%>
<% if (object && object.length > 0) { %>
<table class="list list-with-total">
<%
var col_return_unroutable = !is_col_empty(object, 'return_unroutable', function(o) {return o.stats;});
%>
<table class="list">
<tr>
<% if (mode == 'channel') { %>
<th>Exchange</th>
@ -42,14 +42,6 @@
<% } %>
</tr>
<% } %>
<tr class="total">
<th>Total:</th>
<td class="r"><%= fmt_rate(totals, 'publish', false, 'mini-highlight') %></td>
<td class="r"><%= fmt_rate(totals, 'confirm', false, 'mini-highlight') %></td>
<% if (col_return_unroutable) { %>
<td class="r"><%= fmt_rate(totals, 'return_unroutable', false, 'mini-highlight') %></td>
<% } %>
</tr>
</table>
<% } else { %>
<p> ... no publishes ...</p>

View File

@ -9,6 +9,12 @@
<%= queue_length(queue, 'Unacknowledged', 'messages_unacknowledged') %>
<%= queue_length(queue, 'Total', 'messages') %>
</div>
<% if (statistics_level == 'fine') { %>
<h3>Message rates</h3>
<div class="box">
<%= message_rates(queue.message_stats) %>
</div>
<% } %>
<h3>Details</h3>
<table class="facts">
@ -85,7 +91,7 @@
<% if (statistics_level == 'fine') { %>
<div class="section-hidden">
<h2>Message rates</h2>
<h2>Message rates breakdown</h2>
<div class="hider updatable">
<table class="two-col-layout">
<tr>
@ -93,15 +99,13 @@
<%= format('msg-detail-publishes',
{'mode': 'queue',
'object': queue.incoming,
'label': 'Incoming',
'totals': queue.message_stats}) %>
'label': 'Incoming'}) %>
</td>
<td>
<%= format('msg-detail-deliveries',
{'mode': 'queue',
'object': queue.deliveries,
'totals': queue.message_stats}) %>
'object': queue.deliveries}) %>
</td>
</tr>
</table>

View File

@ -0,0 +1,29 @@
<%
var mode = get_pref('rate-mode');
var size = get_pref('chart-size');
%>
<form action="#/rate-options" method="put" class="auto-submit">
<table class="form">
<tr>
<th><label>Display:</label></th>
<td>
<select name="mode">
<%= fmt_option('Charts', 'chart', mode) %>
<%= fmt_option('Rates (instantaneous)', 'inst', mode) %>
<%= fmt_option('Rates (moving average)', 'avg', mode) %>
</select>
</td>
</tr>
<tr>
<th><label>Chart size:</label></th>
<td>
<select name="size">
<%= fmt_option('Small', 'small', size) %>
<%= fmt_option('Medium', 'Medium', size) %>
<%= fmt_option('Large', 'large', size) %>
</select>
</td>
</tr>
</table>
</form>