./ct_report/coverage/mongoose_admin_api_metrics.COVER.html

1 -module(mongoose_admin_api_metrics).
2
3 -behaviour(mongoose_admin_api).
4 -export([routes/1]).
5
6 -behaviour(cowboy_rest).
7 -export([init/2,
8 is_authorized/2,
9 content_types_provided/2,
10 allowed_methods/2,
11 to_json/2]).
12
13 -ignore_xref([to_json/2, from_json/2]).
14
15 -import(mongoose_admin_api, [parse_body/1, try_handle_request/3, throw_error/2, resource_created/4]).
16
17 -type req() :: cowboy_req:req().
18 -type state() :: mongoose_admin_api:state().
19
20 -include("mongoose.hrl").
21
22 -spec routes(state()) -> mongoose_http_handler:routes().
23 routes(State) ->
24 100 [{"/metrics/", ?MODULE, State},
25 {"/metrics/all/[:metric]", ?MODULE, State#{suffix => all}},
26 {"/metrics/global/[:metric]", ?MODULE, State#{suffix => global}},
27 {"/metrics/host_type/:host_type/[:metric]", ?MODULE, State}].
28
29 -spec init(req(), state()) -> {cowboy_rest, req(), state()}.
30 init(Req, State) ->
31 550 mongoose_admin_api:init(Req, State).
32
33 -spec is_authorized(req(), state()) -> {true | {false, iodata()}, req(), state()}.
34 is_authorized(Req, State) ->
35 550 mongoose_admin_api:is_authorized(Req, State).
36
37 -spec content_types_provided(req(), state()) ->
38 {[{{binary(), binary(), '*'}, atom()}], req(), state()}.
39 content_types_provided(Req, State) ->
40 548 {[
41 {{<<"application">>, <<"json">>, '*'}, to_json}
42 ], Req, State}.
43
44 -spec allowed_methods(req(), state()) -> {[binary()], req(), state()}.
45 allowed_methods(Req, State) ->
46 550 {[<<"OPTIONS">>, <<"HEAD">>, <<"GET">>], Req, State}.
47
48 %% @doc Called for a method of type "GET"
49 -spec to_json(req(), state()) -> {iodata() | stop, req(), state()}.
50 to_json(Req, State) ->
51 548 try_handle_request(Req, State, fun handle_get/2).
52
53 %% Internal functions
54
55 handle_get(Req, State = #{suffix := all}) ->
56 176 Bindings = cowboy_req:bindings(Req),
57 176 case get_metric_name(Bindings) of
58 {metric, Metric} ->
59 88 case mongoose_metrics:get_aggregated_values(Metric) of
60 [] ->
61 1 throw_error(not_found, <<"Metric not found">>);
62 Value ->
63 87 {jiffy:encode(#{metric => prepare_value(Value)}), Req, State}
64 end;
65 all_metrics ->
66 87 Values = get_sum_metrics(),
67 87 {jiffy:encode(#{metrics => Values}), Req, State}
68 end;
69 handle_get(Req, State = #{suffix := global}) ->
70 194 Bindings = cowboy_req:bindings(Req),
71 194 handle_get_values(Req, State, Bindings, global);
72 handle_get(Req, State) ->
73 178 Bindings = cowboy_req:bindings(Req),
74 178 case Bindings of
75 #{host_type := HostType} ->
76 176 handle_get_values(Req, State, Bindings, HostType);
77 #{} ->
78 2 {HostTypes, Metrics} = get_available_host_type_metrics(),
79 2 Global = get_available_global_metrics(),
80 2 Reply = #{host_types => HostTypes,
81 metrics => lists:map(fun prepare_name/1, Metrics),
82 global => lists:map(fun prepare_name/1, Global)},
83 2 {jiffy:encode(Reply), Req, State}
84 end.
85
86 handle_get_values(Req, State, Bindings, HostType) ->
87 370 case get_metric_name(Bindings) of
88 {metric, Metric} ->
89 185 case mongoose_metrics:get_metric_value(HostType, Metric) of
90 {ok, Value} ->
91 183 {jiffy:encode(#{metric => prepare_value(Value)}), Req, State};
92 _Other ->
93 2 throw_error(not_found, <<"Metric not found">>)
94 end;
95 all_metrics ->
96 184 case mongoose_metrics:get_metric_values(HostType) of
97 [] ->
98 1 throw_error(not_found, <<"No metrics found">>);
99 Metrics ->
100 183 Values = prepare_metrics(Metrics),
101 183 {jiffy:encode(#{metrics => Values}), Req, State}
102 end
103 end.
104
105 -spec get_sum_metrics() -> map().
106 get_sum_metrics() ->
107 87 {_HostTypes, Metrics} = get_available_host_type_metrics(),
108 87 maps:from_list([{prepare_name(Metric), get_sum_metric(Metric)} || Metric <- Metrics]).
109
110 -spec get_sum_metric(atom()) -> map().
111 get_sum_metric(Metric) ->
112 8004 maps:from_list(mongoose_metrics:get_aggregated_values(Metric)).
113
114 -spec get_available_metrics(HostType :: mongooseim:host_type()) -> [any()].
115 get_available_metrics(HostType) ->
116 89 mongoose_metrics:get_host_type_metric_names(HostType).
117
118 -spec get_available_host_type_metrics() -> {[any(), ...], [any()]}.
119 get_available_host_type_metrics() ->
120 89 HostTypes = get_available_host_types(),
121 89 Metrics = get_available_metrics(hd(HostTypes)), %% Note: works only for the first host type
122 89 {HostTypes, Metrics}.
123
124 get_available_global_metrics() ->
125 2 mongoose_metrics:get_global_metric_names().
126
127 -spec get_available_host_types() -> [mongooseim:host_type()].
128 get_available_host_types() ->
129 89 ?ALL_HOST_TYPES.
130
131 prepare_metrics(Metrics) ->
132 183 maps:from_list([{prepare_name(NameParts), prepare_value(Value)}
133 183 || {[_HostType | NameParts], Value} <- Metrics]).
134
135 prepare_name(NameParts) ->
136 44466 try
137 44466 ToStrings = [atom_to_list(NamePart) || NamePart <- NameParts],
138 44466 list_to_binary(string:join(ToStrings, "."))
139 catch Class:Reason:Stacktrace ->
140
:-(
erlang:raise(Class, {failed_to_prepare_name, NameParts, Reason}, Stacktrace)
141 end.
142
143 prepare_value(KVs) ->
144 36514 maps:from_list([{prepare_key(K), V} || {K, V} <- KVs]).
145
146 51528 prepare_key(K) when is_integer(K) -> integer_to_binary(K);
147 100128 prepare_key(K) when is_atom(K) -> atom_to_binary(K).
148
149 get_metric_name(#{metric := MetricBin}) ->
150 275 Parts = binary:split(MetricBin, <<".">>, [global]),
151 275 try {metric, lists:map(fun binary_to_existing_atom/1, Parts)}
152 2 catch _:_ -> throw_error(not_found, <<"Metric not found">>)
153 end;
154 get_metric_name(#{}) ->
155 271 all_metrics.
Line Hits Source