Rearrange rates into a details object. Change overview to show global message rates rather than data rates.
This commit is contained in:
parent
04a1485f5c
commit
baf3dabbf6
|
@ -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.
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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) { %>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) ->
|
||||
|
|
|
@ -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()).
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue