summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Klishin <klishinm@vmware.com>2021-11-24 21:08:36 +0300
committerGitHub <noreply@github.com>2021-11-24 21:08:36 +0300
commita20a0f14f83d0911ac036ba148184d01fd12b1fa (patch)
tree75c281942ccc295b3a0e9398359c3d1670dcd80f
parente22e667a103fcb1460896906fe97c7ea6c0ce460 (diff)
parent7676ed96857772ad812b4fc4fc9acd0df9df71d8 (diff)
downloadrabbitmq-server-git-a20a0f14f83d0911ac036ba148184d01fd12b1fa.tar.gz
Merge pull request #3779 from rabbitmq/vhost-exchange-count
Add optional metrics for vhost and exchange count
-rw-r--r--deps/rabbitmq_prometheus/metrics-detailed.md29
-rw-r--r--deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_core_metrics_collector.erl69
-rw-r--r--deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl58
3 files changed, 152 insertions, 4 deletions
diff --git a/deps/rabbitmq_prometheus/metrics-detailed.md b/deps/rabbitmq_prometheus/metrics-detailed.md
index 262fca04bb..bec732f8db 100644
--- a/deps/rabbitmq_prometheus/metrics-detailed.md
+++ b/deps/rabbitmq_prometheus/metrics-detailed.md
@@ -236,3 +236,32 @@ Group `channel_queue_exchange_metrics`:
| Metric | Description |
|--------------------------------------------------|----------------------------------------------|
| rabbitmq_detailed_queue_messages_published_total | Total number of messages published to queues |
+
+### Virtual hosts and exchange metrics
+
+These additional metrics can be useful when virtual hosts or exchanges are
+created on a shared cluster in a self-service way. They are different
+from the rest of the metrics: they are cluster-wide and not node-local.
+These metrics **must not** be aggregated across cluster nodes.
+
+Group `vhost_status`:
+
+| Metric | Description |
+|-------------------------------|----------------------------------|
+| rabbitmq_cluster_vhost_status | Whether a given vhost is running |
+
+Group `exchange_names`:
+
+| Metric | Description |
+|--------------------------------|----------------------------------------------------------------------------------------------------------------------------|
+| rabbitmq_cluster_exchange_name | Enumerates exchanges without any additional info. This value is cluster-wide. A cheaper alternative to `exchange_bindings` |
+
+Group `exchange_bindings`:
+
+| Metric | Description |
+|------------------------------------|-----------------------------------------------------------------|
+| rabbitmq_cluster_exchange_bindings | Number of bindings for an exchange. This value is cluster-wide. |
+
+
+
+
diff --git a/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_core_metrics_collector.erl b/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_core_metrics_collector.erl
index 328ec00276..7c31b71b92 100644
--- a/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_core_metrics_collector.erl
+++ b/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_core_metrics_collector.erl
@@ -29,6 +29,7 @@
%% Used by `/metrics/detailed` endpoint
-define(DETAILED_METRIC_NAME_PREFIX, <<"rabbitmq_detailed_">>).
+-define(CLUSTER_METRIC_NAME_PREFIX, <<"rabbitmq_cluster_">>).
%% ==The source of these metrics can be found in the rabbit_core_metrics module==
%% The relevant files are:
@@ -214,6 +215,19 @@
]}
]).
+%% Metrics that can be only requested through `/metrics/detailed`
+-define(METRICS_CLUSTER,[
+ {vhost_status, [
+ {2, undefined, vhost_status, gauge, "Whether a given vhost is running"}
+ ]},
+ {exchange_bindings, [
+ {2, undefined, exchange_bindings, gauge, "Number of bindings for an exchange. This value is cluster-wide."}
+ ]},
+ {exchange_names, [
+ {2, undefined, exchange_name, gauge, "Enumerates exchanges without any additional info. This value is cluster-wide. A cheaper alternative to `exchange_bindings`"}
+ ]}
+]).
+
-define(TOTALS, [
%% ordering differs from metrics above, refer to list comprehension
{connection_created, connections, gauge, "Connections currently open"},
@@ -232,7 +246,8 @@ register() ->
deregister_cleanup(_) -> ok.
collect_mf('detailed', Callback) ->
- collect(true, ?DETAILED_METRIC_NAME_PREFIX, vhosts_filter_from_pdict(), queues_filter_from_pdict(), enabled_mfs_from_pdict(), Callback),
+ collect(true, ?DETAILED_METRIC_NAME_PREFIX, vhosts_filter_from_pdict(), queues_filter_from_pdict(), enabled_mfs_from_pdict(?METRICS_RAW), Callback),
+ collect(true, ?CLUSTER_METRIC_NAME_PREFIX, vhosts_filter_from_pdict(), queues_filter_from_pdict(), enabled_mfs_from_pdict(?METRICS_CLUSTER), Callback),
%% identity is here to enable filtering on a cluster name (as already happens in existing dashboards)
emit_identity_info(Callback),
ok;
@@ -383,6 +398,14 @@ collect_metrics(_, {Type, Fun, Items}) ->
labels(Item) ->
label(element(1, Item)).
+label(L) when is_binary(L) ->
+ L;
+label(M) when is_map(M) ->
+ maps:fold(fun (K, V, Acc = <<>>) ->
+ <<Acc/binary, K/binary, "=\"", V/binary, "\"">>;
+ (K, V, Acc) ->
+ <<Acc/binary, ",", K/binary, "=\"", V/binary, "\"">>
+ end, <<>>, M);
label(#resource{virtual_host = VHost, kind = exchange, name = Name}) ->
<<"vhost=\"", VHost/binary, "\",exchange=\"", Name/binary, "\"">>;
label(#resource{virtual_host = VHost, kind = queue, name = Name}) ->
@@ -589,6 +612,46 @@ get_data(MF, true, VHostsFilter, _) when is_map(VHostsFilter), MF == queue_metri
end, [], Table);
get_data(queue_consumer_count, true, _, _) ->
ets:tab2list(queue_metrics);
+get_data(vhost_status, _, _, _) ->
+ [ { #{<<"vhost">> => VHost},
+ case rabbit_vhost_sup_sup:is_vhost_alive(VHost) of
+ true -> 1;
+ false -> 0
+ end}
+ || VHost <- rabbit_vhost:list() ];
+get_data(exchange_bindings, _, _, _) ->
+ Exchanges = lists:foldl(fun
+ (#exchange{internal = true}, Acc) ->
+ Acc;
+ (#exchange{name = #resource{name = <<>>}}, Acc) ->
+ Acc;
+ (#exchange{name = EName, type = EType}, Acc) ->
+ maps:put(EName, #{type => atom_to_binary(EType), binding_count => 0}, Acc)
+ end, #{}, rabbit_exchange:list()),
+ WithCount = ets:foldl(
+ fun (#route{binding = #binding{source = EName}}, Acc) ->
+ case maps:is_key(EName, Acc) of
+ false -> Acc;
+ true ->
+ maps:update_with(EName, fun (R = #{binding_count := Cnt}) ->
+ R#{binding_count => Cnt + 1}
+ end, Acc)
+ end
+ end, Exchanges, rabbit_route),
+ maps:fold(fun(#resource{virtual_host = VHost, name = Name}, #{type := Type, binding_count := Bindings}, Acc) ->
+ [{<<"vhost=\"", VHost/binary, "\",exchange=\"", Name/binary, "\",type=\"", Type/binary, "\"">>,
+ Bindings}|Acc]
+ end, [], WithCount);
+get_data(exchange_names, _, _, _) ->
+ lists:foldl(fun
+ (#exchange{internal = true}, Acc) ->
+ Acc;
+ (#exchange{name = #resource{name = <<>>}}, Acc) ->
+ Acc;
+ (#exchange{name = #resource{virtual_host = VHost, name = Name}, type = EType}, Acc) ->
+ Label = <<"vhost=\"", VHost/binary, "\",exchange=\"", Name/binary, "\",type=\"", (atom_to_binary(EType))/binary, "\"">>,
+ [{Label, 1}|Acc]
+ end, [], rabbit_exchange:list());
get_data(Table, _, _, _) ->
ets:tab2list(Table).
@@ -642,13 +705,13 @@ sum('', B) ->
sum(A, B) ->
A + B.
-enabled_mfs_from_pdict() ->
+enabled_mfs_from_pdict(AllMFs) ->
case get(prometheus_mf_filter) of
undefined ->
[];
MFNames ->
MFNameSet = sets:from_list(MFNames),
- [ MF || MF = {Table, _} <- ?METRICS_RAW, sets:is_element(Table, MFNameSet) ]
+ [ MF || MF = {Table, _} <- AllMFs, sets:is_element(Table, MFNameSet) ]
end.
vhosts_filter_from_pdict() ->
diff --git a/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl b/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl
index 10c573988c..c8be5fae28 100644
--- a/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl
+++ b/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl
@@ -54,7 +54,10 @@ groups() ->
queue_consumer_count_all_vhosts_per_object_test,
queue_coarse_metrics_per_object_test,
queue_metrics_per_object_test,
- queue_consumer_count_and_queue_metrics_mutually_exclusive_test
+ queue_consumer_count_and_queue_metrics_mutually_exclusive_test,
+ vhost_status_metric,
+ exchange_bindings_metric,
+ exchange_names_metric
]}
].
@@ -120,6 +123,14 @@ init_per_group(detailed_metrics, Config0) ->
amqp_channel:cast(Ch,
#'basic.publish'{routing_key = QName},
#amqp_msg{payload = <<"msg">>})
+ end, lists:seq(1, MsgNum) ),
+ ExDirect = <<QName/binary, "-direct-exchange">>,
+ #'exchange.declare_ok'{} = amqp_channel:call(Ch, #'exchange.declare'{exchange = ExDirect}),
+ ExTopic = <<QName/binary, "-topic-exchange">>,
+ #'exchange.declare_ok'{} = amqp_channel:call(Ch, #'exchange.declare'{exchange = ExTopic, type = <<"topic">>}),
+ #'queue.bind_ok'{} = amqp_channel:call(Ch, #'queue.bind'{queue = QName, exchange = ExDirect, routing_key = QName}),
+ lists:foreach( fun (Idx) ->
+ #'queue.bind_ok'{} = amqp_channel:call(Ch, #'queue.bind'{queue = QName, exchange = ExTopic, routing_key = integer_to_binary(Idx)})
end, lists:seq(1, MsgNum) )
end)()
|| {VHost, Ch, MsgNum} <- [{<<"/">>, DefaultCh, 3}, {<<"vhost-1">>, VHost1Ch, 7}, {<<"vhost-2">>, VHost2Ch, 11}],
@@ -488,6 +499,51 @@ detailed_metrics_no_families_enabled_by_default(Config) ->
?assertEqual(#{}, parse_response(Body)),
ok.
+vhost_status_metric(Config) ->
+ {_, Body1} = http_get_with_pal(Config, "/metrics/detailed?family=vhost_status", [], 200),
+ Expected = #{rabbitmq_cluster_vhost_status =>
+ #{#{vhost => "vhost-1"} => [1],
+ #{vhost => "vhost-2"} => [1],
+ #{vhost => "/"} => [1]}},
+ ?assertEqual(Expected, parse_response(Body1)),
+ ok.
+
+exchange_bindings_metric(Config) ->
+ {_, Body1} = http_get_with_pal(Config, "/metrics/detailed?family=exchange_bindings", [], 200),
+
+ Bindings = map_get(rabbitmq_cluster_exchange_bindings, parse_response(Body1)),
+ ?assertEqual([11], map_get(#{vhost=>"vhost-2",exchange=>"vhost-2-queue-with-messages-topic-exchange",type=>"topic"}, Bindings)),
+ ?assertEqual([1], map_get(#{vhost=>"vhost-2",exchange=>"vhost-2-queue-with-messages-direct-exchange",type=>"direct"}, Bindings)),
+ ok.
+
+exchange_names_metric(Config) ->
+ {_, Body1} = http_get_with_pal(Config, "/metrics/detailed?family=exchange_names", [], 200),
+
+ Names = maps:filter(
+ fun
+ (#{exchange := [$a, $m, $q|_]}, _) ->
+ false;
+ (_, _) ->
+ true
+ end,
+ map_get(rabbitmq_cluster_exchange_name, parse_response(Body1))),
+
+ ?assertEqual(#{ #{vhost=>"vhost-2",exchange=>"vhost-2-queue-with-messages-topic-exchange",type=>"topic"} => [1],
+ #{vhost=>"vhost-2",exchange=>"vhost-2-queue-with-messages-direct-exchange",type=>"direct"} => [1],
+ #{vhost=>"vhost-1",exchange=>"vhost-1-queue-with-messages-topic-exchange",type=>"topic"} => [1],
+ #{vhost=>"vhost-1",exchange=>"vhost-1-queue-with-messages-direct-exchange",type=>"direct"} => [1],
+ #{vhost=>"/",exchange=>"default-queue-with-messages-topic-exchange",type=>"topic"} => [1],
+ #{vhost=>"/",exchange=>"default-queue-with-messages-direct-exchange",type=>"direct"} => [1],
+ #{vhost=>"vhost-2",exchange=>"vhost-2-queue-with-consumer-topic-exchange",type=>"topic"} => [1],
+ #{vhost=>"vhost-2",exchange=>"vhost-2-queue-with-consumer-direct-exchange",type=>"direct"} => [1],
+ #{vhost=>"vhost-1",exchange=>"vhost-1-queue-with-consumer-topic-exchange",type=>"topic"} => [1],
+ #{vhost=>"vhost-1",exchange=>"vhost-1-queue-with-consumer-direct-exchange",type=>"direct"} => [1],
+ #{vhost=>"/",exchange=>"default-queue-with-consumer-topic-exchange",type=>"topic"} => [1],
+ #{vhost=>"/",exchange=>"default-queue-with-consumer-direct-exchange",type=>"direct"} => [1]
+ }, Names),
+ ok.
+
+
http_get(Config, ReqHeaders, CodeExp) ->
Path = proplists:get_value(prometheus_path, Config, "/metrics"),
http_get(Config, Path, ReqHeaders, CodeExp).