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