1: -module(mongoose_instrument_metrics_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("eunit/include/eunit.hrl"). 5: -include_lib("common_test/include/ct.hrl"). 6: 7: -define(LABELS, #{host_type => <<"localhost">>}). 8: -define(LABELS2, #{host_type => <<"test type">>}). 9: -define(HOST_TYPE, <<"localhost">>). 10: -define(HOST_TYPE2, <<"test type">>). 11: 12: %% Setup and teardown 13: 14: all() -> 15: [{group, prometheus}, 16: {group, exometer}, 17: {group, exometer_global}, 18: {group, prometheus_and_exometer} 19: ]. 20: 21: groups() -> 22: [{prometheus, [parallel], [prometheus_skips_non_metric_event, 23: prometheus_counter_is_created_but_not_initialized, 24: prometheus_counter_is_updated_separately_for_different_labels, 25: prometheus_histogram_is_created_but_not_initialized, 26: prometheus_histogram_is_updated_separately_for_different_labels, 27: multiple_prometheus_metrics_are_updated]}, 28: {exometer, [parallel], [exometer_skips_non_metric_event, 29: exometer_spiral_is_created_and_initialized, 30: exometer_spiral_is_updated_separately_for_different_labels, 31: exometer_histogram_is_created_and_initialized, 32: exometer_histogram_is_updated_separately_for_different_labels, 33: multiple_exometer_metrics_are_updated]}, 34: {exometer_global, [parallel], [multiple_exometer_metrics_are_updated]}, 35: {prometheus_and_exometer, [parallel], [prometheus_and_exometer_metrics_are_updated]} 36: ]. 37: 38: init_per_group(Group, Config) -> 39: [application:ensure_all_started(App) || App <- apps(Group)], 40: mongoose_config:set_opts(#{hosts => [?HOST_TYPE], 41: host_types => [?HOST_TYPE2], 42: instrumentation => opts(Group)}), 43: Config1 = async_helper:start(Config, mongoose_instrument, start_link, []), 44: mongoose_instrument:persist(), 45: Config1 ++ extra_config(Group). 46: 47: end_per_group(_Group, Config) -> 48: async_helper:stop_all(Config), 49: mongoose_config:erase_opts(). 50: 51: init_per_testcase(Case, Config) -> 52: [{event, join_atoms(Case, event)} | Config]. 53: 54: end_per_testcase(_Case, _Config) -> 55: ok. 56: 57: apps(prometheus) -> [prometheus]; 58: apps(exometer) -> [exometer_core]; 59: apps(exometer_global) -> [exometer_core]; 60: apps(prometheus_and_exometer) -> apps(prometheus) ++ apps(exometer). 61: 62: opts(prometheus) -> #{prometheus => #{}}; 63: opts(exometer) -> #{exometer => #{all_metrics_are_global => false}}; 64: opts(exometer_global) -> #{exometer => #{all_metrics_are_global => true}}; 65: opts(prometheus_and_exometer) -> maps:merge(opts(prometheus), opts(exometer)). 66: 67: extra_config(exometer) -> [{prefix, ?HOST_TYPE}]; 68: extra_config(exometer_global) -> [{prefix, global}]; 69: extra_config(_Group) -> []. 70: 71: %% Test cases 72: 73: prometheus_skips_non_metric_event(Config) -> 74: Event = ?config(event, Config), 75: false = mongoose_instrument_prometheus:set_up(Event, ?LABELS, #{}), 76: false = mongoose_instrument_prometheus:set_up(Event, ?LABELS, #{loglevel => error}). 77: 78: prometheus_counter_is_created_but_not_initialized(Config) -> 79: Event = ?config(event, Config), 80: Metric = prom_name(Event, count), 81: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{count => spiral}}), 82: ?assertEqual(undefined, prometheus_counter:value(Metric, [?HOST_TYPE])). 83: 84: prometheus_counter_is_updated_separately_for_different_labels(Config) -> 85: Event = ?config(event, Config), 86: Metric = prom_name(Event, count), 87: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{count => spiral}}), 88: ok = mongoose_instrument:set_up(Event, ?LABELS2, #{metrics => #{count => spiral}}), 89: ok = mongoose_instrument:execute(Event, ?LABELS, #{count => 1}), 90: ok = mongoose_instrument:execute(Event, ?LABELS2, #{count => 2}), 91: ?assertEqual(1, prometheus_counter:value(Metric, [?HOST_TYPE])), 92: ?assertEqual(2, prometheus_counter:value(Metric, [?HOST_TYPE2])). 93: 94: prometheus_histogram_is_created_but_not_initialized(Config) -> 95: Event = ?config(event, Config), 96: Metric = prom_name(Event, time), 97: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{time => histogram}}), 98: ?assertEqual(undefined, prometheus_histogram:value(Metric, [?HOST_TYPE])). 99: 100: prometheus_histogram_is_updated_separately_for_different_labels(Config) -> 101: Event = ?config(event, Config), 102: Metric = prom_name(Event, time), 103: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{time => histogram}}), 104: ok = mongoose_instrument:set_up(Event, ?LABELS2, #{metrics => #{time => histogram}}), 105: ok = mongoose_instrument:execute(Event, ?LABELS, #{time => 1}), 106: ok = mongoose_instrument:execute(Event, ?LABELS2, #{time => 2}), 107: ?assertMatch({[1, 0|_], 1}, prometheus_histogram:value(Metric, [?HOST_TYPE])), 108: ?assertMatch({[0, 1|_], 2}, prometheus_histogram:value(Metric, [?HOST_TYPE2])). 109: 110: multiple_prometheus_metrics_are_updated(Config) -> 111: Event = ?config(event, Config), 112: Counter = prom_name(Event, count), 113: Histogram = prom_name(Event, time), 114: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{count => spiral, 115: time => histogram}}), 116: %% Update both metrics 117: ok = mongoose_instrument:execute(Event, ?LABELS, #{count => 1, time => 2}), 118: ?assertEqual(1, prometheus_counter:value(Counter, [?HOST_TYPE])), 119: HistogramValue = prometheus_histogram:value(Histogram, [?HOST_TYPE]), 120: ?assertMatch({[0, 1|_], 2}, HistogramValue), 121: 122: %% Update only one metric 123: ok = mongoose_instrument:execute(Event, ?LABELS, #{count => 2}), 124: ?assertEqual(3, prometheus_counter:value(Counter, [?HOST_TYPE])), 125: ?assertEqual(HistogramValue, prometheus_histogram:value(Histogram, [?HOST_TYPE])), 126: 127: %% No update 128: ok = mongoose_instrument:execute(Event, ?LABELS, #{something => irrelevant}), 129: ?assertEqual(3, prometheus_counter:value(Counter, [?HOST_TYPE])), 130: ?assertEqual(HistogramValue, prometheus_histogram:value(Histogram, [?HOST_TYPE])). 131: 132: exometer_skips_non_metric_event(Config) -> 133: Event = ?config(event, Config), 134: false = mongoose_instrument_exometer:set_up(Event, ?LABELS, #{}), 135: false = mongoose_instrument_exometer:set_up(Event, ?LABELS, #{loglevel => error}). 136: 137: exometer_spiral_is_created_and_initialized(Config) -> 138: Event = ?config(event, Config), 139: Metric = [?HOST_TYPE, Event, count], 140: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{count => spiral}}), 141: ?assertEqual({ok, [{count, 0}]}, exometer:get_value(Metric, count)). 142: 143: exometer_spiral_is_updated_separately_for_different_labels(Config) -> 144: Event = ?config(event, Config), 145: Metric1 = [?HOST_TYPE, Event, count], 146: Metric2 = [<<"test_type">>, Event, count], 147: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{count => spiral}}), 148: ok = mongoose_instrument:set_up(Event, ?LABELS2, #{metrics => #{count => spiral}}), 149: ok = mongoose_instrument:execute(Event, ?LABELS, #{count => 1}), 150: ok = mongoose_instrument:execute(Event, ?LABELS2, #{count => 2}), 151: ?assertEqual({ok, [{count, 1}]}, exometer:get_value(Metric1, count)), 152: ?assertEqual({ok, [{count, 2}]}, exometer:get_value(Metric2, count)). 153: 154: exometer_histogram_is_created_and_initialized(Config) -> 155: Event = ?config(event, Config), 156: Metric = [?HOST_TYPE, Event, time], 157: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{time => histogram}}), 158: ?assertEqual({ok, [{mean, 0}]}, exometer:get_value(Metric, mean)). 159: 160: exometer_histogram_is_updated_separately_for_different_labels(Config) -> 161: Event = ?config(event, Config), 162: Metric1 = [?HOST_TYPE, Event, time], 163: Metric2 = [<<"test_type">>, Event, time], 164: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{time => histogram}}), 165: ok = mongoose_instrument:set_up(Event, ?LABELS2, #{metrics => #{time => histogram}}), 166: ok = mongoose_instrument:execute(Event, ?LABELS, #{time => 1}), 167: ok = mongoose_instrument:execute(Event, ?LABELS2, #{time => 3}), 168: ?assertEqual({ok, [{mean, 1}]}, exometer:get_value(Metric1, mean)), 169: ?assertEqual({ok, [{mean, 3}]}, exometer:get_value(Metric2, mean)). 170: 171: multiple_exometer_metrics_are_updated(Config) -> 172: Event = ?config(event, Config), 173: Prefix = ?config(prefix, Config), 174: Counter = [Prefix, Event, count], 175: Histogram = [Prefix, Event, time], 176: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{count => spiral, 177: time => histogram}}), 178: %% Update both metrics 179: ok = mongoose_instrument:execute(Event, ?LABELS, #{count => 1, time => 2}), 180: ?assertEqual({ok, [{count, 1}]}, exometer:get_value(Counter, count)), 181: ?assertEqual({ok, [{mean, 2}]}, exometer:get_value(Histogram, mean)), 182: 183: %% Update only one metric 184: ok = mongoose_instrument:execute(Event, ?LABELS, #{count => 5}), 185: ?assertEqual({ok, [{count, 6}]}, exometer:get_value(Counter, count)), 186: ?assertEqual({ok, [{mean, 2}]}, exometer:get_value(Histogram, mean)), 187: 188: %% No update 189: ok = mongoose_instrument:execute(Event, ?LABELS, #{something => irrelevant}), 190: ?assertEqual({ok, [{count, 6}]}, exometer:get_value(Counter, count)), 191: ?assertEqual({ok, [{mean, 2}]}, exometer:get_value(Histogram, mean)). 192: 193: prometheus_and_exometer_metrics_are_updated(Config) -> 194: Event = ?config(event, Config), 195: ok = mongoose_instrument:set_up(Event, ?LABELS, #{metrics => #{count => spiral, 196: time => histogram}}), 197: ok = mongoose_instrument:execute(Event, ?LABELS, #{count => 1, time => 2}), 198: ?assertEqual({ok, [{count, 1}]}, exometer:get_value([?HOST_TYPE, Event, count], count)), 199: ?assertEqual({ok, [{mean, 2}]}, exometer:get_value([?HOST_TYPE, Event, time], mean)), 200: ?assertEqual(1, prometheus_counter:value(prom_name(Event, count), [?HOST_TYPE])), 201: ?assertMatch({[0, 1|_], 2}, prometheus_histogram:value(prom_name(Event, time), [?HOST_TYPE])). 202: 203: %% Helpers 204: 205: join_atoms(A1, A2) -> 206: list_to_atom(join_atoms_to_list(A1, A2)). 207: 208: prom_name(EventName, MetricName) -> 209: join_atoms_to_list(EventName, MetricName). 210: 211: join_atoms_to_list(A1, A2) -> 212: atom_to_list(A1) ++ "_" ++ atom_to_list(A2).