./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 4 [{"/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
:-(
mongoose_admin_api:init(Req, State).
32
33 -spec is_authorized(req(), state()) -> {true | {false, iodata()}, req(), state()}.
34 is_authorized(Req, State) ->
35
:-(
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
:-(
{[
41 {{<<"application">>, <<"json">>, '*'}, to_json}
42 ], Req, State}.
43
44 -spec allowed_methods(req(), state()) -> {[binary()], req(), state()}.
45 allowed_methods(Req, State) ->
46
:-(
{[<<"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
:-(
try_handle_request(Req, State, fun handle_get/2).
52
53 %% Internal functions
54
55 handle_get(Req, State = #{suffix := all}) ->
56
:-(
Bindings = cowboy_req:bindings(Req),
57
:-(
case get_metric_name(Bindings) of
58 {metric, Metric} ->
59
:-(
case mongoose_metrics:get_aggregated_values(Metric) of
60 [] ->
61
:-(
throw_error(not_found, <<"Metric not found">>);
62 Value ->
63
:-(
{jiffy:encode(#{metric => prepare_value(Value)}), Req, State}
64 end;
65 all_metrics ->
66
:-(
Values = get_sum_metrics(),
67
:-(
{jiffy:encode(#{metrics => Values}), Req, State}
68 end;
69 handle_get(Req, State = #{suffix := global}) ->
70
:-(
Bindings = cowboy_req:bindings(Req),
71
:-(
handle_get_values(Req, State, Bindings, global);
72 handle_get(Req, State) ->
73
:-(
Bindings = cowboy_req:bindings(Req),
74
:-(
case Bindings of
75 #{host_type := HostType} ->
76
:-(
handle_get_values(Req, State, Bindings, HostType);
77 #{} ->
78
:-(
{HostTypes, Metrics} = get_available_host_type_metrics(),
79
:-(
Global = get_available_global_metrics(),
80
:-(
Reply = #{host_types => HostTypes,
81 metrics => lists:map(fun prepare_name/1, Metrics),
82 global => lists:map(fun prepare_name/1, Global)},
83
:-(
{jiffy:encode(Reply), Req, State}
84 end.
85
86 handle_get_values(Req, State, Bindings, HostType) ->
87
:-(
case get_metric_name(Bindings) of
88 {metric, Metric} ->
89
:-(
case mongoose_metrics:get_metric_value(HostType, Metric) of
90 {ok, Value} ->
91
:-(
{jiffy:encode(#{metric => prepare_value(Value)}), Req, State};
92 _Other ->
93
:-(
throw_error(not_found, <<"Metric not found">>)
94 end;
95 all_metrics ->
96
:-(
case mongoose_metrics:get_metric_values(HostType) of
97 [] ->
98
:-(
throw_error(not_found, <<"No metrics found">>);
99 Metrics ->
100
:-(
Values = prepare_metrics(Metrics),
101
:-(
{jiffy:encode(#{metrics => Values}), Req, State}
102 end
103 end.
104
105 -spec get_sum_metrics() -> map().
106 get_sum_metrics() ->
107
:-(
{_HostTypes, Metrics} = get_available_host_type_metrics(),
108
:-(
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
:-(
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
:-(
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
:-(
HostTypes = get_available_host_types(),
121
:-(
Metrics = get_available_metrics(hd(HostTypes)), %% Note: works only for the first host type
122
:-(
{HostTypes, Metrics}.
123
124 get_available_global_metrics() ->
125
:-(
mongoose_metrics:get_global_metric_names().
126
127 -spec get_available_host_types() -> [mongooseim:host_type()].
128 get_available_host_types() ->
129
:-(
?ALL_HOST_TYPES.
130
131 prepare_metrics(Metrics) ->
132
:-(
maps:from_list([{prepare_name(NameParts), prepare_value(Value)}
133
:-(
|| {[_HostType | NameParts], Value} <- Metrics]).
134
135 prepare_name(NameParts) ->
136
:-(
try
137
:-(
ToStrings = [atom_to_list(NamePart) || NamePart <- NameParts],
138
:-(
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
:-(
maps:from_list([{prepare_key(K), V} || {K, V} <- KVs]).
145
146
:-(
prepare_key(K) when is_integer(K) -> integer_to_binary(K);
147
:-(
prepare_key(K) when is_atom(K) -> atom_to_binary(K).
148
149 get_metric_name(#{metric := MetricBin}) ->
150
:-(
Parts = binary:split(MetricBin, <<".">>, [global]),
151
:-(
try {metric, lists:map(fun binary_to_existing_atom/1, Parts)}
152
:-(
catch _:_ -> throw_error(not_found, <<"Metric not found">>)
153 end;
154 get_metric_name(#{}) ->
155
:-(
all_metrics.
Line Hits Source