1 |
|
-module(mongoose_system_metrics_collector). |
2 |
|
|
3 |
|
-include("mongoose.hrl"). |
4 |
|
|
5 |
|
-type report_struct() :: |
6 |
|
#{ |
7 |
|
name := term(), |
8 |
|
params := #{term() := term()} |
9 |
|
}. |
10 |
|
|
11 |
|
-export_type([report_struct/0]). |
12 |
|
|
13 |
|
-export([collect/1]). |
14 |
|
|
15 |
|
collect(PrevReport) -> |
16 |
45 |
ReportResults = [ get_reports(RGetter) || RGetter <- report_getters()], |
17 |
45 |
StanzasCount = get_xmpp_stanzas_count(PrevReport), |
18 |
45 |
lists:flatten(ReportResults ++ StanzasCount). |
19 |
|
|
20 |
|
-spec get_reports(fun(() -> [report_struct()])) -> [report_struct()]. |
21 |
|
get_reports(Fun) -> |
22 |
405 |
Fun(). |
23 |
|
|
24 |
|
-spec report_getters() -> [fun(() -> [report_struct()])]. |
25 |
|
report_getters() -> |
26 |
45 |
[ |
27 |
|
fun get_hosts_count/0, |
28 |
|
fun get_domains_count/0, |
29 |
|
fun get_modules/0, |
30 |
|
fun get_number_of_custom_modules/0, |
31 |
|
fun get_cluster_data/0, |
32 |
|
fun get_api/0, |
33 |
|
fun get_transport_mechanisms/0, |
34 |
|
fun get_tls_options/0, |
35 |
|
fun get_outgoing_pools/0 |
36 |
|
]. |
37 |
|
|
38 |
|
get_cluster_data() -> |
39 |
45 |
Steps = [ |
40 |
|
fun get_uptime/0, |
41 |
|
fun get_version/0, |
42 |
|
fun get_components/0, |
43 |
|
fun get_cluster_size/0, |
44 |
|
fun get_config_type/0 |
45 |
|
], |
46 |
45 |
Params = lists:foldl(fun(Step, Acc) -> |
47 |
225 |
maps:merge(Acc, Step()) |
48 |
|
end, #{}, Steps), |
49 |
45 |
[#{name => cluster, params => Params}]. |
50 |
|
|
51 |
|
get_hosts_count() -> |
52 |
45 |
HostTypes = ?ALL_HOST_TYPES, |
53 |
45 |
NumberOfHosts = length(HostTypes), |
54 |
45 |
[#{name => host_count, params => #{value => NumberOfHosts}}]. |
55 |
|
|
56 |
|
get_domains_count() -> |
57 |
45 |
DomainsCount = mongoose_domain_core:domains_count(), |
58 |
45 |
[#{name => domain_count, params => #{value => DomainsCount}}]. |
59 |
|
|
60 |
|
get_modules() -> |
61 |
45 |
HostTypes = ?ALL_HOST_TYPES, |
62 |
45 |
AllModules = lists:flatten([gen_mod:loaded_modules(H) || H <- HostTypes]), |
63 |
45 |
ModulesToReport = filter_behaviour_implementations(lists:usort(AllModules), |
64 |
|
mongoose_module_metrics), |
65 |
45 |
ModsWithOpts = [get_modules_metrics(Host, ModulesToReport) || Host <- HostTypes], |
66 |
45 |
[report_module_with_opts(Mod, Opt) || {Mod, Opt} <- lists:flatten(ModsWithOpts)]. |
67 |
|
|
68 |
|
filter_behaviour_implementations(Modules, Behaviour) -> |
69 |
135 |
lists:filter( |
70 |
|
fun(M) -> |
71 |
4873 |
try lists:keyfind([Behaviour], 2, M:module_info(attributes)) of |
72 |
419 |
{behavior, _} -> true; |
73 |
3948 |
{behaviour, _} -> true; |
74 |
506 |
_ -> false |
75 |
|
catch |
76 |
:-( |
_:_ -> false |
77 |
|
end |
78 |
|
end, Modules). |
79 |
|
|
80 |
|
get_modules_metrics(Host, Modules) -> |
81 |
255 |
lists:map( |
82 |
|
fun(M) -> |
83 |
2678 |
case erlang:function_exported(M, config_metrics, 1) of |
84 |
902 |
true -> {M, M:config_metrics(Host)}; |
85 |
1776 |
false -> {M, [{none, none}]} |
86 |
|
end |
87 |
|
end, Modules). |
88 |
|
|
89 |
|
report_module_with_opts(Module, Opts) -> |
90 |
2678 |
#{name => module_with_opt, params => |
91 |
|
lists:foldl( |
92 |
|
fun |
93 |
|
({none, _}, Acc) -> |
94 |
2137 |
Acc; |
95 |
|
({OptKey, OptValue}, Acc) -> |
96 |
544 |
maps:put(OptKey, OptValue, Acc) |
97 |
|
end, #{module => Module}, Opts) |
98 |
|
}. |
99 |
|
|
100 |
|
get_number_of_custom_modules() -> |
101 |
45 |
HostTypes = ?ALL_HOST_TYPES, |
102 |
45 |
AllModules = lists:flatten( |
103 |
|
lists:map(fun gen_mod:loaded_modules/1, HostTypes)), |
104 |
45 |
GenMods = filter_behaviour_implementations(AllModules, gen_mod), |
105 |
45 |
GenModsSet = sets:from_list(GenMods), |
106 |
45 |
MetricsModule = filter_behaviour_implementations(AllModules, |
107 |
|
mongoose_module_metrics), |
108 |
45 |
MetricsModuleSet = sets:from_list(MetricsModule), |
109 |
45 |
CountCustomMods= sets:size(sets:subtract(GenModsSet, MetricsModuleSet)), |
110 |
45 |
[#{name => custom_module_count, params => #{value => CountCustomMods}}]. |
111 |
|
|
112 |
|
get_uptime() -> |
113 |
45 |
{Uptime, _} = statistics(wall_clock), |
114 |
45 |
UptimeSeconds = Uptime div 1000, |
115 |
45 |
{D, {H, M, S}} = calendar:seconds_to_daystime(UptimeSeconds), |
116 |
45 |
Formatted = io_lib:format("~4..0B-~2..0B:~2..0B:~2..0B", [D,H,M,S]), |
117 |
45 |
#{uptime => list_to_binary(Formatted)}. |
118 |
|
|
119 |
|
get_cluster_size() -> |
120 |
45 |
NodesNo = length(nodes()) + 1, |
121 |
45 |
#{node_number => NodesNo}. |
122 |
|
|
123 |
|
get_version() -> |
124 |
45 |
case lists:keyfind(mongooseim, 1, application:which_applications()) of |
125 |
|
{_, _, Version} -> |
126 |
45 |
#{version => list_to_binary(Version)}; |
127 |
|
_ -> |
128 |
:-( |
[] |
129 |
|
end. |
130 |
|
|
131 |
|
get_components() -> |
132 |
45 |
Domains = mongoose_router:get_all_domains() ++ mongoose_component:dirty_get_all_components(all), |
133 |
45 |
Components = [mongoose_component:lookup_component(D, node()) || D <- Domains], |
134 |
45 |
LenComponents = length(lists:flatten(Components)), |
135 |
45 |
#{component => LenComponents}. |
136 |
|
|
137 |
|
get_api() -> |
138 |
45 |
ApiList = filter_unknown_api(get_http_handler_modules()), |
139 |
45 |
[#{name => http_api, |
140 |
|
params => lists:foldl(fun(Element, Acc) -> |
141 |
180 |
maps:put(Element, enabled, Acc) |
142 |
|
end, #{}, ApiList)}]. |
143 |
|
|
144 |
|
filter_unknown_api(ApiList) -> |
145 |
45 |
AllowedToReport = [mongoose_client_api, mongoose_admin_api, mod_bosh, mod_websockets], |
146 |
45 |
[Api || Api <- ApiList, lists:member(Api, AllowedToReport)]. |
147 |
|
|
148 |
|
get_transport_mechanisms() -> |
149 |
45 |
HTTP = [Mod || Mod <- get_http_handler_modules(), |
150 |
225 |
Mod =:= mod_bosh orelse Mod =:= mod_websockets], |
151 |
45 |
TCP = lists:usort([tcp || #{proto := tcp} <- get_listeners(mongoose_c2s_listener)]), |
152 |
45 |
[#{name => transport_mechanism, |
153 |
|
params => lists:foldl(fun(Element, Acc) -> |
154 |
135 |
maps:put(Element, enabled, Acc) |
155 |
|
end, #{}, HTTP ++ TCP)}]. |
156 |
|
|
157 |
|
get_http_handler_modules() -> |
158 |
90 |
Listeners = get_listeners(ejabberd_cowboy), |
159 |
90 |
lists:usort(lists:flatmap(fun get_http_handler_modules/1, Listeners)). |
160 |
|
|
161 |
|
get_listeners(Module) -> |
162 |
180 |
Listeners = mongoose_config:get_opt(listen), |
163 |
180 |
lists:filter(fun(#{module := Mod}) -> Mod =:= Module end, Listeners). |
164 |
|
|
165 |
|
get_http_handler_modules(#{handlers := Handlers}) -> |
166 |
630 |
[Module || #{module := Module} <- Handlers]. |
167 |
|
|
168 |
|
get_tls_options() -> |
169 |
45 |
TLSOptions = lists:flatmap(fun extract_tls_options/1, get_listeners(mongoose_c2s_listener)), |
170 |
45 |
lists:foldl(fun({Mode, Module}, Acc) -> |
171 |
87 |
[#{name => tls_option, params => #{mode => Mode, module => Module}}] ++ Acc |
172 |
|
end, [], lists:usort(TLSOptions)). |
173 |
|
|
174 |
|
extract_tls_options(#{tls := #{mode := TLSMode, module := TLSModule}}) -> |
175 |
87 |
[{TLSMode, TLSModule}]; |
176 |
:-( |
extract_tls_options(_) -> []. |
177 |
|
|
178 |
|
get_outgoing_pools() -> |
179 |
45 |
OutgoingPools = mongoose_config:get_opt(outgoing_pools), |
180 |
45 |
[#{name => outgoing_pool, |
181 |
45 |
params => #{value => Type}} || #{type := Type} <- OutgoingPools]. |
182 |
|
|
183 |
|
get_xmpp_stanzas_count(PrevReport) -> |
184 |
45 |
StanzaTypes = [xmppMessageSent, xmppMessageReceived, xmppIqSent, |
185 |
|
xmppIqReceived, xmppPresenceSent, xmppPresenceReceived], |
186 |
45 |
NewCount = [count_stanzas(StanzaType) || StanzaType <- StanzaTypes], |
187 |
45 |
StanzasCount = calculate_stanza_rate(PrevReport, NewCount), |
188 |
45 |
[#{name => xmpp_stanza_count, |
189 |
|
params => #{ |
190 |
|
stanza_type => StanzaType, |
191 |
|
total => Total, |
192 |
|
increment => Increment |
193 |
45 |
}} || {StanzaType, Total, Increment} <- StanzasCount]. |
194 |
|
|
195 |
|
count_stanzas(StanzaType) -> |
196 |
270 |
ExometerResults = exometer:get_values(['_', StanzaType]), |
197 |
270 |
StanzaCount = lists:foldl(fun({ _, [{count,Count}, {one, _}]}, Sum) -> |
198 |
1482 |
Count + Sum end, 0, ExometerResults), |
199 |
270 |
{StanzaType, StanzaCount}. |
200 |
|
|
201 |
|
calculate_stanza_rate([], NewCount) -> |
202 |
21 |
[{Type, Count, Count} || {Type, Count} <- NewCount]; |
203 |
|
calculate_stanza_rate(PrevReport, NewCount) -> |
204 |
24 |
ReportProplist = [{Name, Key} || |
205 |
|
#{name := xmpp_stanza_count, |
206 |
24 |
params := #{stanza_type := Name, total := Key}} <- PrevReport], |
207 |
24 |
[{Type, Count, |
208 |
|
case proplists:get_value(Type, ReportProplist) of |
209 |
:-( |
undefined -> Count; |
210 |
144 |
Total -> Count-Total |
211 |
24 |
end} || {Type, Count} <- NewCount]. |
212 |
|
|
213 |
|
get_config_type() -> |
214 |
45 |
ConfigPath = mongoose_config:get_config_path(), |
215 |
45 |
ConfigType = case filename:extension(ConfigPath) of |
216 |
45 |
".toml" -> toml; |
217 |
:-( |
".cfg" -> cfg; |
218 |
:-( |
_ -> unknown_config_type |
219 |
|
end, |
220 |
45 |
#{config_type => ConfigType}. |