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 |
111 |
[{"/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 |
8787 |
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 |
22752 |
prepare_key(K) when is_integer(K) -> integer_to_binary(K); |
145 |
63897 |
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. |