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

+
+ + + + + + + + + + + + + + + + + + + + + +
ConnectionVirtual hostUsernameTransactionalPrefetch countAcks uncommittedMessages unacknowledgedClient 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

-
- - - - - - - - -<% if ('client_flow_blocked' in channel) { %> - -<% } %> -
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 %>
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) { %> - +<% } %> +<% if (connection.send_oct_details) { %> + +<% } %> + + + + + + + + +<% if (connection.recv_oct_details) { %> - <% } %> <% if (connection.send_oct_details) { %> - - - <% } %> - - - - - - + + + + + + +
From clientTo clientChannelsVirtual hostUsernameProtocolStateTimeout
<%= fmt_bytes(connection.recv_oct_details.rate) %>/s
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 hostTypeDurableAuto 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) %> + + +<% if (statistics_level == 'fine') { %> + +<% } %> + - - - + +<% if (statistics_level == 'fine') { %> + + +<% } %> @@ -22,10 +30,10 @@ > - - - - + + + + <% } %> 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) { %> +
OverviewMessage rates
<%= fmt_sort('Virtual host', 'vhost') %> <%= fmt_sort('Name', 'name') %> <%= fmt_sort('Type', 'type') %><%= fmt_sort('Durable', 'durable') %><%= fmt_sort('Auto delete', 'auto_delete') %>ArgumentsParameters<%= fmt_sort('incoming', 'message_stats_in.publish_details.rate') %><%= fmt_sort('outgoing', 'message_stats_out.publish_details.rate') %>
<%= 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) %>
+ +<% if (mode == 'queue') { %> + +<% } else { %> + +<% } %> + + + +<% + for (var i = 0; i < object.length; i++) { + var del = object[i]; +%> + > +<% if (mode == 'queue') { %> + +<% } else { %> + +<% } %> + + + +<% } %> + + + + + +
ChannelQueuedeliver / getack
<%= link_channel(del.channel_details.name) %><%= 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') { %> + +<% } else if (mode == 'exchange-incoming') { %> + +<% } else if (mode == 'exchange-outgoing') { %> + +<% } else { %> + +<% } %> + + +<% + for (var i = 0; i < object.length; i++) { + var pub = object[i]; +%> + > + +<% if (mode == 'channel') { %> + +<% } else if (mode == 'exchange-incoming') { %> + +<% } else if (mode == 'exchange-outgoing') { %> + +<% } else { %> + +<% } %> + + +<% } %> + + + + +
ExchangeChannelQueueExchangepublish
<%= link_exchange(pub.exchange.vhost, pub.exchange.name) %><%= link_channel(pub.channel_details.name) %><%= link_queue(pub.queue_details.vhost, pub.queue_details.name) %><%= 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

- +
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

Erlang Nodename <%= overview.node %>
- - - - - - - + + + + + - + + + - - - + - - - - - - - - - - -
Virtual host<%= queue.vhost %>
Durable<%= fmt_boolean(queue.durable) %>
Auto delete<%= fmt_boolean(queue.auto_delete) %>Exclusive ownerArgumentsConsumersMemoryMessages
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 @@ - + - +<% if (statistics_level == 'fine') { %> + +<% } %> + + - - - - +<% if (statistics_level == 'fine') { %> + + + +<% } %> @@ -31,17 +36,22 @@ > - - - - - - - + + + + +<% if (statistics_level == 'fine') { %> + + + +<% } %> <% } %> 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 @@ -
OverviewOverview MessagesParametersMessage 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<%= 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') %>
<%= 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) %><%= fmt_rate(queue.message_stats, 'publish', false) %><%= fmt_rate(queue.message_stats, 'deliver_get', false) %><%= fmt_rate(queue.message_stats, 'ack', false) %>
+
<% for (var key in table) { %> <% } %> 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) ->
<%= key %><%= table[key] %>