diff options
author | Alexey Lebedeff <binarin@binarin.info> | 2021-11-19 17:27:33 +0100 |
---|---|---|
committer | Alexey Lebedeff <binarin@binarin.info> | 2021-11-24 11:00:41 +0100 |
commit | 6e3012aaf929f9861b4b5110a637f23d07b95b9d (patch) | |
tree | 773e5928b03f7eb6018702fed8e4f4efc82c3425 | |
parent | e22e667a103fcb1460896906fe97c7ea6c0ce460 (diff) | |
download | rabbitmq-server-git-6e3012aaf929f9861b4b5110a637f23d07b95b9d.tar.gz |
Add optional metrics for vhost and exchange count
These can make sense in some scenarios, e.g. when vhost/exchanges are
+created using self-service automation
3 files changed, 149 insertions, 2 deletions
diff --git a/deps/rabbitmq_prometheus/metrics-detailed.md b/deps/rabbitmq_prometheus/metrics-detailed.md index 262fca04bb..9cc4d19734 100644 --- a/deps/rabbitmq_prometheus/metrics-detailed.md +++ b/deps/rabbitmq_prometheus/metrics-detailed.md @@ -236,3 +236,33 @@ 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 can make sense in some scenarios, e.g. when vhost/exchanges are +created using self-service automation. They are also a bit different +from the rest of the metrics, as they not exactly per-node metrics, +but cluster wide. So any aggregations of these values accross multiple +nodes make no sense. + +Group `vhost_status`: + +| Metric | Description | +|--------------------------------|----------------------------------| +| rabbitmq_detailed_vhost_status | Whether a given vhost is running | + +Group `exchange_names`: + +| Metric | Description | +|---------------------------------|----------------------------------------------------------------------------------------------------------| +| rabbitmq_detailed_exchange_name | Enumerates exchanges without any additional info (cheaper than `exchange_bindings`, cluster-wide number) | + +Group `exchange_bindings`: + +| Metric | Description | +|-------------------------------------|------------------------------------------------------------------------| +| rabbitmq_detailed_exchange_bindings | Number of bindings for an exchange (WARNING: it's cluster-wide number) | + + + + 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..1d77a7b2da 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 @@ -214,6 +214,19 @@ ]} ]). +%% Metrics that can be only requested through `/metrics/detailed` +-define(METRICS_RAW_DETAILED,[ + {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 (WARNING: it's cluster-wide number)"} + ]}, + {exchange_names, [ + {2, undefined, exchange_name, gauge, "Enumerates exchanges without any additional info (cheaper than `exchange_bindings`, cluster-wide number)"} + ]} +]). + -define(TOTALS, [ %% ordering differs from metrics above, refer to list comprehension {connection_created, connections, gauge, "Connections currently open"}, @@ -383,6 +396,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 +610,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). @@ -648,7 +709,7 @@ enabled_mfs_from_pdict() -> []; MFNames -> MFNameSet = sets:from_list(MFNames), - [ MF || MF = {Table, _} <- ?METRICS_RAW, sets:is_element(Table, MFNameSet) ] + [ MF || MF = {Table, _} <- ?METRICS_RAW ++ ?METRICS_RAW_DETAILED, 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..1db499ec14 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_detailed_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_detailed_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_detailed_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). |