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