./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 110 [{"/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, metrics => Metrics, global => Global},
81 2 {jiffy:encode(Reply), Req, State}
82 end.
83
84 handle_get_values(Req, State, Bindings, HostType) ->
85 370 case get_metric_name(Bindings) of
86 {metric, Metric} ->
87 185 case mongoose_metrics:get_metric_value(HostType, Metric) of
88 {ok, Value} ->
89 183 {jiffy:encode(#{metric => prepare_value(Value)}), Req, State};
90 _Other ->
91 2 throw_error(not_found, <<"Metric not found">>)
92 end;
93 all_metrics ->
94 184 case mongoose_metrics:get_metric_values(HostType) of
95 [] ->
96 1 throw_error(not_found, <<"No metrics found">>);
97 Metrics ->
98 183 Values = prepare_metrics(Metrics),
99 183 {jiffy:encode(#{metrics => Values}), Req, State}
100 end
101 end.
102
103 -spec get_sum_metrics() -> map().
104 get_sum_metrics() ->
105 87 {_HostTypes, Metrics} = get_available_host_type_metrics(),
106 87 maps:from_list([{Metric, get_sum_metric(Metric)} || Metric <- Metrics]).
107
108 -spec get_sum_metric(atom()) -> map().
109 get_sum_metric(Metric) ->
110 10440 maps:from_list(mongoose_metrics:get_aggregated_values(Metric)).
111
112 -spec get_available_metrics(HostType :: mongooseim:host_type()) -> [any()].
113 get_available_metrics(HostType) ->
114 89 mongoose_metrics:get_host_type_metric_names(HostType).
115
116 -spec get_available_host_type_metrics() -> {[any(), ...], [any()]}.
117 get_available_host_type_metrics() ->
118 89 HostTypes = get_available_host_types(),
119 89 Metrics = [Metric || [Metric] <- get_available_metrics(hd(HostTypes))],
120 89 {HostTypes, Metrics}.
121
122 get_available_global_metrics() ->
123 2 [Metric || [Metric] <- mongoose_metrics:get_global_metric_names()].
124
125 -spec get_available_host_types() -> [mongooseim:host_type()].
126 get_available_host_types() ->
127 89 ?ALL_HOST_TYPES.
128
129 prepare_metrics(Metrics) ->
130 183 maps:from_list([{prepare_name(NameParts, FullName), prepare_value(Value)}
131 183 || {FullName = [_HostType | NameParts], Value} <- Metrics]).
132
133 prepare_name(NameParts, FullName) ->
134 25332 try
135 25332 ToStrings = [atom_to_list(NamePart) || NamePart <- NameParts],
136 25332 list_to_binary(string:join(ToStrings, "."))
137 catch Class:Reason:Stacktrace ->
138
:-(
erlang:raise(Class, {failed_to_prepare_name, FullName, Reason}, Stacktrace)
139 end.
140
141 prepare_value(KVs) ->
142 25602 maps:from_list([{prepare_key(K), V} || {K, V} <- KVs]).
143
144 21186 prepare_key(K) when is_integer(K) -> integer_to_binary(K);
145 63114 prepare_key(K) when is_atom(K) -> atom_to_binary(K).
146
147 get_metric_name(#{metric := MetricBin}) ->
148 275 try {metric, binary_to_existing_atom(MetricBin)}
149 2 catch _:_ -> throw_error(not_found, <<"Metric not found">>)
150 end;
151 get_metric_name(#{}) ->
152 271 all_metrics.
Line Hits Source