Merge from default

This commit is contained in:
Simon MacMullen 2010-11-03 10:30:50 +00:00
commit 89c59aa838
17 changed files with 489 additions and 201 deletions

View File

@ -18,7 +18,7 @@ h1 { font-size: 2em; font-weight: normal; padding: 0; }
b { color: black; font-weight: normal; }
table { border-collapse: collapse; }
table th, table td { font: 12px/17px Verdana,sans-serif; padding: 4px; min-width: 6em; }
table th, table td { font: 12px/17px Verdana,sans-serif; padding: 4px; min-width: 5em; width: auto; }
table.list th, table.list td { vertical-align: top; }
table.list { border-width: 1px; border-bottom: 1px solid #bbb; margin-bottom: 1em; }
@ -27,14 +27,24 @@ table.list th { text-align: center; font-weight: bold; border-top: 1px solid #bb
table.list td a { display: block; width: 100%; }
table.list th a.sort { display: block; width: 100%; cursor: pointer; }
table.list th a.sort .arrow { color: #888; }
table.list td p { margin: 0; padding: 1px 0 0 0; }
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; }
sub { display: block; color: #888; }
.unknown { color: #888; }
table.facts th { text-align: right; font-weight: bold; }
table.facts th, table.facts td { vertical-align: top; }
table.facts { border-left: 1px solid #ccc; border-right: 1px solid #ddd; }
table.facts th { color: black; padding: 10px; }
table.facts td { padding: 10px 10px 10px 10px; }
table.facts th, table.facts td { text-align: center; vertical-align: top; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; }
table.facts-long th { text-align: right; font-weight: bold; }
table.facts-long th, table.facts-long td { vertical-align: top; }
tr.alt td {
background: #eee;
@ -48,9 +58,9 @@ td.status div.red * { color:white; }
td.status div.yellow { background: yellow; }
td.status div.green { background: lightgreen; }
td.l { text-align: left; }
td.c { text-align: center; }
td.r { text-align: right; }
td.l { text-align: left !important; }
td.c { text-align: center !important; }
td.r { text-align: right !important; }
p.status-ok { color: #888; }
p.status-error { background: #f44; border: 2px solid #800; color: white; margin-top: 50px !important; }
@ -86,7 +96,8 @@ table.form select.narrow { width: 80px; }
table.form .multifield { margin: 0; padding: 0; }
table.form .multifield p { margin: 0; padding: 0; }
table.two-col-layout td { width: 50%; vertical-align: top; }
table.two-col-layout { width: 100%; }
table.two-col-layout > tbody > tr > td { width: 50%; vertical-align: top; }
input[type=submit], button { padding: 8px; background-color: #aaf; border-radius: 5px; -moz-border-radius: 5px; color: black !important; text-decoration: none; cursor: pointer; border: none; font-weight: bold; }
@ -95,6 +106,8 @@ input[type=submit], button { padding: 8px; background-color: #aaf; border-radius
h3 { padding: 0 0 2px 0; margin: 1em 0 1em 0; font-size: 1em; border-bottom: 1px solid #E4E4E4; font-weight: normal; }
acronym { background: #add; color: #222; padding: 2px 4px; border-radius: 2px; -moz-border-radius: 2px; cursor: pointer; }
table.bindings { margin-bottom: 2em; }
td.binding-endpoint span.object { border: 2px solid #bbb; padding: 10px; border-radius: 10px; -moz-border-radius: 10px; }
td.binding-endpoint span.arrow { font-size: 200%; }

View File

@ -34,6 +34,21 @@ function fmt_boolean(b) {
return b ? "●" : "○";
}
function fmt_parameters(obj) {
var res = '';
if (obj.durable) {
res += '<acronym title="Durable">D</acronym> ';
}
if (obj.auto_delete) {
res += '<acronym title="Auto-delete">AD</acronym> ';
}
var args = fmt_table_short(obj.arguments);
if (args != '') {
res += '<p>' + args + '</p>';
}
return res;
}
function fmt_color(r, thresholds) {
if (r == undefined) return '';
if (thresholds == undefined) thresholds = DESCRIPTOR_THRESHOLDS;
@ -49,19 +64,22 @@ function fmt_color(r, thresholds) {
return 'green';
}
function fmt_rate(obj, name) {
return fmt_rate0(obj, name, fmt_num);
function fmt_rate(obj, name, show_total) {
return fmt_rate0(obj, name, fmt_num, show_total);
}
function fmt_rate_bytes(obj, name) {
return fmt_rate0(obj, name, fmt_bytes);
return fmt_rate0(obj, name, fmt_bytes, true);
}
function fmt_rate0(obj, name, fmt) {
function fmt_rate0(obj, name, fmt, show_total) {
if (obj == undefined || obj[name] == undefined) return '';
var res = '<sub>(' + fmt(obj[name]) + ' total)</sub>';
var res = '';
if (obj[name + '_details'] != undefined) {
res = fmt(obj[name + '_details'].rate) + '/s' + res;
res = fmt(obj[name + '_details'].rate) + '/s';
}
if (show_total) {
res += '<sub>(' + fmt(obj[name]) + ' total)</sub>';
}
return res;
}

View File

@ -1,29 +1,55 @@
<h1>Channel: <b><%= channel.name %></b></h1>
<div class="section">
<h2>Details</h2>
<div class="hider updatable">
<table class="facts">
<tr>
<th>Connection</th>
<th>Virtual host</th>
<th>Username</th>
<th>Transactional</th>
<th>Prefetch count</th>
<th>Acks uncommitted</th>
<th>Messages unacknowledged</th>
<th>Client blocked</th>
</tr>
<tr>
<td><%= link_conn(channel.connection_details.name) %></td>
<td><%= channel.vhost %></td>
<td><%= channel.user %></td>
<td><%= fmt_boolean(channel.transactional) %></td>
<td><%= channel.prefetch_count %></td>
<td><%= channel.acks_uncommitted %></td>
<td><%= channel.messages_unacknowledged %></td>
<td><%= fmt_boolean(channel.client_flow_blocked) %></td>
</tr>
</table>
</div>
</div>
<% if (statistics_level == 'fine') { %>
<div class="section">
<h2>Message Rates</h2>
<div class="hider updatable">
<%= message_rates(channel.message_stats) %>
<table class="two-col-layout">
<tr>
<td>
<%= format('msg-detail-publishes',
{'mode': 'channel',
'object': channel.publishes,
'label': 'Publishes',
'totals': channel.message_stats}) %>
</td>
<td>
<%= format('msg-detail-deliveries',
{'mode': 'channel',
'object': channel.deliveries,
'totals': channel.message_stats}) %>
</td>
</tr>
</table>
<span class="br"></span>
</div>
</div>
<% } %>
<div class="section">
<h2>Overview</h2>
<div class="hider updatable">
<table class="facts">
<tr><th>Connection</th><td><%= link_conn(channel.connection_details.name) %></td></tr>
<tr><th>Virtual host</th><td><%= channel.vhost %></td></tr>
<tr><th>Username</th><td><%= channel.user %></td></tr>
<tr><th>Transactional</th><td><%= fmt_boolean(channel.transactional) %></td></tr>
<tr><th>Prefetch count</th><td><%= channel.prefetch_count %></td></tr>
<tr><th>Acks uncommitted</th><td><%= channel.acks_uncommitted %></td></tr>
<tr><th>Messages unacknowledged</th><td><%= channel.messages_unacknowledged %></td></tr>
<% if ('client_flow_blocked' in channel) { %>
<tr><th>Client blocked</th><td><%= fmt_boolean(channel.client_flow_blocked) %></td></tr>
<% } %>
</table>
</div>
</div>

View File

@ -61,11 +61,9 @@
<td class="c"><%= channel.prefetch_count %></td>
<td class="c"><%= channel.messages_unacknowledged %></td>
<% if (statistics_level == 'fine') { %>
<td class="l"><%= fmt_rate(channel.message_stats, 'publish') %></td>
<td class="l"><%= fmt_rate(channel.message_stats, 'deliver_get') %></td>
<td class="l">
<%= fmt_rate(channel.message_stats, 'ack') %>
</td>
<td class="r"><%= fmt_rate(channel.message_stats, 'publish') %></td>
<td class="r"><%= fmt_rate(channel.message_stats, 'deliver_get') %></td>
<td class="r"><%= fmt_rate(channel.message_stats, 'ack') %></td>
<% } %>
</tr>
<% } %>

View File

@ -5,28 +5,38 @@
<div class="hider updatable">
<table class="facts">
<tr>
<% if (connection.recv_oct_details) { %>
<tr>
<th>From client</th>
<% } %>
<% if (connection.send_oct_details) { %>
<th>To client</th>
<% } %>
<th>Channels</th>
<th>Virtual host</th>
<th>Username</th>
<th>Protocol</th>
<th>State</th>
<th>Timeout</th>
</tr>
<tr>
<% if (connection.recv_oct_details) { %>
<td>
<span class="mini-highlight"><%= fmt_bytes(connection.recv_oct_details.rate) %>/s</span>
</td>
</tr>
<% } %>
<% if (connection.send_oct_details) { %>
<tr>
<th>To client</th>
<td>
<span class="mini-highlight"><%= fmt_bytes(connection.send_oct_details.rate) %>/s</span>
</td>
</tr>
<% } %>
<tr><th>Channels</th><td><%= connection.channels %></td></tr>
<tr><th>Virtual host</th><td><%= link_vhost(connection.vhost) %></td></tr>
<tr><th>Username</th><td><%= link_user(connection.user) %></td></tr>
<tr><th>Protocol</th><td><%= connection.protocol %></td></tr>
<tr><th>State</th><td><%= connection.state %></td></tr>
<tr><th>Timeout</th><td><%= connection.timeout %></td></tr>
<td><%= connection.channels %></td>
<td><%= link_vhost(connection.vhost) %></td>
<td><%= link_user(connection.user) %></td>
<td><%= connection.protocol %></td>
<td><%= connection.state %></td>
<td><%= connection.timeout %></td>
</tr>
</table>
</div>
</div>

View File

@ -1,21 +1,61 @@
<h1>Exchange: <b><%= fmt_exchange(exchange.name) %></b></h1>
<div class="section">
<h2>Overview</h2>
<h2>Details</h2>
<div class="hider">
<table class="facts">
<tr><th>Virtual host</th><td><%= exchange.vhost %></td></tr>
<tr><th>Type</th><td><%= exchange.type %></td></tr>
<tr><th>Durable</th><td><%= fmt_boolean(exchange.durable) %></td></tr>
<tr><th>Auto delete</th><td><%= fmt_boolean(exchange.auto_delete) %></td></tr>
<tr>
<th>Virtual host</th>
<th>Type</th>
<th>Durable</th>
<th>Auto delete</th>
<th>Arguments</th>
</tr>
<tr>
<td><%= exchange.vhost %></td>
<td><%= exchange.type %></td>
<td><%= fmt_boolean(exchange.durable) %></td>
<td><%= fmt_boolean(exchange.auto_delete) %></td>
<td><%= fmt_table_short(exchange.arguments) %></td>
</tr>
</table>
</div>
</div>
<% if (statistics_level == 'fine') { %>
<div class="section-hidden">
<h2>Message Rates</h2>
<div class="hider updatable">
<p>
The incoming rate is the rate at which messages are published
directly to this exchange. The outgoing rate is the rate at which
messages enter queues, having been published directly to this
exchange.
</p>
<table class="two-col-layout">
<tr>
<td>
<%= format('msg-detail-publishes',
{'mode': 'exchange-incoming',
'object': exchange.incoming,
'label': 'Incoming',
'totals': exchange.message_stats_in}) %>
</td>
<td>
<%= format('msg-detail-publishes',
{'mode': 'exchange-outgoing',
'object': exchange.outgoing,
'label': 'Outgoing',
'totals': exchange.message_stats_out}) %>
</td>
</tr>
</table>
<span class="br"></span>
</div>
</div>
<% } %>
<div class="section">
<h2>Bindings</h2>
<div class="hider updatable">

View File

@ -5,13 +5,21 @@
<%= maybe_truncate(exchanges) %>
<table class="list">
<thead>
<tr>
<th colspan="4">Overview</th>
<% if (statistics_level == 'fine') { %>
<th colspan="2">Message rates</th>
<% } %>
</tr>
<tr>
<th><%= fmt_sort('Virtual host', 'vhost') %></th>
<th><%= fmt_sort('Name', 'name') %></th>
<th><%= fmt_sort('Type', 'type') %></th>
<th><%= fmt_sort('Durable', 'durable') %></th>
<th><%= fmt_sort('Auto delete', 'auto_delete') %></th>
<th>Arguments</th>
<th>Parameters</th>
<% if (statistics_level == 'fine') { %>
<th><%= fmt_sort('incoming', 'message_stats_in.publish_details.rate') %></th>
<th><%= fmt_sort('outgoing', 'message_stats_out.publish_details.rate') %></th>
<% } %>
</tr>
</thead>
<tbody>
@ -22,10 +30,10 @@
<tr<%= alt_rows(i)%>>
<td><%= link_vhost(exchange.vhost) %></td>
<td><%= link_exchange(exchange.vhost, exchange.name) %></td>
<td><%= exchange.type %></td>
<td class="c"><%= fmt_boolean(exchange.durable) %></td>
<td class="c"><%= fmt_boolean(exchange.auto_delete) %></td>
<td><%= fmt_table_short(exchange.arguments) %></td>
<td class="c"><%= exchange.type %></td>
<td class="c"><%= fmt_parameters(exchange) %></td>
<td class="r"><%= fmt_rate(exchange.message_stats_in, 'publish', false) %></td>
<td class="r"><%= fmt_rate(exchange.message_stats_out, 'publish', false) %></td>
</tr>
<% } %>
</tbody>

View File

@ -0,0 +1,35 @@
<h3>Deliveries</h3>
<% if (object && object.length > 0) { %>
<table class="list list-with-total">
<tr>
<% if (mode == 'queue') { %>
<th>Channel</th>
<% } else { %>
<th>Queue</th>
<% } %>
<th>deliver / get</th>
<th>ack</th>
</tr>
<%
for (var i = 0; i < object.length; i++) {
var del = object[i];
%>
<tr<%= alt_rows(i)%>>
<% if (mode == 'queue') { %>
<td><%= link_channel(del.channel_details.name) %></td>
<% } else { %>
<td><%= link_queue(del.queue_details.vhost, del.queue_details.name) %></td>
<% } %>
<td class="r"><%= fmt_rate(del.stats, 'deliver_get') %></td>
<td class="r"><%= fmt_rate(del.stats, 'ack') %></td>
</tr>
<% } %>
<tr class="total">
<th>Total:</th>
<td><span class="mini-highlight"><%= fmt_rate(totals, 'deliver_get') %></span></td>
<td><span class="mini-highlight"><%= fmt_rate(totals, 'ack') %></span></td>
</tr>
</table>
<% } else { %>
<p> ... no deliveries ...</p>
<% } %>

View File

@ -0,0 +1,41 @@
<h3><%= label %></h3>
<% if (object && object.length > 0) { %>
<table class="list list-with-total">
<tr>
<% if (mode == 'channel') { %>
<th>Exchange</th>
<% } else if (mode == 'exchange-incoming') { %>
<th>Channel</th>
<% } else if (mode == 'exchange-outgoing') { %>
<th>Queue</th>
<% } else { %>
<th>Exchange</th>
<% } %>
<th>publish</th>
</tr>
<%
for (var i = 0; i < object.length; i++) {
var pub = object[i];
%>
<tr<%= alt_rows(i)%>>
<% if (mode == 'channel') { %>
<td><%= link_exchange(pub.exchange.vhost, pub.exchange.name) %></td>
<% } else if (mode == 'exchange-incoming') { %>
<td><%= link_channel(pub.channel_details.name) %></td>
<% } else if (mode == 'exchange-outgoing') { %>
<td><%= link_queue(pub.queue_details.vhost, pub.queue_details.name) %></td>
<% } else { %>
<td><%= link_exchange(pub.exchange.vhost, pub.exchange.name) %></td>
<% } %>
<td class="r"><%= fmt_rate(pub.stats, 'publish') %></td>
</tr>
<% } %>
<tr class="total">
<th>Total:</th>
<td><span class="mini-highlight"><%= fmt_rate(totals, 'publish') %></span></td>
</tr>
</table>
<% } else { %>
<p> ... no <%= label.toLowerCase() %> ...</p>
<% } %>

View File

@ -13,7 +13,7 @@
<div class="section">
<h2>System Information</h2>
<div class="hider updatable">
<table class="facts">
<table class="facts-long">
<tr>
<th>Erlang Nodename</th>
<td><%= overview.node %></td>

View File

@ -1,53 +1,68 @@
<h1>Queue <b><%= queue.name %></b></h1>
<div class="section">
<h2>Overview</h2>
<h2>Details</h2>
<div class="hider updatable">
<table class="facts">
<tr>
<th>Virtual host</th>
<td><%= queue.vhost %></td>
</tr>
<tr>
<th>Durable</th>
<td><%= fmt_boolean(queue.durable) %></td>
</tr>
<tr>
<th>Auto delete</th>
<td><%= fmt_boolean(queue.auto_delete) %></td>
<th>Exclusive owner</th>
<th>Arguments</th>
<th>Consumers</th>
<th>Memory</th>
<th>Messages</th>
</tr>
<tr>
<th>Exclusive owner</th>
<td><%= queue.vhost %></td>
<td><%= fmt_boolean(queue.durable) %></td>
<td><%= fmt_boolean(queue.auto_delete) %></td>
<td>
<% if (queue.owner_pid != 'none') { %>
<%= link_conn(queue.owner_pid_details.name) %>
<% } %>
</td>
</tr>
<tr>
<th>Consumers</th>
<td><%= fmt_table_short(queue.arguments) %></td>
<td><%= fmt_string(queue.consumers) %></td>
</tr>
<tr>
<th>Memory</th>
<td><%= fmt_bytes(queue.memory) %></td>
</tr>
<tr>
<th>Messages</th>
<td>
<td class="l">
<%= fmt_string(queue.messages_ready) %> ready<br/>
<%= fmt_string(queue.messages_unacknowledged) %> unacknowledged<br/>
<%= fmt_string(queue.messages) %> total
</td>
</tr>
<tr>
<th>Arguments</th>
<td><%= fmt_table_short(queue.arguments) %></td>
</tr>
</table>
</div>
</div>
<% if (statistics_level == 'fine') { %>
<div class="section-hidden">
<h2>Message Rates</h2>
<div class="hider updatable">
<table class="two-col-layout">
<tr>
<td>
<%= format('msg-detail-publishes',
{'mode': 'queue',
'object': queue.incoming,
'label': 'Incoming',
'totals': queue.message_stats}) %>
</td>
<td>
<%= format('msg-detail-deliveries',
{'mode': 'queue',
'object': queue.deliveries,
'totals': queue.message_stats}) %>
</td>
</tr>
</table>
<span class="br"></span>
</div>
</div>
<% } %>
<div class="section">
<h2>Bindings</h2>
<div class="hider updatable">

View File

@ -7,20 +7,25 @@
<table class="list">
<thead>
<tr>
<th colspan="2">Overview</th>
<th colspan="4">Overview</th>
<th colspan="3">Messages</th>
<th colspan="4">Parameters</th>
<% if (statistics_level == 'fine') { %>
<th colspan="3">Message rates</th>
<% } %>
</tr>
<tr>
<th><%= fmt_sort('Virtual host', 'vhost') %></th>
<th><%= fmt_sort('Name', 'name') %></th>
<th><%= fmt_sort('Exclusive', 'owner_pid_details.name') %></th>
<th>Parameters</th>
<th><%= fmt_sort('Ready', 'messages_ready') %></th>
<th><%= fmt_sort('Unacked', 'messages_unacknowledged') %></th>
<th><%= fmt_sort('Total', 'messages') %></th>
<th><%= fmt_sort('Durable', 'durable') %></th>
<th><%= fmt_sort('Auto delete', 'auto_delete') %></th>
<th><%= fmt_sort('Exclusive', 'owner_pid_details.name') %></th>
<th>Arguments</th>
<% if (statistics_level == 'fine') { %>
<th><%= fmt_sort('incoming', 'message_stats.publish_details.rate') %></th>
<th><%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %></th>
<th><%= fmt_sort('ack', 'message_stats.ack_details.rate') %></th>
<% } %>
</tr>
</thead>
<tbody>
@ -31,17 +36,22 @@
<tr<%= alt_rows(i) %>>
<td><%= link_vhost(queue.vhost) %></td>
<td><%= link_queue(queue.vhost, queue.name) %></td>
<td class="r"><%= fmt_string(queue.messages_ready) %></td>
<td class="r"><%= fmt_string(queue.messages_unacknowledged) %></td>
<td class="r"><%= fmt_string(queue.messages) %></td>
<td class="c"><%= fmt_boolean(queue.durable) %></td>
<td class="c"><%= fmt_boolean(queue.auto_delete) %></td>
<td class="c">
<td>
<% if (queue.owner_pid != 'none') { %>
<%= link_conn(queue.owner_pid_details.name) %>
<% } %>
</td>
<td><%= fmt_table_short(queue.arguments) %></td>
<td class="c">
<%= fmt_parameters(queue) %>
</td>
<td class="r"><%= fmt_string(queue.messages_ready) %></td>
<td class="r"><%= fmt_string(queue.messages_unacknowledged) %></td>
<td class="r"><%= fmt_string(queue.messages) %></td>
<% if (statistics_level == 'fine') { %>
<td class="r"><%= fmt_rate(queue.message_stats, 'publish', false) %></td>
<td class="r"><%= fmt_rate(queue.message_stats, 'deliver_get', false) %></td>
<td class="r"><%= fmt_rate(queue.message_stats, 'ack', false) %></td>
<% } %>
</tr>
<% } %>
</tbody>

View File

@ -1,4 +1,4 @@
<table class="facts">
<table class="facts-long">
<% for (var key in table) { %>
<tr><th><%= key %></th><td><%= table[key] %></td></tr>
<% } %>

View File

@ -28,11 +28,11 @@
-export([event/1]).
-export([get_queues/1, get_connections/0, get_connection/1,
-export([get_queues/1, get_queue/1, get_exchanges/1, get_exchange/1,
get_connections/0, get_connection/1,
get_overview/0, get_channels/0, get_channel/1]).
-export([group_sum/2]).
%% TODO can these not be exported any more?
-export([pget/2, add/2, rates/5]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
@ -47,6 +47,44 @@
-define(DELIVER_GET, [deliver, deliver_no_ack, get, get_no_ack]).
-define(FINE_STATS, [publish, ack, deliver_get] ++ ?DELIVER_GET).
-define(
FINE_STATS_CHANNEL_LIST,
[{channel_queue_stats, [channel], message_stats, channel},
{channel_exchange_stats,[channel], message_stats, channel}]).
-define(
FINE_STATS_CHANNEL_DETAIL,
[{channel_queue_stats, [channel], message_stats, channel},
{channel_exchange_stats, [channel], message_stats, channel},
{channel_exchange_stats, [channel, exchange], publishes, channel},
{channel_queue_stats, [channel, queue], deliveries, channel}]).
-define(
FINE_STATS_QUEUE_LIST,
[{channel_queue_stats, [queue], message_stats, queue},
{channel_queue_exchange_stats, [queue], message_stats, queue}]).
-define(
FINE_STATS_QUEUE_DETAIL,
[{channel_queue_stats, [queue], message_stats, queue},
{channel_queue_exchange_stats, [queue], message_stats, queue},
{channel_queue_stats, [queue, channel], deliveries, queue},
{channel_queue_exchange_stats, [queue, exchange], incoming, queue}]).
-define(
FINE_STATS_EXCHANGE_LIST,
[{channel_exchange_stats, [exchange], message_stats_in, exchange},
{channel_queue_exchange_stats, [exchange], message_stats_out, exchange}]).
-define(
FINE_STATS_EXCHANGE_DETAIL,
[{channel_exchange_stats, [exchange], message_stats_in, exchange},
{channel_queue_exchange_stats, [exchange], message_stats_out, exchange},
{channel_exchange_stats, [exchange, channel], incoming, exchange},
{channel_queue_exchange_stats, [exchange, queue], outgoing, exchange}]).
-define(FINE_STATS_NONE, []).
%%----------------------------------------------------------------------------
start_link() ->
@ -76,7 +114,16 @@ event(Event) ->
gen_server:cast(?MODULE, {event, Event}).
get_queues(Qs) ->
gen_server:call(?MODULE, {get_queues, Qs}, infinity).
gen_server:call(?MODULE, {get_queues, Qs, list}, infinity).
get_queue(Q) ->
gen_server:call(?MODULE, {get_queues, [Q], detail}, infinity).
get_exchanges(Xs) ->
gen_server:call(?MODULE, {get_exchanges, Xs, list}, infinity).
get_exchange(X) ->
gen_server:call(?MODULE, {get_exchanges, [X], detail}, infinity).
get_connections() ->
gen_server:call(?MODULE, get_connections, infinity).
@ -119,12 +166,6 @@ lookup_element(Table, Key, Pos) ->
catch error:badarg -> []
end.
name_to_id(Table, Name) ->
case ets:match(Table, {{'$1', create}, '_', Name}) of
[] -> none;
[[Id]] -> Id
end.
result_or_error([]) -> error;
result_or_error(S) -> S.
@ -144,21 +185,21 @@ rate(Stats, Timestamp, OldStats, OldTimestamp, Key) ->
{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)]).
%% List = [{ [{channel, Pid}, ...], [{deliver, 123}, ...] } ...]
group_sum([], List) ->
lists:foldl(fun ({_, Item1}, Item0) ->
gs_update(Item0, Item1)
end, [], List);
group_sum(GroupBy, List) ->
lists:foldl(fun ({Ids, Item0}, Acc) ->
Id = [{G, pget(G, Ids)} || G <- GroupBy],
dict:update(Id, fun(Item1) ->
gs_update(Item0, Item1)
end,
Item0, Acc)
end, dict:new(), List).
group_sum([Group | Groups], List) ->
D = lists:foldl(
fun (Next = {Ids, _}, Dict) ->
Id = {Group, pget(Group, Ids)},
dict:update(Id, fun(Cur) -> [Next | Cur] end, [Next], Dict)
end, dict:new(), List),
dict:map(fun(_, SubList) ->
group_sum(Groups, SubList)
end, D).
gs_update(Item0, Item1) ->
Keys = sets:to_list(sets:from_list(
@ -197,17 +238,18 @@ augment(K, Items, Fun, Tables) ->
Id -> {Key, Fun(Id, Tables)}
end.
%% augment_channel_pid(Pid, Tables) ->
%% Ch = lookup_element(
%% orddict:fetch(channel_stats, Tables),
%% {Pid, create}),
%% Conn = lookup_element(
%% orddict:fetch(connection_stats, Tables),
%% {pget(connection, Ch), create}),
%% [{number, pget(number, Ch)},
%% {connection_name, pget(name, Conn)},
%% {peer_address, pget(peer_address, Conn)},
%% {peer_port, pget(peer_port, Conn)}].
augment_channel_pid(Pid, Tables) ->
Ch = lookup_element(
orddict:fetch(channel_stats, Tables),
{Pid, create}),
Conn = lookup_element(
orddict:fetch(connection_stats, Tables),
{pget(connection, Ch), create}),
[{number, pget(number, Ch)},
{name, pget(name, Ch)},
{connection_name, pget(name, Conn)},
{peer_address, pget(peer_address, Conn)},
{peer_port, pget(peer_port, Conn)}].
augment_connection_pid(Pid, Tables) ->
Conn = lookup_element(orddict:fetch(connection_stats, Tables),
@ -216,73 +258,75 @@ augment_connection_pid(Pid, Tables) ->
{peer_port, pget(peer_port, Conn)},
{name, pget(name, Conn)}].
%% augment_queue_pid(Pid, Tables) ->
%% Q = lookup_element(
%% orddict:fetch(queue_stats, Tables), NB this won't work any more
%% {Pid, create}),
%% [{name, pget(name, Q)},
%% {vhost, pget(vhost, Q)}].
augment_queue_pid(Pid, _Tables) ->
%% TODO This should be in rabbit_amqqueue?
[Q] = mnesia:dirty_match_object(
rabbit_queue,
#amqqueue{pid = rabbit_misc:string_to_pid(Pid), _ = '_'}),
Name = Q#amqqueue.name,
[{name, Name#resource.name},
{vhost, Name#resource.virtual_host}].
augment_msg_stats(Stats, Tables) ->
[augment_msg_stats_items(Props, Tables) || Props <- Stats].
augment_msg_stats_items(Props, Tables) ->
augment(Props, [{connection, fun augment_connection_pid/2}], Tables).
augment(Props, [{connection, fun augment_connection_pid/2},
{channel, fun augment_channel_pid/2},
{queue, fun augment_queue_pid/2}], Tables).
%%----------------------------------------------------------------------------
%% TODO some sort of generalised query mechanism for the coarse stats?
init([]) ->
rabbit_mgmt_db_handler:add_handler(),
{ok, #state{tables = orddict:from_list(
[{Key, ets:new(anon, [private])} ||
Key <- ?TABLES])}}.
handle_call({get_queues, Qs0}, _From, State = #state{tables = Tables}) ->
Table = orddict:fetch(queue_stats, Tables),
Qs1 = merge_created_stats(Qs0, Table),
handle_call({get_queues, Qs0, Mode}, _From, State = #state{tables = Tables}) ->
FineStats = case Mode of
list -> ?FINE_STATS_QUEUE_LIST;
detail -> ?FINE_STATS_QUEUE_DETAIL
end,
Qs1 = merge_stats(Qs0, FineStats, queue_stats, Tables),
Qs2 = [[{messages, add(pget(messages_ready, Q),
pget(messages_unacknowledged, Q))} | Q] || Q <- Qs1],
Qs3 = [augment(Q, [{owner_pid, fun augment_connection_pid/2}], Tables) ||
Q <- Qs2],
{reply, Qs3, State};
handle_call(get_connections, _From, State = #state{tables = Tables}) ->
Table = orddict:fetch(connection_stats, Tables),
{reply, [zero_old_rates(S) || S <- merge_created_stats(Table)], State};
handle_call({get_connection, Name}, _From, State = #state{tables = Tables}) ->
Table = orddict:fetch(connection_stats, Tables),
Id = name_to_id(Table, Name),
{reply, result_or_error(lookup_element(Table, {Id, create}) ++
zero_old_rates(
lookup_element(Table, {Id, stats}))), State};
handle_call(get_channels, _From, 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),
{reply, augment_msg_stats(merge_fine_stats(Stats, [FineQ, FineX]), Tables),
handle_call({get_exchanges, Xs, Mode}, _From,
State = #state{tables = Tables}) ->
FineStats = case Mode of
list -> ?FINE_STATS_EXCHANGE_LIST;
detail -> ?FINE_STATS_EXCHANGE_DETAIL
end,
{reply, merge_stats(Xs, FineStats, none, Tables),
State};
handle_call(get_connections, _From, State = #state{tables = Tables}) ->
Conns = created_events(connection_stats, Tables),
{reply, merge_stats(Conns, ?FINE_STATS_NONE, connection_stats, Tables),
State};
handle_call({get_connection, Name}, _From, State = #state{tables = Tables}) ->
Conns = created_event(Name, connection_stats, Tables),
[Res] = merge_stats(Conns, ?FINE_STATS_NONE, connection_stats, Tables),
{reply, result_or_error(Res), State};
handle_call(get_channels, _From, State = #state{tables = Tables}) ->
Chs = created_events(channel_stats, Tables),
Res = merge_stats(Chs, ?FINE_STATS_CHANNEL_LIST, channel_stats, Tables),
{reply, Res, State};
handle_call({get_channel, Name}, _From, State = #state{tables = Tables}) ->
Table = orddict:fetch(channel_stats, Tables),
Id = name_to_id(Table, Name),
Chs = [lookup_element(Table, {Id, create})],
%% TODO extract commonality between this and get_channels
Stats = merge_created_stats(Chs, Table),
FineQ = get_fine_stats(channel_queue_stats, [channel], Tables),
FineX = get_fine_stats(channel_exchange_stats, [channel], Tables),
[Res] = augment_msg_stats(merge_fine_stats(Stats, [FineQ, FineX]), Tables),
Chs = created_event(Name, channel_stats, Tables),
[Res] = merge_stats(Chs, ?FINE_STATS_CHANNEL_DETAIL, channel_stats, Tables),
{reply, result_or_error(Res), State};
handle_call(get_overview, _From, State = #state{tables = Tables}) ->
FineQ = extract_singleton_fine_stats(
get_fine_stats(channel_queue_stats, [], Tables)),
FineX = extract_singleton_fine_stats(
get_fine_stats(channel_exchange_stats, [], Tables)),
FineQ = get_fine_stats(channel_queue_stats, [], Tables),
FineX = get_fine_stats(channel_exchange_stats, [], Tables),
{reply, [{message_stats, FineX ++ FineQ}], State};
handle_call(_Request, _From, State) ->
@ -435,13 +479,17 @@ fine_stats_key(ChPid, {QPid, X}) -> {ChPid, id(QPid), X};
fine_stats_key(ChPid, QPid) when is_pid(QPid) -> {ChPid, id(QPid)};
fine_stats_key(ChPid, X) -> {ChPid, X}.
merge_created_stats(Table) ->
merge_created_stats(
[Facts || {{_, create}, Facts, _Name} <- ets:tab2list(Table)],
Table).
created_event(Name, Type, Tables) ->
Table = orddict:fetch(Type, Tables),
Id = case ets:match(Table, {{'$1', create}, '_', Name}) of
[] -> none;
[[I]] -> I
end,
[lookup_element(Table, {Id, create})].
merge_created_stats(In, Table) ->
[Facts ++ lookup_element(Table, {pget(pid, Facts), stats}) || Facts <- In].
created_events(Type, Tables) ->
[Facts || {{_, create}, Facts, _Name}
<- ets:tab2list(orddict:fetch(Type, Tables))].
get_fine_stats(Type, GroupBy, Tables) ->
Table = orddict:fetch(Type, Tables),
@ -457,25 +505,50 @@ format_id({ChPid, QPid, #resource{name=XName, virtual_host=XVhost}}) ->
[{channel, ChPid}, {queue, QPid},
{exchange, [{name, XName}, {vhost, XVhost}]}].
merge_fine_stats(Stats, []) ->
Stats;
merge_fine_stats(Stats, [Dict | Dicts]) ->
merge_fine_stats([merge_fine_stats0(Props, Dict) || Props <- Stats], Dicts).
merge_stats(Objs, FineSpecs, Type, Tables) ->
WithCoarse =
case Type of
none ->
Objs;
_ ->
Table = orddict:fetch(Type, Tables),
[Obj ++ zero_old_rates(
lookup_element(Table, {pget(pid, Obj), stats}))
|| Obj <- Objs]
end,
FineStats = [{AttachName, AttachBy,
get_fine_stats(FineStatsType, GroupBy, Tables)}
|| {FineStatsType, GroupBy, AttachName, AttachBy}
<- FineSpecs],
augment_msg_stats(merge_fine_stats(WithCoarse, FineStats, Tables), Tables).
merge_fine_stats0(Props, Dict) ->
Id = pget(pid, Props),
case dict:find([{channel, Id}], Dict) of
{ok, Stats} -> [{message_stats, pget(message_stats, Props, []) ++
Stats} |
proplists:delete(message_stats, Props)];
merge_fine_stats(Stats, [], _Tables) ->
Stats;
merge_fine_stats(Stats, [{AttachName, AttachBy, Dict} | Rest], Tables) ->
merge_fine_stats([merge_fine_stats0(AttachName, AttachBy,
Props, Dict, Tables)
|| Props <- Stats], Rest, Tables).
merge_fine_stats0(AttachName, AttachBy, Props, Dict, Tables) ->
Id = case AttachBy of
exchange ->
[{name, pget(name, Props)}, {vhost, pget(vhost, Props)}];
_ ->
pget(pid, Props)
end,
case dict:find({AttachBy, Id}, Dict) of
{ok, Stats} -> [{AttachName, pget(AttachName, Props, []) ++
augment_fine_stats(Stats, Tables)} |
proplists:delete(AttachName, Props)];
error -> Props
end.
extract_singleton_fine_stats(Dict) ->
case dict:to_list(Dict) of
[] -> [];
[{[], Stats}] -> Stats
end.
augment_fine_stats(Dict, Tables) when element(1, Dict) == dict ->
[[{stats, augment_fine_stats(Stats, Tables)} |
augment_msg_stats_items([IdTuple], Tables)]
|| {IdTuple, Stats} <- dict:to_list(Dict)];
augment_fine_stats(Stats, _Tables) ->
Stats.
zero_old_rates(Stats) -> [maybe_zero_rate(S) || S <- Stats].

View File

@ -48,9 +48,9 @@ resource_exists(ReqData, Context) ->
end, ReqData, Context}.
to_json(ReqData, Context) ->
rabbit_mgmt_util:reply(
rabbit_mgmt_format:exchange(rabbit_exchange:info(exchange(ReqData))),
ReqData, Context).
X0 = exchange(ReqData),
[X] = rabbit_mgmt_db:get_exchange(X0),
rabbit_mgmt_util:reply(X, ReqData, Context).
accept_content(ReqData, Context) ->
Name = rabbit_mgmt_util:id(exchange, ReqData),
@ -83,7 +83,8 @@ exchange(ReqData) ->
not_found -> not_found;
VHost -> Name = rabbit_misc:r(VHost, exchange, id(ReqData)),
case rabbit_exchange:lookup(Name) of
{ok, X} -> X;
{ok, X} -> rabbit_mgmt_format:exchange(
rabbit_exchange:info(X));
{error, not_found} -> not_found
end
end.

View File

@ -41,7 +41,7 @@ resource_exists(ReqData, Context) ->
end, ReqData, Context}.
to_json(ReqData, Context) ->
Xs = exchanges(ReqData),
Xs = rabbit_mgmt_db:get_exchanges(exchanges(ReqData)),
rabbit_mgmt_util:reply_list(
rabbit_mgmt_util:filter_vhost(Xs, ReqData, Context),
ReqData, Context).

View File

@ -49,7 +49,7 @@ resource_exists(ReqData, Context) ->
to_json(ReqData, Context) ->
Q0 = queue(ReqData),
[Q] = rabbit_mgmt_db:get_queues([Q0]),
[Q] = rabbit_mgmt_db:get_queue(Q0),
rabbit_mgmt_util:reply(Q, ReqData, Context).
accept_content(ReqData, Context) ->