diff --git a/deps/rabbitmq_management/priv/www/css/main.css b/deps/rabbitmq_management/priv/www/css/main.css
index c96ce0bc13..07af8ad9be 100644
--- a/deps/rabbitmq_management/priv/www/css/main.css
+++ b/deps/rabbitmq_management/priv/www/css/main.css
@@ -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%; }
diff --git a/deps/rabbitmq_management/priv/www/js/formatters.js b/deps/rabbitmq_management/priv/www/js/formatters.js
index 255b019389..1c7712966f 100644
--- a/deps/rabbitmq_management/priv/www/js/formatters.js
+++ b/deps/rabbitmq_management/priv/www/js/formatters.js
@@ -34,6 +34,21 @@ function fmt_boolean(b) {
return b ? "●" : "○";
}
+function fmt_parameters(obj) {
+ var res = '';
+ if (obj.durable) {
+ res += 'D ';
+ }
+ if (obj.auto_delete) {
+ res += 'AD ';
+ }
+ var args = fmt_table_short(obj.arguments);
+ if (args != '') {
+ res += '
' + args + '
';
+ }
+ 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 = '(' + fmt(obj[name]) + ' total)';
+ 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 += '(' + fmt(obj[name]) + ' total)';
}
return res;
}
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/channel.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/channel.ejs
index d20d12abb8..15df24bd34 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/channel.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/channel.ejs
@@ -1,29 +1,55 @@
Channel: <%= channel.name %>
+
+
Details
+
+
+
+ | Connection |
+ Virtual host |
+ Username |
+ Transactional |
+ Prefetch count |
+ Acks uncommitted |
+ Messages unacknowledged |
+ Client blocked |
+
+
+ | <%= link_conn(channel.connection_details.name) %> |
+ <%= channel.vhost %> |
+ <%= channel.user %> |
+ <%= fmt_boolean(channel.transactional) %> |
+ <%= channel.prefetch_count %> |
+ <%= channel.acks_uncommitted %> |
+ <%= channel.messages_unacknowledged %> |
+ <%= fmt_boolean(channel.client_flow_blocked) %> |
+
+
+
+
+
<% if (statistics_level == 'fine') { %>
Message Rates
-<%= message_rates(channel.message_stats) %>
+
+
+ |
+ <%= format('msg-detail-publishes',
+ {'mode': 'channel',
+ 'object': channel.publishes,
+ 'label': 'Publishes',
+ 'totals': channel.message_stats}) %>
+ |
+
+ <%= format('msg-detail-deliveries',
+ {'mode': 'channel',
+ 'object': channel.deliveries,
+ 'totals': channel.message_stats}) %>
+ |
+
+
<% } %>
-
-
-
Overview
-
-
- | Connection | <%= link_conn(channel.connection_details.name) %> |
- | Virtual host | <%= channel.vhost %> |
- | Username | <%= channel.user %> |
- | Transactional | <%= fmt_boolean(channel.transactional) %> |
- | Prefetch count | <%= channel.prefetch_count %> |
- | Acks uncommitted | <%= channel.acks_uncommitted %> |
- | Messages unacknowledged | <%= channel.messages_unacknowledged %> |
-<% if ('client_flow_blocked' in channel) { %>
- | Client blocked | <%= fmt_boolean(channel.client_flow_blocked) %> |
-<% } %>
-
-
-
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/channels-list.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/channels-list.ejs
index 9e71c3d019..90ce045253 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/channels-list.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/channels-list.ejs
@@ -61,11 +61,9 @@
<%= channel.prefetch_count %> |
<%= channel.messages_unacknowledged %> |
<% if (statistics_level == 'fine') { %>
- <%= fmt_rate(channel.message_stats, 'publish') %> |
- <%= fmt_rate(channel.message_stats, 'deliver_get') %> |
-
- <%= fmt_rate(channel.message_stats, 'ack') %>
- |
+ <%= fmt_rate(channel.message_stats, 'publish') %> |
+ <%= fmt_rate(channel.message_stats, 'deliver_get') %> |
+ <%= fmt_rate(channel.message_stats, 'ack') %> |
<% } %>
<% } %>
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs
index 5a9521f8b0..d6d4c7e33b 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs
@@ -5,28 +5,38 @@
+
<% if (connection.recv_oct_details) { %>
-
| From client |
+<% } %>
+<% if (connection.send_oct_details) { %>
+ To client |
+<% } %>
+ Channels |
+ Virtual host |
+ Username |
+ Protocol |
+ State |
+ Timeout |
+
+
+<% if (connection.recv_oct_details) { %>
|
<%= fmt_bytes(connection.recv_oct_details.rate) %>/s
|
-
<% } %>
<% if (connection.send_oct_details) { %>
-
- | To client |
<%= fmt_bytes(connection.send_oct_details.rate) %>/s
|
-
<% } %>
- | Channels | <%= connection.channels %> |
- | Virtual host | <%= link_vhost(connection.vhost) %> |
- | Username | <%= link_user(connection.user) %> |
- | Protocol | <%= connection.protocol %> |
- | State | <%= connection.state %> |
- | Timeout | <%= connection.timeout %> |
+ <%= connection.channels %> |
+ <%= link_vhost(connection.vhost) %> |
+ <%= link_user(connection.user) %> |
+ <%= connection.protocol %> |
+ <%= connection.state %> |
+ <%= connection.timeout %> |
+
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/exchange.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/exchange.ejs
index bb65997d2d..fb9422ce1b 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/exchange.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/exchange.ejs
@@ -1,21 +1,61 @@
Exchange: <%= fmt_exchange(exchange.name) %>
-
Overview
+
Details
- | Virtual host | <%= exchange.vhost %> |
- | Type | <%= exchange.type %> |
- | Durable | <%= fmt_boolean(exchange.durable) %> |
- | Auto delete | <%= fmt_boolean(exchange.auto_delete) %> |
+ | Virtual host |
+ Type |
+ Durable |
+ Auto delete |
Arguments |
+
+
+ | <%= exchange.vhost %> |
+ <%= exchange.type %> |
+ <%= fmt_boolean(exchange.durable) %> |
+ <%= fmt_boolean(exchange.auto_delete) %> |
<%= fmt_table_short(exchange.arguments) %> |
+<% if (statistics_level == 'fine') { %>
+
+
Message Rates
+
+
+ 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.
+
+
+
+ |
+ <%= format('msg-detail-publishes',
+ {'mode': 'exchange-incoming',
+ 'object': exchange.incoming,
+ 'label': 'Incoming',
+ 'totals': exchange.message_stats_in}) %>
+ |
+
+ <%= format('msg-detail-publishes',
+ {'mode': 'exchange-outgoing',
+ 'object': exchange.outgoing,
+ 'label': 'Outgoing',
+ 'totals': exchange.message_stats_out}) %>
+ |
+
+
+
+
+
+<% } %>
+
+
Bindings
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/exchanges.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/exchanges.ejs
index 9b1d9f4a3e..7fa3d5c6a3 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/exchanges.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/exchanges.ejs
@@ -5,13 +5,21 @@
<%= maybe_truncate(exchanges) %>
+
+ | Overview |
+<% if (statistics_level == 'fine') { %>
+ Message rates |
+<% } %>
+
| <%= fmt_sort('Virtual host', 'vhost') %> |
<%= fmt_sort('Name', 'name') %> |
<%= fmt_sort('Type', 'type') %> |
- <%= fmt_sort('Durable', 'durable') %> |
- <%= fmt_sort('Auto delete', 'auto_delete') %> |
- Arguments |
+ Parameters |
+<% if (statistics_level == 'fine') { %>
+ <%= fmt_sort('incoming', 'message_stats_in.publish_details.rate') %> |
+ <%= fmt_sort('outgoing', 'message_stats_out.publish_details.rate') %> |
+<% } %>
@@ -22,10 +30,10 @@
>
| <%= link_vhost(exchange.vhost) %> |
<%= link_exchange(exchange.vhost, exchange.name) %> |
- <%= exchange.type %> |
- <%= fmt_boolean(exchange.durable) %> |
- <%= fmt_boolean(exchange.auto_delete) %> |
- <%= fmt_table_short(exchange.arguments) %> |
+ <%= exchange.type %> |
+ <%= fmt_parameters(exchange) %> |
+ <%= fmt_rate(exchange.message_stats_in, 'publish', false) %> |
+ <%= fmt_rate(exchange.message_stats_out, 'publish', false) %> |
<% } %>
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/msg-detail-deliveries.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/msg-detail-deliveries.ejs
new file mode 100644
index 0000000000..0fcf33618b
--- /dev/null
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/msg-detail-deliveries.ejs
@@ -0,0 +1,35 @@
+Deliveries
+<% if (object && object.length > 0) { %>
+
+
+<% if (mode == 'queue') { %>
+ | Channel |
+<% } else { %>
+ Queue |
+<% } %>
+ deliver / get |
+ ack |
+
+<%
+ for (var i = 0; i < object.length; i++) {
+ var del = object[i];
+%>
+ >
+<% if (mode == 'queue') { %>
+ | <%= link_channel(del.channel_details.name) %> |
+<% } else { %>
+ <%= link_queue(del.queue_details.vhost, del.queue_details.name) %> |
+<% } %>
+ <%= fmt_rate(del.stats, 'deliver_get') %> |
+ <%= fmt_rate(del.stats, 'ack') %> |
+
+<% } %>
+
+ | Total: |
+ <%= fmt_rate(totals, 'deliver_get') %> |
+ <%= fmt_rate(totals, 'ack') %> |
+
+
+<% } else { %>
+ ... no deliveries ...
+<% } %>
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/msg-detail-publishes.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/msg-detail-publishes.ejs
new file mode 100644
index 0000000000..0efa0861f8
--- /dev/null
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/msg-detail-publishes.ejs
@@ -0,0 +1,41 @@
+<%= label %>
+<% if (object && object.length > 0) { %>
+
+
+<% if (mode == 'channel') { %>
+ | Exchange |
+<% } else if (mode == 'exchange-incoming') { %>
+ Channel |
+<% } else if (mode == 'exchange-outgoing') { %>
+ Queue |
+<% } else { %>
+ Exchange |
+<% } %>
+ publish |
+
+<%
+ for (var i = 0; i < object.length; i++) {
+ var pub = object[i];
+%>
+ >
+
+<% if (mode == 'channel') { %>
+ | <%= link_exchange(pub.exchange.vhost, pub.exchange.name) %> |
+<% } else if (mode == 'exchange-incoming') { %>
+ <%= link_channel(pub.channel_details.name) %> |
+<% } else if (mode == 'exchange-outgoing') { %>
+ <%= link_queue(pub.queue_details.vhost, pub.queue_details.name) %> |
+<% } else { %>
+ <%= link_exchange(pub.exchange.vhost, pub.exchange.name) %> |
+<% } %>
+ <%= fmt_rate(pub.stats, 'publish') %> |
+
+<% } %>
+
+ | Total: |
+ <%= fmt_rate(totals, 'publish') %> |
+
+
+<% } else { %>
+ ... no <%= label.toLowerCase() %> ...
+<% } %>
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/overview.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/overview.ejs
index b29ff452dd..bce04da443 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/overview.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/overview.ejs
@@ -13,7 +13,7 @@
System Information
-
+
| Erlang Nodename |
<%= overview.node %> |
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs
index 9950ed9945..7efc13e48c 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs
@@ -1,53 +1,68 @@
Queue <%= queue.name %>
-
Overview
+
Details
| Virtual host |
- <%= queue.vhost %> |
-
-
| Durable |
- <%= fmt_boolean(queue.durable) %> |
-
-
| Auto delete |
- <%= fmt_boolean(queue.auto_delete) %> |
+ Exclusive owner |
+ Arguments |
+ Consumers |
+ Memory |
+ Messages |
- | Exclusive owner |
+ <%= queue.vhost %> |
+ <%= fmt_boolean(queue.durable) %> |
+ <%= fmt_boolean(queue.auto_delete) %> |
<% if (queue.owner_pid != 'none') { %>
<%= link_conn(queue.owner_pid_details.name) %>
<% } %>
|
-
-
- | Consumers |
+ <%= fmt_table_short(queue.arguments) %> |
<%= fmt_string(queue.consumers) %> |
-
-
- | Memory |
<%= fmt_bytes(queue.memory) %> |
-
-
- | Messages |
-
+ |
<%= fmt_string(queue.messages_ready) %> ready
<%= fmt_string(queue.messages_unacknowledged) %> unacknowledged
<%= fmt_string(queue.messages) %> total
|
-
- | Arguments |
- <%= fmt_table_short(queue.arguments) %> |
-
+<% if (statistics_level == 'fine') { %>
+
+
Message Rates
+
+
+
+ |
+ <%= format('msg-detail-publishes',
+ {'mode': 'queue',
+ 'object': queue.incoming,
+ 'label': 'Incoming',
+ 'totals': queue.message_stats}) %>
+
+ |
+
+ <%= format('msg-detail-deliveries',
+ {'mode': 'queue',
+ 'object': queue.deliveries,
+ 'totals': queue.message_stats}) %>
+ |
+
+
+
+
+
+<% } %>
+
Bindings
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs
index fe4d82828f..2da366f4a4 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs
@@ -7,20 +7,25 @@
- | Overview |
+ Overview |
Messages |
- Parameters |
+<% if (statistics_level == 'fine') { %>
+ Message rates |
+<% } %>
| <%= fmt_sort('Virtual host', 'vhost') %> |
<%= fmt_sort('Name', 'name') %> |
+ <%= fmt_sort('Exclusive', 'owner_pid_details.name') %> |
+ Parameters |
<%= fmt_sort('Ready', 'messages_ready') %> |
<%= fmt_sort('Unacked', 'messages_unacknowledged') %> |
<%= fmt_sort('Total', 'messages') %> |
- <%= fmt_sort('Durable', 'durable') %> |
- <%= fmt_sort('Auto delete', 'auto_delete') %> |
- <%= fmt_sort('Exclusive', 'owner_pid_details.name') %> |
- Arguments |
+<% if (statistics_level == 'fine') { %>
+ <%= fmt_sort('incoming', 'message_stats.publish_details.rate') %> |
+ <%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %> |
+ <%= fmt_sort('ack', 'message_stats.ack_details.rate') %> |
+<% } %>
@@ -31,17 +36,22 @@
>
| <%= link_vhost(queue.vhost) %> |
<%= link_queue(queue.vhost, queue.name) %> |
- <%= fmt_string(queue.messages_ready) %> |
- <%= fmt_string(queue.messages_unacknowledged) %> |
- <%= fmt_string(queue.messages) %> |
- <%= fmt_boolean(queue.durable) %> |
- <%= fmt_boolean(queue.auto_delete) %> |
-
+ |
<% if (queue.owner_pid != 'none') { %>
<%= link_conn(queue.owner_pid_details.name) %>
<% } %>
|
- <%= fmt_table_short(queue.arguments) %> |
+
+ <%= fmt_parameters(queue) %>
+ |
+ <%= fmt_string(queue.messages_ready) %> |
+ <%= fmt_string(queue.messages_unacknowledged) %> |
+ <%= fmt_string(queue.messages) %> |
+<% if (statistics_level == 'fine') { %>
+ <%= fmt_rate(queue.message_stats, 'publish', false) %> |
+ <%= fmt_rate(queue.message_stats, 'deliver_get', false) %> |
+ <%= fmt_rate(queue.message_stats, 'ack', false) %> |
+<% } %>
<% } %>
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs
index 4e37d77380..cd1a7c9343 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs
@@ -1,4 +1,4 @@
-
+
<% for (var key in table) { %>
| <%= key %> | <%= table[key] %> |
<% } %>
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl
index f860b1d5d1..3a69a3ee0f 100644
--- a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl
+++ b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl
@@ -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].
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchange.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchange.erl
index 3d11c03a61..9d2e5cba18 100644
--- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchange.erl
+++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchange.erl
@@ -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.
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchanges.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchanges.erl
index ff8b22c790..9806245cea 100644
--- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchanges.erl
+++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchanges.erl
@@ -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).
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_queue.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_queue.erl
index 6f85f9a677..163e3fe5fb 100644
--- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_queue.erl
+++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_queue.erl
@@ -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) ->