./ct_report/coverage/mongoose_metrics.COVER.html

1 %%==============================================================================
2 %% Copyright 2014 Erlang Solutions Ltd.
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15 %%==============================================================================
16 -module(mongoose_metrics).
17
18 -include("mongoose.hrl").
19 -include("mongoose_metrics_definitions.hrl").
20
21 %% API
22 -export([init/0,
23 init_mongooseim_metrics/0,
24 create_generic_hook_metric/2,
25 create_probe_metric/3,
26 ensure_db_pool_metric/1,
27 update/3,
28 ensure_metric/3,
29 ensure_subscribed_metric/3,
30 get_metric_value/1,
31 get_metric_values/1,
32 get_metric_value/2,
33 sample_metric/1,
34 get_host_type_metric_names/1,
35 get_global_metric_names/0,
36 get_aggregated_values/1,
37 increment_generic_hook_metric/2,
38 get_rdbms_data_stats/0,
39 get_rdbms_data_stats/1,
40 get_dist_data_stats/0,
41 get_up_time/0,
42 get_mnesia_running_db_nodes_count/0,
43 remove_host_type_metrics/1,
44 remove_all_metrics/0,
45 get_report_interval/0,
46 get_host_type_prefix/1
47 ]).
48
49 -ignore_xref([get_dist_data_stats/0, get_mnesia_running_db_nodes_count/0,
50 get_rdbms_data_stats/0, get_rdbms_data_stats/1, get_up_time/0,
51 remove_host_type_metrics/1, get_report_interval/0,
52 sample_metric/1, get_metric_value/1]).
53
54 -define(PREFIXES, mongoose_metrics_prefixes).
55 -define(DEFAULT_REPORT_INTERVAL, 60000). %%60s
56
57 -type use_or_skip() :: use | skip.
58 -type hook_name() :: atom().
59 -type metric_name() :: atom() | list(atom() | binary()).
60 -type short_metric_type() :: spiral | histogram | counter | gauge.
61 -type metric_type() :: tuple() | short_metric_type().
62
63 %% ---------------------------------------------------------------------
64 %% API
65 %% ---------------------------------------------------------------------
66
67 -spec init() -> ok.
68 init() ->
69 100 mongoose_instrument_registry:start(), % TODO move it out of this module
70 100 prepare_prefixes(),
71 100 create_vm_metrics(),
72 100 create_global_metrics(?GLOBAL_COUNTERS),
73 100 create_data_metrics(),
74 100 create_host_type_metrics().
75
76 -spec init_mongooseim_metrics() -> ok.
77 init_mongooseim_metrics() ->
78 100 create_host_type_hook_metrics(),
79 100 create_global_metrics(?MNESIA_COUNTERS),
80 100 init_subscriptions().
81
82 init_subscriptions() ->
83 100 Reporters = exometer_report:list_reporters(),
84 100 lists:foreach(
85 fun({Name, _ReporterPid}) ->
86
:-(
Interval = get_report_interval(),
87
:-(
subscribe_to_all(Name, Interval)
88 end, Reporters).
89
90 -spec create_generic_hook_metric(mongooseim:host_type(), atom()) ->
91 ok | {ok, already_present} | {error, any()}.
92 create_generic_hook_metric(HostType, Hook) ->
93 28895 UseOrSkip = filter_hook(Hook),
94 28895 do_create_generic_hook_metric(HostType, Hook, UseOrSkip).
95
96 -spec create_probe_metric(mongooseim:host_type_or_global(), atom(), module()) ->
97 ok | {ok, already_present} | {error, any()}.
98 create_probe_metric(HostType, Name, Module) ->
99 300 {Metric, Spec} = ?PROBE(Name, Module),
100 300 ensure_metric(HostType, Metric, Spec).
101
102 % TODO: change to HostType after mongoose_wpool_rdbms
103 ensure_db_pool_metric({rdbms, Host, Tag} = Name) ->
104
:-(
ensure_metric(Host,
105 [data, rdbms, Tag],
106 {function, mongoose_metrics, get_rdbms_data_stats, [[Name]], proplist,
107 [workers | ?INET_STATS]}).
108
109 -spec update(HostType :: mongooseim:host_type_or_global(), Name :: term() | list(),
110 Change :: term()) -> any().
111 update(HostType, Name, Change) when is_list(Name) ->
112 411281 exometer:update(name_by_all_metrics_are_global(HostType, Name), Change);
113 update(HostType, Name, Change) ->
114 206570 update(HostType, [Name], Change).
115
116 -spec ensure_metric(mongooseim:host_type_or_global(), metric_name(), metric_type()) ->
117 ok | {ok, already_present} | {error, any()}.
118 ensure_metric(HostType, Metric, Type) when is_tuple(Type) ->
119 1069 ensure_metric(HostType, Metric, Type, element(1, Type));
120 ensure_metric(HostType, Metric, Type) ->
121 62440 ensure_metric(HostType, Metric, Type, Type).
122
123 get_metric_value(HostType, Name) when is_list(Name) ->
124 259 get_metric_value(name_by_all_metrics_are_global(HostType, Name));
125 get_metric_value(HostType, Name) ->
126 256 get_metric_value(HostType, [Name]).
127
128 get_metric_value(Metric) ->
129 339 exometer:get_value(Metric).
130
131 get_metric_values(Metric) when is_list(Metric) ->
132 228 exometer:get_values(Metric);
133 get_metric_values(HostType) ->
134 184 exometer:get_values([HostType]).
135
136 %% Force update a probe metric
137 sample_metric(Metric) ->
138 6 exometer:sample(Metric).
139
140 get_host_type_metric_names(HostType) ->
141 91 HostTypeName = get_host_type_prefix(HostType),
142 91 [MetricName || {[_HostTypeName | MetricName], _, _} <- exometer:find_entries([HostTypeName])].
143
144 get_global_metric_names() ->
145 2 get_host_type_metric_names(global).
146
147 get_aggregated_values(Metric) ->
148 6613 exometer:aggregate([{{['_', Metric], '_', '_'}, [], [true]}], [one, count, value]).
149
150 -spec increment_generic_hook_metric(mongooseim:host_type_or_global(), atom()) -> ok | {error, any()}.
151 increment_generic_hook_metric(HostType, Hook) ->
152 186192 UseOrSkip = filter_hook(Hook),
153 186192 do_increment_generic_hook_metric(HostType, Hook, UseOrSkip).
154
155 get_rdbms_data_stats() ->
156
:-(
Pools = lists:filter(fun({Type, _Host, _Tag}) -> Type == rdbms end, mongoose_wpool:get_pools()),
157
:-(
get_rdbms_data_stats(Pools).
158
159 get_rdbms_data_stats(Pools) ->
160
:-(
RDBMSWorkers =
161 lists:flatmap(
162 fun({Type, Host, Tag}) ->
163
:-(
PoolName = mongoose_wpool:make_pool_name(Type, Host, Tag),
164
:-(
Wpool = wpool_pool:find_wpool(PoolName),
165
:-(
PoolSize = wpool_pool:wpool_get(size, Wpool),
166
:-(
[whereis(wpool_pool:worker_name(PoolName, I)) || I <- lists:seq(1, PoolSize)]
167 end,
168 Pools),
169
170
:-(
get_rdbms_stats(RDBMSWorkers).
171
172 get_dist_data_stats() ->
173 132 DistStats = [dist_inet_stats(PortOrPid) || {_, PortOrPid} <- erlang:system_info(dist_ctrl)],
174 132 [{connections, length(DistStats)} | merge_stats(DistStats)].
175
176 -spec get_up_time() -> {value, integer()}.
177 get_up_time() ->
178 130 {value, erlang:round(element(1, erlang:statistics(wall_clock))/1000)}.
179
180 -spec get_mnesia_running_db_nodes_count() -> {value, non_neg_integer()}.
181 get_mnesia_running_db_nodes_count() ->
182 132 {value, length(mnesia:system_info(running_db_nodes))}.
183
184 remove_host_type_metrics(HostType) ->
185
:-(
HostTypeName = get_host_type_prefix(HostType),
186
:-(
lists:foreach(fun remove_metric/1, exometer:find_entries([HostTypeName])).
187
188 remove_all_metrics() ->
189 100 persistent_term:erase(?PREFIXES),
190 100 lists:foreach(fun remove_metric/1, exometer:find_entries([])).
191
192 %% ---------------------------------------------------------------------
193 %% Internal functions
194 %% ---------------------------------------------------------------------
195
196 prepare_prefixes() ->
197 100 Prebuilt = maps:from_list([begin
198 535 Prefix = make_host_type_prefix(HT),
199 535 {Prefix, Prefix}
200 100 end || HT <- ?ALL_HOST_TYPES ]),
201 100 Prefixes = maps:from_list([ {HT, make_host_type_prefix(HT)}
202 100 || HT <- ?ALL_HOST_TYPES ]),
203 100 persistent_term:put(?PREFIXES, maps:merge(Prebuilt, Prefixes)).
204
205 -spec all_metrics_are_global() -> boolean().
206 all_metrics_are_global() ->
207 475097 mongoose_config:get_opt(all_metrics_are_global).
208
209 get_host_type_prefix(global) ->
210 209154 global;
211 get_host_type_prefix(HostType) when is_binary(HostType) ->
212 235207 case persistent_term:get(?PREFIXES, #{}) of
213 235206 #{HostType := HostTypePrefix} -> HostTypePrefix;
214 1 #{} -> make_host_type_prefix(HostType)
215 end.
216
217 make_host_type_prefix(HT) when is_binary(HT) ->
218 1071 binary:replace(HT, <<" ">>, <<"_">>, [global]).
219
220 pick_prefix_by_all_metrics_are_global(HostType) ->
221 475097 case all_metrics_are_global() of
222 30827 true -> global;
223 444270 false -> get_host_type_prefix(HostType)
224 end.
225
226 pick_by_all_metrics_are_global(WhenGlobal, WhenNot) ->
227
:-(
case all_metrics_are_global() of
228
:-(
true -> WhenGlobal;
229
:-(
false -> WhenNot
230 end.
231
232 -spec name_by_all_metrics_are_global(HostType :: mongooseim:host_type_or_global(),
233 Name :: list()) -> FinalName :: list().
234 name_by_all_metrics_are_global(HostType, Name) ->
235 475097 [pick_prefix_by_all_metrics_are_global(HostType) | Name].
236
237 get_report_interval() ->
238 572 application:get_env(exometer_core, mongooseim_report_interval,
239 ?DEFAULT_REPORT_INTERVAL).
240
241 -spec do_create_generic_hook_metric(HostType :: mongooseim:host_type_or_global(),
242 Hook :: hook_name(),
243 UseOrSkip :: use_or_skip()) ->
244 ok | {ok, already_present} | {error, any()}.
245 do_create_generic_hook_metric(_, _, skip) ->
246 10378 ok;
247 do_create_generic_hook_metric(HostType, Hook, use) ->
248 18517 ensure_metric(HostType, Hook, spiral).
249
250 -spec do_increment_generic_hook_metric(HostType :: mongooseim:host_type_or_global(),
251 Hook :: hook_name(),
252 UseOrSkip :: use_or_skip()) ->
253 ok | {error, any()}.
254 do_increment_generic_hook_metric(_, _, skip) ->
255 99452 ok;
256 do_increment_generic_hook_metric(HostType, Hook, use) ->
257 86740 update(HostType, Hook, 1).
258
259 get_rdbms_stats(RDBMSWorkers) ->
260
:-(
RDBMSConnections = [{catch mongoose_rdbms:get_db_info(Pid), Pid} || Pid <- RDBMSWorkers],
261
:-(
Ports = [get_port_from_rdbms_connection(Conn) || Conn <- RDBMSConnections],
262
:-(
PortStats = [inet_stats(Port) || Port <- lists:flatten(Ports)],
263
:-(
[{workers, length(RDBMSConnections)} | merge_stats(PortStats)].
264
265 get_port_from_rdbms_connection({{ok, DB, Pid}, _WorkerPid}) when DB =:= mysql;
266 DB =:= pgsql ->
267
:-(
ProcState = sys:get_state(Pid),
268
:-(
get_port_from_proc_state(DB, ProcState);
269 get_port_from_rdbms_connection({{ok, odbc, Pid}, WorkerPid}) ->
270
:-(
Links = element(2, erlang:process_info(Pid, links)) -- [WorkerPid],
271
:-(
[Port || Port <- Links, is_port(Port), {name, "tcp_inet"} == erlang:port_info(Port, name)];
272 get_port_from_rdbms_connection(_) ->
273
:-(
undefined.
274
275 %% @doc Gets a socket from mysql/epgsql library Gen_server state
276 get_port_from_proc_state(mysql, State) ->
277 %% -record(state, {server_version, connection_id, socket, sockmod, ssl_opts,
278 %% host, port, user, password, log_warnings,
279 %% ping_timeout,
280 %% query_timeout, query_cache_time,
281 %% affected_rows = 0, status = 0, warning_count = 0, insert_id = 0,
282 %% transaction_level = 0, ping_ref = undefined,
283 %% stmts = dict:new(), query_cache = empty, cap_found_rows = false}).
284
:-(
SockInfo = element(4, State),
285
:-(
get_port_from_sock(SockInfo);
286 get_port_from_proc_state(pgsql, State) ->
287 %% -record(state, {mod,
288 %% sock,
289 %% data = <<>>,
290 %% backend,
291 %% handler,
292 %% codec,
293 %% queue = queue:new(),
294 %% async,
295 %% parameters = [],
296 %% types = [],
297 %% columns = [],
298 %% rows = [],
299 %% results = [],
300 %% batch = [],
301 %% sync_required,
302 %% txstatus,
303 %% complete_status :: undefined | atom() | {atom(), integer()},
304 %% repl_last_received_lsn,
305 %% repl_last_flushed_lsn,
306 %% repl_last_applied_lsn,
307 %% repl_feedback_required,
308 %% repl_cbmodule,
309 %% repl_cbstate,
310 %% repl_receiver}).
311
:-(
SockInfo = element(3, State),
312
:-(
get_port_from_sock(SockInfo).
313
314 get_port_from_sock({sslsocket, {_, Port, _, _}, _}) ->
315
:-(
Port;
316 get_port_from_sock(Port) ->
317
:-(
Port.
318
319 merge_stats(Stats) ->
320 132 OrdDict = lists:foldl(fun(Stat, Acc) ->
321 275 StatDict = orddict:from_list(Stat),
322 275 orddict:merge(fun merge_stats_fun/3, Acc, StatDict)
323 end, orddict:from_list(?EMPTY_INET_STATS), Stats),
324
325 132 orddict:to_list(OrdDict).
326
327 merge_stats_fun(recv_max, V1, V2) ->
328 275 erlang:max(V1, V2);
329 merge_stats_fun(send_max, V1, V2) ->
330 275 erlang:max(V1, V2);
331 merge_stats_fun(_, V1, V2) ->
332 1375 V1 + V2.
333
334 dist_inet_stats(Pid) when is_pid(Pid) ->
335 275 try
336 275 {ok, {sslsocket, FD, _Pids}} = tls_sender:dist_tls_socket(Pid),
337 275 gen_tcp = element(1, FD),
338 275 inet_stats(element(2, FD))
339 catch C:R:S ->
340
:-(
?LOG_INFO(#{what => dist_inet_stats_failed, class => C, reason => R, stacktrace => S}),
341
:-(
?EMPTY_INET_STATS
342 end;
343 dist_inet_stats(Port) ->
344
:-(
inet_stats(Port).
345
346 inet_stats(Port) ->
347 275 try
348 275 {ok, Stats} = inet:getstat(Port, ?INET_STATS),
349 275 Stats
350 catch C:R:S ->
351
:-(
?LOG_INFO(#{what => inet_stats_failed, class => C, reason => R, stacktrace => S}),
352
:-(
?EMPTY_INET_STATS
353 end.
354
355 remove_metric({Name, _, _}) ->
356 28188 exometer_admin:delete_entry(Name).
357
358 %% decided whether to use a metric for given hook or not
359 -spec filter_hook(hook_name()) -> use_or_skip().
360 3874 filter_hook(sm_register_connection_hook) -> skip;
361 3870 filter_hook(sm_remove_connection_hook) -> skip;
362 597 filter_hook(auth_failed) -> skip;
363 11661 filter_hook(user_send_packet) -> skip;
364 2172 filter_hook(user_send_message) -> skip;
365 4313 filter_hook(user_send_presence) -> skip;
366 3827 filter_hook(user_send_iq) -> skip;
367 17831 filter_hook(user_receive_packet) -> skip;
368 2992 filter_hook(user_receive_message) -> skip;
369 8852 filter_hook(user_receive_presence) -> skip;
370 5115 filter_hook(user_receive_iq) -> skip;
371 568 filter_hook(xmpp_bounce_message) -> skip;
372 579 filter_hook(xmpp_stanza_dropped) -> skip;
373 29035 filter_hook(xmpp_send_element) -> skip;
374 664 filter_hook(roster_get) -> skip;
375 565 filter_hook(roster_set) -> skip;
376 1101 filter_hook(roster_push) -> skip;
377 3576 filter_hook(register_user) -> skip;
378 3571 filter_hook(remove_user) -> skip;
379 597 filter_hook(privacy_iq_get) -> skip;
380 674 filter_hook(privacy_iq_set) -> skip;
381 3796 filter_hook(privacy_check_packet) -> skip;
382
:-(
filter_hook(mam_get_prefs) -> skip;
383
:-(
filter_hook(mam_set_prefs) -> skip;
384
:-(
filter_hook(mam_remove_archive) -> skip;
385
:-(
filter_hook(mam_archive_message) -> skip;
386
:-(
filter_hook(mam_muc_get_prefs) -> skip;
387
:-(
filter_hook(mam_muc_set_prefs) -> skip;
388
:-(
filter_hook(mam_muc_remove_archive) -> skip;
389
:-(
filter_hook(mam_muc_lookup_messages) -> skip;
390
:-(
filter_hook(mam_muc_archive_message) -> skip;
391
:-(
filter_hook(mam_muc_flush_messages) -> skip;
392
393 105257 filter_hook(_) -> use.
394
395 create_global_metrics(Metrics) ->
396 200 lists:foreach(fun({Metric, Spec}) -> ensure_metric(global, Metric, Spec) end, Metrics).
397
398 create_vm_metrics() ->
399 100 lists:foreach(fun({Metric, FunSpec, DataPoints}) ->
400 200 FunSpecTuple = list_to_tuple(FunSpec ++ [DataPoints]),
401 200 catch ensure_metric(global, Metric, FunSpecTuple)
402 end, ?VM_STATS).
403
404 -spec create_host_type_metrics() -> ok.
405 create_host_type_metrics() ->
406 100 lists:foreach(fun create_host_type_metrics/1, ?ALL_HOST_TYPES).
407
408 -spec create_host_type_metrics(mongooseim:host_type()) -> 'ok'.
409 create_host_type_metrics(HostType) ->
410 535 lists:foreach(fun(Name) -> ensure_metric(HostType, Name, spiral) end, ?GENERAL_SPIRALS),
411 535 lists:foreach(fun(Name) -> ensure_metric(HostType, Name, histogram) end, ?GENERAL_HISTOGRAMS),
412 535 lists:foreach(fun(Name) -> ensure_metric(HostType, Name, counter) end, ?TOTAL_COUNTERS).
413
414 -spec create_host_type_hook_metrics() -> ok.
415 create_host_type_hook_metrics() ->
416 100 lists:foreach(fun create_host_type_hook_metrics/1, ?ALL_HOST_TYPES).
417
418 -spec create_host_type_hook_metrics(mongooseim:host_type()) -> 'ok'.
419 create_host_type_hook_metrics(HostType) ->
420 535 Hooks = mongoose_metrics_hooks:get_hooks(HostType),
421 535 gen_hook:add_handlers(Hooks).
422
423 ensure_metric(HostType, Metric, Type, ShortType) when is_atom(Metric) ->
424 37607 ensure_metric(HostType, [Metric], Type, ShortType);
425
426 ensure_metric(HostType, Metric, Type, probe = ShortType) ->
427 548 PrefixedMetric = name_by_all_metrics_are_global(HostType, Metric),
428 548 {ShortType, Opts} = Type,
429 548 case exometer:info(PrefixedMetric, type) of
430 undefined ->
431 508 ExometerOpts = [{module, mongoose_metrics_probe}, {type, ShortType}] ++ Opts,
432 508 do_create_metric(PrefixedMetric, ad_hoc, ExometerOpts);
433 _ ->
434 40 {ok, already_present}
435 end;
436 ensure_metric(HostType, Metric, Type, ShortType) when is_list(Metric) ->
437 %% the split into ShortType and Type is needed because function metrics are
438 %% defined as tuples (that is Type), while exometer:info returns only 'function'
439 62961 PrefixedMetric = name_by_all_metrics_are_global(HostType, Metric),
440 62961 case exometer:info(PrefixedMetric, type) of
441 undefined ->
442 28004 do_create_metric(PrefixedMetric, Type, []);
443 34957 ShortType -> {ok, already_present}
444 end.
445
446 %% @doc Creates a metric and subcribes it to the reporters
447 -spec ensure_subscribed_metric(HostType :: mongooseim:host_type_or_global(),
448 Metric :: metric_name(),
449 Type :: metric_type()) -> ok | term().
450 ensure_subscribed_metric(HostType, Metric, Type) ->
451 5681 case ensure_metric(HostType, Metric, Type) of
452 ok ->
453 48 PrefixedMetric = name_by_all_metrics_are_global(HostType, Metric),
454 48 Reporters = exometer_report:list_reporters(),
455 48 Interval = get_report_interval(),
456 48 lists:foreach(
457 fun({Reporter, _Pid}) ->
458
:-(
FullMetric = {PrefixedMetric, Type, []},
459
:-(
subscribe_metric(Reporter, FullMetric, Interval)
460 end,
461 Reporters);
462 {ok, already_present} ->
463 5633 ?LOG_DEBUG(#{what => metric_already_present,
464 5633 host_type => HostType, metric => Metric, type => Type}),
465 5633 ok;
466 Other ->
467
:-(
?LOG_WARNING(#{what => cannot_create_metric, reason => Other,
468
:-(
host_type => HostType, metric => Metric,type => Type}),
469
:-(
Other
470 end.
471
472 do_create_metric(PrefixedMetric, ExometerType, ExometerOpts) ->
473 28512 case catch exometer:new(PrefixedMetric, ExometerType, ExometerOpts) of
474
:-(
{'EXIT', {exists, _}} -> {ok, already_present};
475 28512 ok -> ok;
476
:-(
{'EXIT', Error} -> {error, Error}
477 end.
478
479 create_data_metrics() ->
480 100 lists:foreach(fun(Metric) -> ensure_metric(global, Metric, histogram) end,
481 ?GLOBAL_HISTOGRAMS),
482 100 lists:foreach(fun(Metric) -> ensure_metric(global, Metric, spiral) end,
483 ?GLOBAL_SPIRALS),
484 100 lists:foreach(fun({Metric, Spec}) -> ensure_metric(global, Metric, Spec) end,
485 ?DATA_FUN_METRICS).
486
487 start_metrics_subscriptions(Reporter, MetricPrefix, Interval) ->
488
:-(
[subscribe_metric(Reporter, Metric, Interval)
489
:-(
|| Metric <- exometer:find_entries(MetricPrefix)].
490
491 subscribe_metric(Reporter, {Name, counter, _}, Interval) ->
492
:-(
subscribe_verbose(Reporter, Name, [value], Interval);
493 subscribe_metric(Reporter, {Name, histogram, _}, Interval) ->
494
:-(
subscribe_verbose(Reporter, Name, [min, mean, max, median, 95, 99, 999], Interval);
495 subscribe_metric(Reporter, {Name, _, _}, Interval) ->
496
:-(
subscribe_verbose(Reporter, Name, default, Interval).
497
498 subscribe_verbose(Reporter, Name, Types, Interval) ->
499
:-(
case exometer_report:subscribe(Reporter, Name, Types, Interval) of
500
:-(
ok -> ok;
501 Other ->
502
:-(
?LOG_ERROR(#{what => metrics_subscribe_failed,
503 reporter => Reporter, metric_name => Name,
504
:-(
reason => Other}),
505
:-(
Other
506 end.
507
508 subscribe_to_all(Reporter, Interval) ->
509
:-(
HostTypePrefixes = pick_by_all_metrics_are_global([], ?ALL_HOST_TYPES),
510
:-(
lists:foreach(
511 fun(Prefix) ->
512
:-(
UnspacedPrefix = get_host_type_prefix(Prefix),
513
:-(
start_metrics_subscriptions(Reporter, [UnspacedPrefix], Interval)
514 end, [global | HostTypePrefixes]).
Line Hits Source