Rearrange rates into a details object. Change overview to show global message rates rather than data rates.

This commit is contained in:
Simon MacMullen 2010-09-02 12:58:34 +01:00
parent 04a1485f5c
commit baf3dabbf6
12 changed files with 126 additions and 58 deletions

View File

@ -1,26 +1,26 @@
Before preview release:
Write more management:
Exchange / queue arguments support
Bindings arguments support
Channel GET
Finish basic documentation.
Figure out supervision.
Cross-browser testing.
Rearrange rates for future expansion.
Fix the rate bug.
Before first release:
Write remaining management:
Exchange / queue arguments support
Bindings arguments support
Application list / start / stop
VM stop
Reset / rotate logs / cluster
Add superuser bit (bug 22983).
Tests for permissions.
Fix the rate bug.
More user-friendly form submission errors.
Make it work with fine stats turned off.
Do sensible things with vhosts in UI.
Fix refreshing.
Write more thorough unit tests, especially around listings.
Fix backing queue status breakage.
Then:
Figure out something sensible to do with time series.

View File

@ -46,8 +46,10 @@ p.status-paused { color: #888; }
p.status-warning { color: black; background-color: #ff8; }
p.status-error { color: white; background-color: #f00; }
.highlight { width: 150px; font-size: 150%; float: right; text-align:center; padding:10px; background-color: #ddd; margin: 0 20px 20px 0; color: #888; border-radius: 10px; -moz-border-radius: 10px; }
.highlight strong { font-size: 2em; display: block; color: #444; font-weight: normal; }
.highlight, .highlight-right { width: 150px; font-size: 150%; text-align:center; padding:10px; background-color: #ddd; margin: 0 20px 20px 0; color: #888; border-radius: 10px; -moz-border-radius: 10px; }
.highlight strong, .highlight-right strong { font-size: 2em; display: block; color: #444; font-weight: normal; }
.highlight { float: left; }
.highlight-right { float: right; }
div.section, div.section-hidden { border: 1px solid #ddd; margin: 0 0 1em 0; }
div.section-hidden div { display: none; }

View File

@ -40,10 +40,20 @@ function fmt_color(r) {
}
function fmt_rate(obj, name) {
if (obj == undefined || obj[name] == undefined) return '';
return fmt_rate0(obj, name, fmt_num);
}
return fmt_num(obj[name + '_rate']) + '/s' +
'<sub>(' + fmt_num(obj[name]) + ' total)</sub>';
function fmt_rate_bytes(obj, name) {
return fmt_rate0(obj, name, fmt_bytes);
}
function fmt_rate0(obj, name, fmt) {
if (obj == undefined || obj[name] == undefined) return '';
var res = '<sub>(' + fmt(obj[name]) + ' total)</sub>';
if (obj[name + '_details'] != undefined) {
res = fmt(obj[name + '_details'].rate) + '/s' + res;
}
return res;
}
function fmt_exchange(name) {

View File

@ -171,14 +171,10 @@ function postprocess() {
}
function with_reqs(reqs, acc, fun) {
var key;
for (var k in reqs) {
key = k;
}
if (key != undefined) {
if (keys(reqs).length > 0) {
var key = keys(reqs)[0];
with_req('/api' + reqs[key], function(resp) {
acc[key] = jQuery.parseJSON(resp.responseText);
acc['last-modified'] = resp.getResponseHeader('Last-Modified');
var remainder = {};
for (var k in reqs) {
if (k != key) remainder[k] = reqs[k];
@ -284,3 +280,11 @@ function fill_path_template(template, params) {
function debug(str) {
$('<p>' + str + '</p>').appendTo('#debug');
}
function keys(obj) {
var ks = [];
for (var k in obj) {
ks.push(k);
}
return ks;
}

View File

@ -41,8 +41,8 @@
<td class="l"><%= fmt_rate(channels[i].message_stats, 'publish') %></td>
<td class="l"><%= fmt_rate(channels[i].message_stats, 'deliver') %></td>
<td class="l"><%= fmt_rate(channels[i].message_stats, 'get') %></td>
<td class="l"><%= fmt_rate(channels[i].message_stats, 'deliver_noack') %></td>
<td class="l"><%= fmt_rate(channels[i].message_stats, 'get_noack') %></td>
<td class="l"><%= fmt_rate(channels[i].message_stats, 'deliver_no_ack') %></td>
<td class="l"><%= fmt_rate(channels[i].message_stats, 'get_no_ack') %></td>
<td class="l">
<%= fmt_rate(channels[i].message_stats, 'ack') %>
<% if (channels[i].messages_unacknowledged > 0) { %>

View File

@ -3,15 +3,15 @@
<div class="section">
<h2>Overview</h2>
<div>
<div class="highlight">
<div class="highlight-right">
Receiving
<strong><%= fmt_bytes(connection.recv_oct_rate) %>/s</strong>
<strong><%= fmt_bytes(connection.recv_oct_details.rate) %>/s</strong>
(<%= fmt_bytes(connection.recv_oct) %> total)
</div>
<div class="highlight">
<div class="highlight-right">
<span>Sending</span>
<strong><%= fmt_bytes(connection.send_oct_rate) %>/s</strong>
<strong><%= fmt_bytes(connection.send_oct_details.rate) %>/s</strong>
(<%= fmt_bytes(connection.send_oct) %> total)
</div>
<table class="facts">

View File

@ -16,8 +16,8 @@
<% for (var i = 0; i < connections.length; i++) { %>
<tr<%= alt_rows(i)%>>
<td><%= link_conn(connections[i].name) %></td>
<td><%= fmt_bytes(connections[i].recv_oct_rate) %>/s<sub>(<%= fmt_bytes(connections[i].recv_oct) %> total)</sub></td>
<td><%= fmt_bytes(connections[i].send_oct_rate) %>/s<sub>(<%= fmt_bytes(connections[i].send_oct) %> total)</sub></td>
<td><%= fmt_rate_bytes(connections[i], 'recv_oct') %></td>
<td><%= fmt_rate_bytes(connections[i], 'send_oct') %></td>
<td><%= connections[i].channels %></td>
<td><%= link_vhost(connections[i].vhost) %></td>
<td><%= link_user(connections[i].user) %></td>

View File

@ -1,28 +1,53 @@
<h1>Overview</h1>
<div class="section">
<h2>Message Rates</h2>
<div>
<% if (keys(overview.message_stats).length > 0) {
var items = [['Publish', 'publish'], ['Deliver', 'deliver'],
['Acknowledge', 'ack'], ['Get', 'get'],
['Deliver (noack)', 'deliver_no_ack'],
['Get (noack)', 'get_no_ack']];
for (var i in items) {
var name = items[i][0];
var key = items[i][1] + '_details';
if (key in overview.message_stats) {
%>
<div class="highlight">
Receiving
<strong><%= fmt_bytes(overview.recv_oct_rate) %>/s</strong>
</div>
<div class="highlight">
<span>Sending</span>
<strong><%= fmt_bytes(overview.send_oct_rate) %>/s</strong>
<%= name %>
<strong><%= Math.round(overview.message_stats[key].rate) %></strong>
msg/s
</div>
<%
}
}
}
else {
%>
<p>Currently idle</p>
<%
}
%>
<span class="br"></span>
</div>
</div>
<div class="section">
<h2>System Information</h2>
<div>
<table class="facts">
<tr>
<th>node</th>
<th>Erlang Nodename</th>
<td><%= overview.node %></td>
</tr>
<tr>
<th>pid</th>
<th>Operating System PID</th>
<td><%= overview.os_pid %></td></tr>
<tr>
<th>bound to</th>
<th>Bound To</th>
<td><%= overview.bound_to %></td></tr>
<tr>
<th>file descriptors</th>
<th>File Descriptors</th>
<td class="status">
<div class="<%= fmt_color(overview.fd_used / overview.fd_total) %>">
<%= overview.fd_used %> / <%= overview.fd_total %>
@ -31,7 +56,7 @@
</td>
</tr>
<tr>
<th>erlang processes</th>
<th>Erlang Processes</th>
<td class="status">
<div class="<%= fmt_color(overview.proc_used / overview.proc_total) %>">
<%= overview.proc_used %> / <%= overview.proc_total %>
@ -40,7 +65,7 @@
</td>
</tr>
<tr>
<th>memory</th>
<th>Memory</th>
<td class="status">
<div class="<%= fmt_color(overview.mem_used / overview.mem_total) %>">
<%= fmt_bytes(overview.mem_used) %> / <%= fmt_bytes(overview.mem_total) %>
@ -49,3 +74,5 @@
</td>
</tr>
</table>
</div>
</div>

View File

@ -116,20 +116,22 @@ rate(Stats, Timestamp, OldStats, OldTimestamp, Key) ->
unknown;
_ ->
Diff = pget(Key, Stats) - pget(Key, OldStats),
{list_to_atom(atom_to_list(Key) ++ "_rate"),
Diff / (timer:now_diff(Timestamp, OldTimestamp) / 1000000)}
Name = list_to_atom(atom_to_list(Key) ++ "_details"),
Rate = Diff / (timer:now_diff(Timestamp, OldTimestamp) / 1000000),
{Name, [{rate, Rate},
{last_event, rabbit_mgmt_format:timestamp(Timestamp)}]}
end.
sum(Table, Keys) ->
lists:foldl(fun (Stats, Acc) ->
[{Key, Val + pget(Key, Stats, 0)} || {Key, Val} <- Acc]
end,
[{Key, 0} || Key <- Keys],
[Value || {_Key, Value, _TS} <- ets:tab2list(Table)]).
%% sum(Table, Keys) ->
%% lists:foldl(fun (Stats, Acc) ->
%% [{Key, Val + pget(Key, Stats, 0)} || {Key, Val} <- Acc]
%% end,
%% [{Key, 0} || Key <- Keys],
%% [Value || {_Key, Value, _TS} <- ets:tab2list(Table)]).
group_sum(GroupBy, List) ->
lists:foldl(fun ({Ids, New}, Acc) ->
Id = {GroupBy, pget(GroupBy, Ids)},
Id = [{G, pget(G, Ids)} || G <- GroupBy],
dict:update(Id, fun(Cur) -> gs_update(Cur, New) end,
New, Acc)
end,
@ -137,7 +139,17 @@ group_sum(GroupBy, List) ->
[I || I <- List]).
gs_update(Cur, New) ->
[{Key, Val + pget(Key, Cur, 0)} || {Key, Val} <- New].
[{Key, gs_update_add(Key, Val, pget(Key, Cur, 0))} || {Key, Val} <- New].
gs_update_add(Key, Old, New) ->
case lists:reverse(atom_to_list(Key)) of
"sliated_" ++ _ -> %% "_details" backwards
[{rate, pget(rate, Old) + pget(rate, New)},
{last_event, erlang:max(pget(last_event, Old),
pget(last_event, New))}];
_ ->
Old + New
end.
%%----------------------------------------------------------------------------
@ -219,14 +231,17 @@ handle_call({get_connection, Name}, State = #state{tables = Tables}) ->
handle_call(get_channels, State = #state{tables = Tables}) ->
Table = orddict:fetch(channel_stats, Tables),
Stats = merge_created_stats(Table),
FineQ = get_fine_stats(channel_queue_stats, channel, Tables),
FineX = get_fine_stats(channel_exchange_stats, channel, Tables),
FineQ = get_fine_stats(channel_queue_stats, [channel], Tables),
FineX = get_fine_stats(channel_exchange_stats, [channel], Tables),
{ok, augment_msg_stats(merge_fine_stats(Stats, [FineQ, FineX]), Tables),
State};
handle_call(get_overview, State = #state{tables = Tables}) ->
Table = orddict:fetch(connection_stats, Tables),
{ok, sum(Table, [recv_oct, send_oct, recv_oct_rate, send_oct_rate]), State};
FineQ = extract_singleton_fine_stats(
get_fine_stats(channel_queue_stats, [], Tables)),
FineX = extract_singleton_fine_stats(
get_fine_stats(channel_exchange_stats, [], Tables)),
{ok, [{message_stats, FineX ++ FineQ}], State};
handle_call(_Request, State) ->
{ok, not_understood, State}.
@ -387,7 +402,7 @@ merge_fine_stats(Stats, [Dict|Dicts]) ->
merge_fine_stats0(Props, Dict) ->
Id = pget(pid, Props),
case dict:find({channel, Id}, Dict) of
case dict:find([{channel, Id}], Dict) of
{ok, Stats} ->
Stats0 = pget(message_stats, Props, []),
[{message_stats, Stats0 ++ Stats}|
@ -395,6 +410,12 @@ merge_fine_stats0(Props, Dict) ->
error -> Props
end.
extract_singleton_fine_stats(Dict) ->
case dict:to_list(Dict) of
[] -> [];
[{[], Stats}] -> Stats
end.
queue_to_list(#amqqueue{name = Name, durable = Durable,
auto_delete = AutoDelete,
exclusive_owner = ExclusiveOwner,

View File

@ -20,7 +20,7 @@
%%
-module(rabbit_mgmt_format).
-export([format/2, print/2, pid/1, ip/1, table/1, tuple/1]).
-export([format/2, print/2, pid/1, ip/1, table/1, tuple/1, timestamp/1]).
-export([protocol/1, resource/1, permissions/1, user_permissions/1]).
-export([exchange/1, user/1, binding/1, pack_props/2, url/2]).
@ -86,6 +86,11 @@ protocol({Major, Minor, 0}) ->
protocol({Major, Minor, Revision}) ->
print("~p-~p-~p", [Major, Minor, Revision]).
timestamp(unknown) ->
unknown;
timestamp({MegaSecs, Secs, MicroSecs}) ->
trunc(MegaSecs*1000000000 + Secs*1000 + MicroSecs/1000).
resource(unknown) ->
unknown;
resource(Res) ->

View File

@ -55,8 +55,7 @@ is_authorized(ReqData, Context) ->
end.
now_ms() ->
{MegaSecs, Secs, MicroSecs} = now(),
trunc(MegaSecs*1000000000 + Secs*1000 + MicroSecs/1000).
rabbit_mgmt_format:timestamp(now()).
http_date() ->
httpd_util:rfc1123_date(erlang:universaltime()).

View File

@ -38,10 +38,10 @@ rates_test() ->
NewTS = {0, 10, 0},
WithRates = rabbit_mgmt_db:rates(New, NewTS, Previous, PreviousTS,
[foo, bar, bash]),
equals(0.1, pget(foo_rate, WithRates)),
equals(10, pget(bar_rate, WithRates)),
undefined = pget(bash_rate, WithRates),
undefined = pget(baz_rate, WithRates).
equals(0.1, pget(rate, pget(foo_details, WithRates))),
equals(10, pget(rate, pget(bar_details, WithRates))),
undefined = pget(bash_details, WithRates),
undefined = pget(baz_details, WithRates).
http_overview_test() ->
%% Rather crude, but this req doesn't say much and at least this means it