From 8c20cd1b2de30dd3a393df31f6b7a2529b2417ca Mon Sep 17 00:00:00 2001 From: Gerhard Lazu Date: Mon, 21 Jun 2021 17:21:11 +0100 Subject: [PATCH] Return a valid prometheus_format for multiple counters When we use the same counters for different categories (a.k.a. names), e.g. protocol AMQP091 & STREAM, prometheus_format was returning an invalid exposition format, e.g. # TYPE rabbitmq_global_messages_received_total counter # HELP rabbitmq_global_messages_received_total Total number of messages received from publishers rabbitmq_global_messages_received_total{protocol="amqp091"} 0 # TYPE rabbitmq_global_messages_received_total counter # HELP rabbitmq_global_messages_received_total Total number of messages received from publishers rabbitmq_global_messages_received_total{protocol="stream"} 0 Instead of: # TYPE rabbitmq_global_messages_received_total counter # HELP rabbitmq_global_messages_received_total Total number of messages received from publishers rabbitmq_global_messages_received_total{protocol="amqp091"} 0 rabbitmq_global_messages_received_total{protocol="stream"} 0 The above was invalid as captured here: https://github.com/OpenObservability/OpenMetrics/issues/13 This change fixes that, makes test failures a lot easier to understand, and also speeds them up by 4x: time make tests -> 5.240s time make tests -> 1.292s There is no reason to use ct for integration tests when eunit tests will do the job. While we don't have a lot of tests today, the more we add, the slower ct gets, while eunit remains fast. Pair @dumbbell Signed-off-by: Gerhard Lazu --- src/seshat_counters.erl | 26 +++++---- test/seshat_SUITE.erl | 74 ------------------------- test/seshat_counters_server_SUITE.erl | 80 --------------------------- test/seshat_counters_server_test.erl | 44 +++++++++++++++ test/seshat_counters_test.erl | 58 +++++++++++++++++++ 5 files changed, 118 insertions(+), 164 deletions(-) delete mode 100644 test/seshat_SUITE.erl delete mode 100644 test/seshat_counters_server_SUITE.erl create mode 100644 test/seshat_counters_server_test.erl create mode 100644 test/seshat_counters_test.erl diff --git a/src/seshat_counters.erl b/src/seshat_counters.erl index 7124982..eb54904 100644 --- a/src/seshat_counters.erl +++ b/src/seshat_counters.erl @@ -66,16 +66,22 @@ overview(Group) -> #{}, seshat_counters_server:get_table(Group)). prometheus_format(Group) -> - ets:foldl(fun({Name, Ref, Fields}, Acc) -> - Counters = lists:foldl( - fun ({Key, Index, Type, Description}, Acc0) -> - [{Key, counters:get(Ref, Index), Type, Description} - | Acc0] - end, - [], - Fields - ), - Acc#{Name => Counters} + ets:foldl(fun({Labels, Ref, Fields}, Acc) -> + lists:foldl( + fun ({Name, Index, Type, Help}, Acc0) -> + InitialMetric = #{type => Type, + help => Help, + values => #{}}, + Metric = maps:get(Name, Acc0, InitialMetric), + Values = maps:get(values, Metric), + Counter = counters:get(Ref, Index), + Values1 = Values#{Labels => Counter}, + Metric1 = Metric#{values => Values1}, + Acc0#{Name => Metric1} + end, + Acc, + Fields + ) end, #{}, seshat_counters_server:get_table(Group)). diff --git a/test/seshat_SUITE.erl b/test/seshat_SUITE.erl deleted file mode 100644 index b23d4f1..0000000 --- a/test/seshat_SUITE.erl +++ /dev/null @@ -1,74 +0,0 @@ -%% This Source Code Form is subject to the terms of the Mozilla Public -%% License, v. 2.0. If a copy of the MPL was not distributed with this -%% file, You can obtain one at https://mozilla.org/MPL/2.0/. -%% -%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. -%% - --module(seshat_SUITE). - --compile(nowarn_export_all). --compile(export_all). - --export([]). - --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). - -%%%=================================================================== -%%% Common Test callbacks -%%%=================================================================== - -all() -> - [{group, tests}]. - -all_tests() -> - [counters]. - -groups() -> - [{tests, [], all_tests()}]. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_Group, Config) -> - Config. - -end_per_group(_Group, _Config) -> - ok. - -init_per_testcase(_TestCase, Config) -> - {ok, Apps} = application:ensure_all_started(seshat), - [{started_apps, Apps} | Config]. - -end_per_testcase(_TestCase, Config) -> - [application:stop(App) - || App <- lists:reverse(?config(started_apps, Config))], - ok. - -%%%=================================================================== -%%% Test cases -%%%=================================================================== -counters(_Config) -> - Group = "pets", - Counters = [ - { - carrots_eaten_total, 1, counter, - "Total number of carrots eaten on a meal" - }, - { - holes_dug_total, 2, counter, - "Total number of holes dug in an afternoon" - } - ], - seshat_counters:new_group(Group), - seshat_counters:new(Group, "rabbit", Counters), - Ref = seshat_counters:fetch(Group, "rabbit"), - counters:add(Ref, 1, 3), - counters:add(Ref, 2, 1), - Overview = seshat_counters:overview(Group), - ?assertMatch(#{"rabbit" := #{carrots_eaten_total := 3,holes_dug_total := 1}}, - Overview). diff --git a/test/seshat_counters_server_SUITE.erl b/test/seshat_counters_server_SUITE.erl deleted file mode 100644 index a5d0740..0000000 --- a/test/seshat_counters_server_SUITE.erl +++ /dev/null @@ -1,80 +0,0 @@ -%% This Source Code Form is subject to the terms of the Mozilla Public -%% License, v. 2.0. If a copy of the MPL was not distributed with this -%% file, You can obtain one at https://mozilla.org/MPL/2.0/. -%% -%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. -%% - --module(seshat_counters_server_SUITE). - --compile(nowarn_export_all). --compile(export_all). - --export([]). - --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). - -%%%=================================================================== -%%% Common Test callbacks -%%%=================================================================== - -all() -> - [{group, tests}]. - -all_tests() -> - [ - get_table, - get_tables, - delete_table - ]. - -groups() -> - [{tests, [], all_tests()}]. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_Group, Config) -> - Config. - -end_per_group(_Group, _Config) -> - ok. - -init_per_testcase(_TestCase, Config) -> - {ok, Apps} = application:ensure_all_started(seshat), - [{started_apps, Apps} | Config]. - -end_per_testcase(_TestCase, Config) -> - [application:stop(App) - || App <- lists:reverse(?config(started_apps, Config))], - ok. - -%%%=================================================================== -%%% Test cases -%%%=================================================================== -get_table(_Config) -> - Ref0 = seshat_counters_server:create_table("burrows"), - Ref1 = seshat_counters_server:get_table("burrows"), - ?assertEqual(Ref0, Ref1), - ?assert(ets:info(Ref0) =/= undefined). - -get_tables(_Config) -> - seshat_counters_server:create_table("burrows"), - seshat_counters_server:create_table("nests"), - seshat_counters_server:create_table("roosts"), - Tables = lists:sort(maps:keys(seshat_counters_server:get_tables())), - ?assertMatch(Tables, lists:sort(["burrows", "nests", "roosts"])). - -delete_table(_Config) -> - seshat_counters_server:create_table("burrows"), - seshat_counters_server:create_table("nests"), - seshat_counters_server:create_table("roosts"), - Tables0 = lists:sort(maps:keys(seshat_counters_server:get_tables())), - ?assertMatch(Tables0, lists:sort(["burrows", "nests", "roosts"])), - seshat_counters_server:delete_table("nests"), - Tables = lists:sort(maps:keys(seshat_counters_server:get_tables())), - ?assertMatch(Tables, lists:sort(["burrows", "roosts"])). diff --git a/test/seshat_counters_server_test.erl b/test/seshat_counters_server_test.erl new file mode 100644 index 0000000..cfc520f --- /dev/null +++ b/test/seshat_counters_server_test.erl @@ -0,0 +1,44 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. +%% + +-module(seshat_counters_server_test). +-include_lib("eunit/include/eunit.hrl"). + +setup() -> + {ok, _} = application:ensure_all_started(seshat). + +cleanup(_) -> + ok = application:stop(seshat). + +test_suite_test_() -> + {foreach, + fun setup/0, + fun cleanup/1, + [ fun get_table/0, + fun get_tables/0, + fun delete_table/0 ]}. + +get_table() -> + CreatedTable = seshat_counters_server:create_table("burrows"), + QueriedTable = seshat_counters_server:get_table("burrows"), + ?assertEqual(CreatedTable, QueriedTable), + ?assertNotEqual(undefined, ets:info(CreatedTable)). + +get_tables() -> + seshat_counters_server:create_table("burrows"), + seshat_counters_server:create_table("nests"), + Tables = lists:sort(maps:keys(seshat_counters_server:get_tables())), + ?assertEqual(["burrows", "nests"], Tables). + +delete_table() -> + seshat_counters_server:create_table("burrows"), + seshat_counters_server:create_table("nests"), + Tables = lists:sort(maps:keys(seshat_counters_server:get_tables())), + ?assertMatch(["burrows", "nests"], Tables), + seshat_counters_server:delete_table("nests"), + Tables1 = lists:sort(maps:keys(seshat_counters_server:get_tables())), + ?assertMatch(["burrows"], Tables1). diff --git a/test/seshat_counters_test.erl b/test/seshat_counters_test.erl new file mode 100644 index 0000000..036e654 --- /dev/null +++ b/test/seshat_counters_test.erl @@ -0,0 +1,58 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. +%% + +-module(seshat_counters_test). +-include_lib("eunit/include/eunit.hrl"). + +setup() -> + {ok, _} = application:ensure_all_started(seshat). + +cleanup(_) -> + ok = application:stop(seshat). + +test_suite_test_() -> + {foreach, + fun setup/0, + fun cleanup/1, + [ fun overview/0, + fun prometheus_format_multiple_names/0 ]}. + +overview() -> + Group = "pets", + Counters = [ + { + carrots_eaten_total, 1, counter, + "Total number of carrots eaten on a meal" + }, + { + holes_dug_total, 2, counter, + "Total number of holes dug in an afternoon" + } + ], + seshat_counters:new_group(Group), + seshat_counters:new(Group, "rabbit", Counters), + Ref = seshat_counters:fetch(Group, "rabbit"), + counters:add(Ref, 1, 3), + counters:add(Ref, 2, 1), + Overview = seshat_counters:overview(Group), + ?assertEqual( + #{"rabbit" => #{carrots_eaten_total => 3, + holes_dug_total => 1}}, + Overview). + +prometheus_format_multiple_names() -> + Group = people, + Counters = [{ foo, 1, counter, "Total foos given" }], + seshat_counters:new_group(Group), + seshat_counters:new(Group, {name, you}, Counters), + seshat_counters:new(Group, {name, me}, Counters), + PrometheusFormat = seshat_counters:prometheus_format(Group), + ExpectedPrometheusFormat = #{ foo => #{ type => counter, + help => "Total foos given", + values => #{{name, me} => 0, + {name, you} => 0} }}, + ?assertEqual(ExpectedPrometheusFormat, PrometheusFormat).