./ct_report/coverage/mongoose_system_metrics_collector.COVER.html

1 -module(mongoose_system_metrics_collector).
2
3 -include("mongoose.hrl").
4
5 -type report_struct() ::
6 #{
7 report_name := term(),
8 key := term(),
9 value := term()
10 }.
11
12 -export_type([report_struct/0]).
13
14 -export([collect/1]).
15
16 collect(PrevReport) ->
17 33 ReportResults = [ get_reports(RGetter) || RGetter <- report_getters()],
18 33 StanzasCount = get_xmpp_stanzas_count(PrevReport),
19 33 lists:flatten(ReportResults ++ StanzasCount).
20
21 -spec get_reports(fun(() -> [report_struct()])) -> [report_struct()].
22 get_reports(Fun) ->
23 429 Fun().
24
25 -spec report_getters() -> [fun(() -> [report_struct()])].
26 report_getters() ->
27 33 [
28 fun get_hosts_count/0,
29 fun get_domains_count/0,
30 fun get_modules/0,
31 fun get_number_of_custom_modules/0,
32 fun get_uptime/0,
33 fun get_cluster_size/0,
34 fun get_version/0,
35 fun get_components/0,
36 fun get_api/0,
37 fun get_transport_mechanisms/0,
38 fun get_tls_options/0,
39 fun get_outgoing_pools/0,
40 fun get_config_type/0
41 ].
42
43 get_hosts_count() ->
44 33 HostTypes = ?ALL_HOST_TYPES,
45 33 NumberOfHosts = length(HostTypes),
46 33 [#{report_name => hosts, key => count, value => NumberOfHosts}].
47
48 get_domains_count() ->
49 33 DomainsCount = mongoose_domain_core:domains_count(),
50 33 [#{report_name => domains, key => count, value => DomainsCount}].
51
52 get_modules() ->
53 33 HostTypes = ?ALL_HOST_TYPES,
54 33 AllModules = lists:flatten([gen_mod:loaded_modules(H) || H <- HostTypes]),
55 33 ModulesToReport = filter_behaviour_implementations(lists:usort(AllModules),
56 mongoose_module_metrics),
57 33 ModsWithOpts = [get_modules_metrics(Host, ModulesToReport) || Host <- HostTypes],
58 33 [report_module_with_opts(Mod, Opt) || {Mod, Opt} <- lists:flatten(ModsWithOpts)].
59
60 filter_behaviour_implementations(Modules, Behaviour) ->
61 99 lists:filter(
62 fun(M) ->
63 4095 try lists:keyfind([Behaviour], 2, M:module_info(attributes)) of
64 116 {behavior, _} -> true;
65 3834 {behaviour, _} -> true;
66 145 _ -> false
67 catch
68
:-(
_:_ -> false
69 end
70 end, Modules).
71
72 get_modules_metrics(Host, Modules) ->
73 188 lists:map(
74 fun(M) ->
75 2645 case erlang:function_exported(M, config_metrics, 1) of
76 779 true -> {M, M:config_metrics(Host)};
77 1866 false -> {M, [{none, none}]}
78 end
79 end, Modules).
80
81 report_module_with_opts(Module, Opts) ->
82 2645 lists:map(
83 fun({OptKey, OptValue}) ->
84 2645 #{report_name => Module, key => OptKey, value => OptValue}
85 end,Opts).
86
87 get_number_of_custom_modules() ->
88 33 HostTypes = ?ALL_HOST_TYPES,
89 33 AllModules = lists:flatten(
90 lists:map(fun gen_mod:loaded_modules/1, HostTypes)),
91 33 GenMods = filter_behaviour_implementations(AllModules, gen_mod),
92 33 GenModsSet = sets:from_list(GenMods),
93 33 MetricsModule = filter_behaviour_implementations(AllModules,
94 mongoose_module_metrics),
95 33 MetricsModuleSet = sets:from_list(MetricsModule),
96 33 CountCustomMods= sets:size(sets:subtract(GenModsSet, MetricsModuleSet)),
97 33 #{report_name => custom_modules, key => count, value => CountCustomMods}.
98
99 get_uptime() ->
100 33 {Uptime, _} = statistics(wall_clock),
101 33 UptimeSeconds = Uptime div 1000,
102 33 {D, {H, M, S}} = calendar:seconds_to_daystime(UptimeSeconds),
103 33 Formatted = io_lib:format("~4..0B-~2..0B:~2..0B:~2..0B", [D,H,M,S]),
104 33 [#{report_name => cluster, key => uptime, value => list_to_binary(Formatted)}].
105
106 get_cluster_size() ->
107 33 NodesNo = length(nodes()) + 1,
108 33 [#{report_name => cluster, key => number_of_nodes, value => NodesNo}].
109
110 get_version() ->
111 33 case lists:keyfind(mongooseim, 1, application:which_applications()) of
112 {_, _, Version} ->
113 33 #{report_name => cluster, key => mim_version, value => list_to_binary(Version)};
114 _ ->
115
:-(
[]
116 end.
117
118 get_components() ->
119 33 Domains = mongoose_router:get_all_domains() ++ ejabberd_router:dirty_get_all_components(all),
120 33 Components = [ejabberd_router:lookup_component(D, node()) || D <- Domains],
121 33 LenComponents = length(lists:flatten(Components)),
122 33 #{report_name => cluster, key => number_of_components, value => LenComponents}.
123
124 get_api() ->
125 33 ApiList = filter_unknown_api(get_http_handler_modules()),
126 33 [#{report_name => http_api, key => Api, value => enabled} || Api <- ApiList].
127
128 filter_unknown_api(ApiList) ->
129 33 AllowedToReport = [ mongoose_api, mongoose_client_api_rooms_messages,
130 mongoose_client_api_rooms_users, mongoose_client_api_rooms_config,
131 mongoose_client_api_rooms, mongoose_client_api_contacts,
132 mongoose_client_api_messages, lasse_handler, mongoose_api_admin,
133 mod_bosh, mod_websockets],
134 33 [Api || Api <- ApiList, lists:member(Api, AllowedToReport)].
135
136 get_transport_mechanisms() ->
137 33 HTTP = [Mod || Mod <- get_http_handler_modules(),
138 528 Mod =:= mod_bosh orelse Mod =:= mod_websockets],
139 33 TCP = lists:usort([tcp || #{proto := tcp} <- get_listeners(ejabberd_c2s)]),
140 33 [#{report_name => transport_mechanism,
141 key => Transport,
142 33 value => enabled} || Transport <- HTTP ++ TCP].
143
144 get_http_handler_modules() ->
145 66 Listeners = get_listeners(ejabberd_cowboy),
146 66 Modules = lists:flatten([Modules || #{handlers := Modules} <- Listeners]),
147 % Modules Option can have variable number of elements. To be more
148 % error-proof, extracting 3rd element instead of pattern matching.
149 66 lists:usort(lists:map(fun(Module) -> element(3, Module) end, Modules)).
150
151 get_listeners(Module) ->
152 132 Listeners = mongoose_config:get_opt(listen),
153 132 lists:filter(fun(#{module := Mod}) -> Mod =:= Module end, Listeners).
154
155 get_tls_options() ->
156 33 TLSOptions = lists:flatmap(fun extract_tls_options/1, get_listeners(ejabberd_c2s)),
157 33 [#{report_name => tls_option, key => TLSMode, value => TLSModule} ||
158 33 {TLSMode, TLSModule} <- lists:usort(TLSOptions)].
159
160 extract_tls_options(#{tls := Opts}) ->
161 36 Modes = [starttls, starttls_required, tls],
162 36 case [Opt || Opt <- Opts, lists:member(Opt, Modes)] of
163 [TLSMode] ->
164 36 TLSModule = proplists:get_value(tls_module, Opts, fast_tls),
165 36 [{TLSMode, TLSModule}];
166 _ ->
167
:-(
[]
168 end;
169 29 extract_tls_options(_) -> [].
170
171 get_outgoing_pools() ->
172 33 OutgoingPools = mongoose_config:get_opt(outgoing_pools, []),
173 33 [#{report_name => outgoing_pools,
174 key => type,
175 33 value => Type} || #{type := Type} <- OutgoingPools].
176
177 get_xmpp_stanzas_count(PrevReport) ->
178 33 StanzaTypes = [xmppMessageSent, xmppMessageReceived, xmppIqSent,
179 xmppIqReceived, xmppPresenceSent, xmppPresenceReceived],
180 33 NewCount = [count_stanzas(StanzaType) || StanzaType <- StanzaTypes],
181 33 StanzasCount = calculate_stanza_rate(PrevReport, NewCount),
182 33 [#{report_name => StanzaType,
183 key => Total,
184 33 value => Increment} || {StanzaType, Total, Increment} <- StanzasCount].
185
186 count_stanzas(StanzaType) ->
187 198 ExometerResults = exometer:get_values(['_', StanzaType]),
188 198 StanzaCount = lists:foldl(fun({ _, [{count,Count}, {one, _}]}, Sum) ->
189 1080 Count + Sum end, 0, ExometerResults),
190 198 {StanzaType, StanzaCount}.
191
192 calculate_stanza_rate([], NewCount) ->
193 19 [{Type, Count, Count} || {Type, Count} <- NewCount];
194 calculate_stanza_rate(PrevReport, NewCount) ->
195 14 ReportProplist = [{Name, Key} ||
196 14 #{report_name := Name, key := Key} <- PrevReport],
197 14 [{Type, Count,
198 case proplists:get_value(Type, ReportProplist) of
199
:-(
undefined -> Count;
200 84 Total -> Count-Total
201 14 end} || {Type, Count} <- NewCount].
202
203 get_config_type() ->
204 33 ConfigPath = mongoose_config:get_config_path(),
205 33 ConfigType = case filename:extension(ConfigPath) of
206 33 ".toml" -> toml;
207
:-(
".cfg" -> cfg;
208
:-(
_ -> unknown_config_type
209 end,
210 33 [#{report_name => cluster, key => config_type, value => ConfigType}].
Line Hits Source