./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 104 mongoose_instrument_registry:start(), % TODO move it out of this module
70 104 prepare_prefixes(),
71 104 create_vm_metrics(),
72 104 create_global_metrics(?GLOBAL_COUNTERS),
73 104 create_data_metrics(),
74 104 create_host_type_metrics().
75
76 -spec init_mongooseim_metrics() -> ok.
77 init_mongooseim_metrics() ->
78 104 create_host_type_hook_metrics(),
79 104 create_global_metrics(?MNESIA_COUNTERS),
80 104 init_subscriptions().
81
82 init_subscriptions() ->
83 104 Reporters = exometer_report:list_reporters(),
84 104 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 32376 UseOrSkip = filter_hook(Hook),
94 32376 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 312 {Metric, Spec} = ?PROBE(Name, Module),
100 312 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 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 1288560 exometer:update(name_by_all_metrics_are_global(HostType, Name), Change);
113 update(HostType, Name, Change) ->
114 464232 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 1247 ensure_metric(HostType, Metric, Type, element(1, Type));
120 ensure_metric(HostType, Metric, Type) ->
121 73128 ensure_metric(HostType, Metric, Type, Type).
122
123 get_metric_value(HostType, Name) when is_list(Name) ->
124 260 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 360 exometer:get_value(Metric).
130
131 get_metric_values(Metric) when is_list(Metric) ->
132 232 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 10528 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 415994 UseOrSkip = filter_hook(Hook),
153 415994 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 130 RDBMSWorkers =
161 lists:flatmap(
162 fun({Type, Host, Tag}) ->
163 130 PoolName = mongoose_wpool:make_pool_name(Type, Host, Tag),
164 130 Wpool = wpool_pool:find_wpool(PoolName),
165 130 PoolSize = wpool_pool:wpool_get(size, Wpool),
166 130 [whereis(wpool_pool:worker_name(PoolName, I)) || I <- lists:seq(1, PoolSize)]
167 end,
168 Pools),
169
170 130 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 104 persistent_term:erase(?PREFIXES),
190 104 lists:foreach(fun remove_metric/1, exometer:find_entries([])).
191
192 %% ---------------------------------------------------------------------
193 %% Internal functions
194 %% ---------------------------------------------------------------------
195
196 prepare_prefixes() ->
197 104 Prebuilt = maps:from_list([begin
198 558 Prefix = make_host_type_prefix(HT),
199 558 {Prefix, Prefix}
200 104 end || HT <- ?ALL_HOST_TYPES ]),
201 104 Prefixes = maps:from_list([ {HT, make_host_type_prefix(HT)}
202 104 || HT <- ?ALL_HOST_TYPES ]),
203 104 persistent_term:put(?PREFIXES, maps:merge(Prebuilt, Prefixes)).
204
205 -spec all_metrics_are_global() -> boolean().
206 all_metrics_are_global() ->
207 1363243 mongoose_config:get_opt(all_metrics_are_global).
208
209 get_host_type_prefix(global) ->
210 760086 global;
211 get_host_type_prefix(HostType) when is_binary(HostType) ->
212 569648 case persistent_term:get(?PREFIXES, #{}) of
213 569639 #{HostType := HostTypePrefix} -> HostTypePrefix;
214 9 #{} -> make_host_type_prefix(HostType)
215 end.
216
217 make_host_type_prefix(HT) when is_binary(HT) ->
218 1125 binary:replace(HT, <<" ">>, <<"_">>, [global]).
219
220 pick_prefix_by_all_metrics_are_global(HostType) ->
221 1363243 case all_metrics_are_global() of
222 33600 true -> global;
223 1329643 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 1363243 [pick_prefix_by_all_metrics_are_global(HostType) | Name].
236
237 get_report_interval() ->
238 612 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 11432 ok;
247 do_create_generic_hook_metric(HostType, Hook, use) ->
248 20944 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 224318 ok;
256 do_increment_generic_hook_metric(HostType, Hook, use) ->
257 191676 update(HostType, Hook, 1).
258
259 get_rdbms_stats(RDBMSWorkers) ->
260 130 RDBMSConnections = [{catch mongoose_rdbms:get_db_info(Pid), Pid} || Pid <- RDBMSWorkers],
261 130 Ports = [get_port_from_rdbms_connection(Conn) || Conn <- RDBMSConnections],
262 130 PortStats = [inet_stats(Port) || Port <- lists:flatten(Ports)],
263 130 [{workers, length(RDBMSConnections)} | merge_stats(PortStats)].
264
265 get_port_from_rdbms_connection({{ok, DB, Pid}, _WorkerPid}) when DB =:= mysql;
266 DB =:= pgsql ->
267 650 ProcState = sys:get_state(Pid),
268 650 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 650 SockInfo = element(3, State),
312 650 get_port_from_sock(SockInfo).
313
314 get_port_from_sock({sslsocket, {_, Port, _, _}, _}) ->
315 650 Port;
316 get_port_from_sock(Port) ->
317
:-(
Port.
318
319 merge_stats(Stats) ->
320 262 OrdDict = lists:foldl(fun(Stat, Acc) ->
321 925 StatDict = orddict:from_list(Stat),
322 925 orddict:merge(fun merge_stats_fun/3, Acc, StatDict)
323 end, orddict:from_list(?EMPTY_INET_STATS), Stats),
324
325 262 orddict:to_list(OrdDict).
326
327 merge_stats_fun(recv_max, V1, V2) ->
328 925 erlang:max(V1, V2);
329 merge_stats_fun(send_max, V1, V2) ->
330 925 erlang:max(V1, V2);
331 merge_stats_fun(_, V1, V2) ->
332 4625 V1 + V2.
333
334 dist_inet_stats(Pid) when is_pid(Pid) ->
335
:-(
try
336
:-(
{ok, {sslsocket, FD, _Pids}} = tls_sender:dist_tls_socket(Pid),
337
:-(
gen_tcp = element(1, FD),
338
:-(
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 275 inet_stats(Port).
345
346 inet_stats(Port) ->
347 925 try
348 925 {ok, Stats} = inet:getstat(Port, ?INET_STATS),
349 925 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 30888 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 7116 filter_hook(sm_register_connection_hook) -> skip;
361 7114 filter_hook(sm_remove_connection_hook) -> skip;
362 627 filter_hook(auth_failed) -> skip;
363 24132 filter_hook(user_send_packet) -> skip;
364 4608 filter_hook(user_send_message) -> skip;
365 7507 filter_hook(user_send_presence) -> skip;
366 8200 filter_hook(user_send_iq) -> skip;
367 41539 filter_hook(user_receive_packet) -> skip;
368 12709 filter_hook(user_receive_message) -> skip;
369 15743 filter_hook(user_receive_presence) -> skip;
370 10052 filter_hook(user_receive_iq) -> skip;
371 606 filter_hook(xmpp_bounce_message) -> skip;
372 632 filter_hook(xmpp_stanza_dropped) -> skip;
373 63026 filter_hook(xmpp_send_element) -> skip;
374 749 filter_hook(roster_get) -> skip;
375 589 filter_hook(roster_set) -> skip;
376 1395 filter_hook(roster_push) -> skip;
377 6550 filter_hook(register_user) -> skip;
378 6519 filter_hook(remove_user) -> skip;
379 620 filter_hook(privacy_iq_get) -> skip;
380 697 filter_hook(privacy_iq_set) -> skip;
381 4126 filter_hook(privacy_check_packet) -> skip;
382 243 filter_hook(mam_get_prefs) -> skip;
383 390 filter_hook(mam_set_prefs) -> skip;
384 581 filter_hook(mam_remove_archive) -> skip;
385 6233 filter_hook(mam_archive_message) -> skip;
386 42 filter_hook(mam_muc_get_prefs) -> skip;
387 42 filter_hook(mam_muc_set_prefs) -> skip;
388 701 filter_hook(mam_muc_remove_archive) -> skip;
389 491 filter_hook(mam_muc_lookup_messages) -> skip;
390 1853 filter_hook(mam_muc_archive_message) -> skip;
391 318 filter_hook(mam_muc_flush_messages) -> skip;
392
393 212620 filter_hook(_) -> use.
394
395 create_global_metrics(Metrics) ->
396 208 lists:foreach(fun({Metric, Spec}) -> ensure_metric(global, Metric, Spec) end, Metrics).
397
398 create_vm_metrics() ->
399 104 lists:foreach(fun({Metric, FunSpec, DataPoints}) ->
400 208 FunSpecTuple = list_to_tuple(FunSpec ++ [DataPoints]),
401 208 catch ensure_metric(global, Metric, FunSpecTuple)
402 end, ?VM_STATS).
403
404 -spec create_host_type_metrics() -> ok.
405 create_host_type_metrics() ->
406 104 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 558 lists:foreach(fun(Name) -> ensure_metric(HostType, Name, spiral) end, ?GENERAL_SPIRALS),
411 558 lists:foreach(fun(Name) -> ensure_metric(HostType, Name, histogram) end, ?GENERAL_HISTOGRAMS),
412 558 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 104 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 558 Hooks = mongoose_metrics_hooks:get_hooks(HostType),
421 558 gen_hook:add_handlers(Hooks).
422
423 ensure_metric(HostType, Metric, Type, ShortType) when is_atom(Metric) ->
424 42724 ensure_metric(HostType, [Metric], Type, ShortType);
425
426 ensure_metric(HostType, Metric, Type, probe = ShortType) ->
427 602 PrefixedMetric = name_by_all_metrics_are_global(HostType, Metric),
428 602 {ShortType, Opts} = Type,
429 602 case exometer:info(PrefixedMetric, type) of
430 undefined ->
431 532 ExometerOpts = [{module, mongoose_metrics_probe}, {type, ShortType}] ++ Opts,
432 532 do_create_metric(PrefixedMetric, ad_hoc, ExometerOpts);
433 _ ->
434 70 {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 73773 PrefixedMetric = name_by_all_metrics_are_global(HostType, Metric),
440 73773 case exometer:info(PrefixedMetric, type) of
441 undefined ->
442 30732 do_create_metric(PrefixedMetric, Type, []);
443 43041 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 6231 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 6183 ?LOG_DEBUG(#{what => metric_already_present,
464 6183 host_type => HostType, metric => Metric, type => Type}),
465 6183 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 31264 case catch exometer:new(PrefixedMetric, ExometerType, ExometerOpts) of
474
:-(
{'EXIT', {exists, _}} -> {ok, already_present};
475 31264 ok -> ok;
476
:-(
{'EXIT', Error} -> {error, Error}
477 end.
478
479 create_data_metrics() ->
480 104 lists:foreach(fun(Metric) -> ensure_metric(global, Metric, histogram) end,
481 ?GLOBAL_HISTOGRAMS),
482 104 lists:foreach(fun(Metric) -> ensure_metric(global, Metric, spiral) end,
483 ?GLOBAL_SPIRALS),
484 104 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