1 |
|
-module(mongoose_metrics_api). |
2 |
|
-export([get_metrics/1, |
3 |
|
get_metrics_as_dicts/2, |
4 |
|
get_cluster_metrics_as_dicts/3]). |
5 |
|
|
6 |
|
-include("mongoose_logger.hrl"). |
7 |
|
-include("mongoose.hrl"). |
8 |
|
|
9 |
|
-type name() :: [atom() | integer()]. |
10 |
|
-type key() :: atom(). |
11 |
|
-type metric_result() :: |
12 |
|
{ok, #{binary() => binary() | non_neg_integer()}}. |
13 |
|
-type dict_result() :: #{binary() => binary() | non_neg_integer()}. |
14 |
|
-type metric_dict_result() :: |
15 |
|
{ok, #{binary() => binary() | [dict_result()]}}. |
16 |
|
-type metric_node_dict_result() :: |
17 |
|
{ok, #{binary() => binary() | [metric_dict_result()]}} |
18 |
|
| {error, binary()}. |
19 |
|
|
20 |
|
-spec get_metrics(Name :: name()) -> {ok, [metric_result()]}. |
21 |
|
get_metrics(Name) -> |
22 |
20 |
PrepName = prepare_host_types(Name), |
23 |
20 |
Values = mongoose_metrics:get_metric_values(PrepName), |
24 |
20 |
{ok, lists:map(fun make_metric_result/1, Values)}. |
25 |
|
|
26 |
|
-spec get_metrics_as_dicts(Name :: name(), Keys :: [key()]) -> |
27 |
|
{ok, [metric_dict_result()]}. |
28 |
|
get_metrics_as_dicts(Name, Keys) -> |
29 |
18 |
PrepName = prepare_host_types(Name), |
30 |
18 |
Values = mongoose_metrics:get_metric_values(PrepName), |
31 |
18 |
{ok, [make_metric_dict_result(V, Keys) || V <- Values]}. |
32 |
|
|
33 |
|
-spec get_cluster_metrics_as_dicts(Name :: name(), Keys :: [key()], |
34 |
|
Nodes :: [node()]) -> |
35 |
|
{ok, [metric_node_dict_result()]}. |
36 |
|
get_cluster_metrics_as_dicts(Name, Keys, Nodes) -> |
37 |
24 |
PrepName = prepare_host_types(Name), |
38 |
24 |
Nodes2 = prepare_nodes_arg(Nodes), |
39 |
24 |
F = fun(Node) -> |
40 |
34 |
case rpc:call(Node, mongoose_metrics, get_metric_values, [PrepName]) of |
41 |
|
{badrpc, Reason} -> |
42 |
4 |
[{[error, Reason], []}]; |
43 |
|
Result -> |
44 |
30 |
Result |
45 |
|
end |
46 |
|
end, |
47 |
24 |
Results = mongoose_lib:pmap(F, Nodes2), |
48 |
24 |
{ok, [make_node_result(Node, Result, Keys) |
49 |
24 |
|| {Node, Result} <- lists:zip(Nodes2, Results)]}. |
50 |
|
|
51 |
|
make_node_result(Node, {ok, Values}, Keys) -> |
52 |
34 |
{ok, #{<<"node">> => Node, |
53 |
4178 |
<<"result">> => [make_metric_dict_result(V, Keys) || V <- Values]}}; |
54 |
|
make_node_result(Node, Other, _Keys) -> |
55 |
:-( |
?LOG_ERROR(#{what => metric_get_failed, |
56 |
:-( |
remote_node => Node, reason => Other}), |
57 |
:-( |
{error, <<"Failed to get metrics">>}. |
58 |
|
|
59 |
|
filter_keys(Dict, []) -> |
60 |
3386 |
Dict; |
61 |
|
filter_keys(Dict, Keys) -> |
62 |
5924 |
[KV || KV = {Key, _} <- Dict, lists:member(Key, Keys)]. |
63 |
|
|
64 |
|
prepare_nodes_arg([]) -> |
65 |
10 |
[node()|nodes()]; |
66 |
|
prepare_nodes_arg(Nodes) -> |
67 |
14 |
Nodes. |
68 |
|
|
69 |
|
make_metric_result({Name, Dict}) -> |
70 |
4226 |
PreparedName = format_name(Name), |
71 |
4226 |
Map = format_dict(Dict), |
72 |
4226 |
{ok, Map#{<<"name">> => PreparedName}}. |
73 |
|
|
74 |
|
make_metric_dict_result({Name, Dict}, Keys) -> |
75 |
9310 |
PreparedName = format_name(Name), |
76 |
9310 |
{ok, #{<<"name">> => PreparedName, <<"dict">> => format_dict_entries(Dict, Keys)}}. |
77 |
|
|
78 |
|
format_dict_entries(Dict, Keys) -> |
79 |
9310 |
[{ok, #{<<"key">> => Key, <<"value">> => Value}} |
80 |
9310 |
|| {Key, Value} <- filter_keys(Dict, Keys)]. |
81 |
|
|
82 |
|
format_name(Name) -> |
83 |
13536 |
lists:map(fun format_name_segment/1, Name). |
84 |
|
|
85 |
|
format_name_segment(Segment) when is_atom(Segment) -> |
86 |
28676 |
{ok, atom_to_binary(Segment)}; |
87 |
|
format_name_segment(Segment) when is_binary(Segment) -> |
88 |
9050 |
{ok, Segment}. |
89 |
|
|
90 |
|
format_dict(Dict) -> |
91 |
4226 |
format_dict2(maps:from_list(Dict)). |
92 |
|
|
93 |
|
format_dict2(#{one := _} = Dict) -> |
94 |
3242 |
format_spiral(Dict); |
95 |
|
format_dict2(#{ms_since_reset := _} = Dict) -> |
96 |
136 |
format_counter(Dict); |
97 |
|
format_dict2(#{value := _} = Dict) -> |
98 |
64 |
format_gauge(Dict); |
99 |
|
format_dict2(#{median := _} = Dict) -> |
100 |
736 |
format_histogram(Dict); |
101 |
|
format_dict2(#{connections := _, recv_cnt := _} = Dict) -> |
102 |
10 |
format_merged_inet_stats(Dict); |
103 |
|
format_dict2(#{processes_used := _} = Dict) -> |
104 |
10 |
format_vm_stats_memory(Dict); |
105 |
|
format_dict2(#{port_count := _} = Dict) -> |
106 |
10 |
format_vm_system_info(Dict); |
107 |
|
format_dict2(#{fsm := _, regular := _} = Dict) -> |
108 |
10 |
format_probe_queues(Dict); |
109 |
|
format_dict2(#{unavailable_nodes := _, available_nodes := _} = Dict) -> |
110 |
:-( |
mongoose_metrics_probe_cets:format_probe_cets(Dict); |
111 |
|
format_dict2(#{recv_cnt := _, workers := _} = Dict) -> |
112 |
8 |
format_rdbms_stats(Dict). |
113 |
|
|
114 |
|
format_spiral(#{one := One, count := Count}) -> |
115 |
3242 |
#{<<"type">> => <<"spiral">>, <<"one">> => One, <<"count">> => Count}. |
116 |
|
|
117 |
|
format_counter(#{value := Value, ms_since_reset := MS}) -> |
118 |
136 |
#{<<"type">> => <<"counter">>, <<"value">> => Value, <<"ms_since_reset">> => MS}. |
119 |
|
|
120 |
|
format_gauge(#{value := Value}) -> |
121 |
64 |
#{<<"type">> => <<"gauge">>, <<"value">> => Value}. |
122 |
|
|
123 |
|
format_histogram(#{n := N, mean := Mean, min := Min, max := Max, median := Median, |
124 |
|
50 := P50, 75 := P75, 90 := P90, 95 := P95, |
125 |
|
99 := P99, 999 := P999}) -> |
126 |
736 |
#{<<"type">> => <<"histogram">>, <<"n">> => N, <<"mean">> => Mean, |
127 |
|
<<"min">> => Min, <<"max">> => Max, <<"median">> => Median, |
128 |
|
<<"p50">> => P50, <<"p75">> => P75, <<"p90">> => P90, <<"p95">> => P95, |
129 |
|
<<"p99">> => P99, <<"p999">> => P999}. |
130 |
|
|
131 |
|
format_merged_inet_stats(#{connections := Cons, |
132 |
|
recv_cnt := RCnt, recv_max := RMax, recv_oct := ROct, |
133 |
|
send_cnt := SCnt, send_max := SMax, send_oct := SOct, |
134 |
|
send_pend := SPend}) -> |
135 |
|
%% Metrics from a pool of connections |
136 |
10 |
#{<<"type">> => <<"merged_inet_stats">>, <<"connections">> => Cons, |
137 |
|
<<"recv_cnt">> => RCnt, <<"recv_max">> => RMax, <<"recv_oct">> => ROct, |
138 |
|
<<"send_cnt">> => SCnt, <<"send_max">> => SMax, <<"send_oct">> => SOct, |
139 |
|
<<"send_pend">> => SPend}. |
140 |
|
|
141 |
|
format_rdbms_stats(#{recv_cnt := RCnt, recv_max := RMax, recv_oct := ROct, |
142 |
|
send_cnt := SCnt, send_max := SMax, send_oct := SOct, |
143 |
|
send_pend := SPend, workers := Workers}) -> |
144 |
8 |
#{<<"type">> => <<"rdbms_stats">>, <<"workers">> => Workers, |
145 |
|
<<"recv_cnt">> => RCnt, <<"recv_max">> => RMax, <<"recv_oct">> => ROct, |
146 |
|
<<"send_cnt">> => SCnt, <<"send_max">> => SMax, <<"send_oct">> => SOct, |
147 |
|
<<"send_pend">> => SPend}. |
148 |
|
|
149 |
|
format_vm_stats_memory(#{total := Total, processes_used := P, |
150 |
|
atom_used := A, binary := B, ets := E, system := S}) -> |
151 |
10 |
#{<<"type">> => <<"vm_stats_memory">>, |
152 |
|
<<"total">> => Total, <<"processes_used">> => P, <<"atom_used">> => A, |
153 |
|
<<"binary">> => B, <<"ets">> => E, <<"system">> => S}. |
154 |
|
|
155 |
|
format_vm_system_info(#{port_count := PortCount, port_limit := PortLimit, |
156 |
|
process_count := ProcessCount, process_limit := ProcessLimit, |
157 |
|
ets_limit := EtsLimit}) -> |
158 |
10 |
#{<<"type">> => <<"vm_system_info">>, |
159 |
|
<<"port_count">> => PortCount, <<"port_limit">> => PortLimit, |
160 |
|
<<"process_count">> => ProcessCount, <<"process_limit">> => ProcessLimit, |
161 |
|
<<"ets_limit">> => EtsLimit}. |
162 |
|
|
163 |
|
format_probe_queues(#{fsm := FSM, regular := Regular, total := Total}) -> |
164 |
10 |
#{<<"type">> => <<"probe_queues">>, |
165 |
|
<<"fsm">> => FSM, <<"regular">> => Regular, <<"total">> => Total}. |
166 |
|
|
167 |
|
prepare_host_types(Name) -> |
168 |
62 |
lists:map( |
169 |
|
fun(Ele) -> |
170 |
56 |
case lists:member(atom_to_binary(Ele), ?ALL_HOST_TYPES) of |
171 |
|
true -> |
172 |
2 |
binary:replace(atom_to_binary(Ele), <<" ">>, <<"_">>); |
173 |
|
false -> |
174 |
54 |
Ele |
175 |
|
end |
176 |
|
end, |
177 |
|
Name). |