|
|
@@ -14,29 +14,31 @@
|
|
|
%% limitations under the License.
|
|
|
%%--------------------------------------------------------------------
|
|
|
|
|
|
--module(emqx_rule_metrics).
|
|
|
+-module(emqx_plugin_libs_metrics).
|
|
|
|
|
|
-behaviour(gen_server).
|
|
|
|
|
|
--include("rule_engine.hrl").
|
|
|
-
|
|
|
%% API functions
|
|
|
--export([ start_link/0
|
|
|
- , stop/0
|
|
|
- ]).
|
|
|
-
|
|
|
--export([ get_rules_matched/1
|
|
|
+-export([ start_link/1
|
|
|
+ , stop/1
|
|
|
+ , child_spec/1
|
|
|
]).
|
|
|
|
|
|
--export([ inc/2
|
|
|
- , inc/3
|
|
|
- , get/2
|
|
|
- , get_rule_speed/1
|
|
|
- , create_rule_metrics/1
|
|
|
- , clear_rule_metrics/1
|
|
|
+-export([ inc/3
|
|
|
+ , inc/4
|
|
|
+ , get/3
|
|
|
+ , get_speed/2
|
|
|
+ , create_metrics/2
|
|
|
+ , clear_metrics/2
|
|
|
]).
|
|
|
|
|
|
--export([ get_rule_metrics/1
|
|
|
+-export([ get_metrics/2
|
|
|
+ , get_matched/2
|
|
|
+ , get_success/2
|
|
|
+ , get_failed/2
|
|
|
+ , inc_matched/2
|
|
|
+ , inc_success/2
|
|
|
+ , inc_failed/2
|
|
|
]).
|
|
|
|
|
|
%% gen_server callbacks
|
|
|
@@ -57,10 +59,14 @@
|
|
|
-define(SAMPLING, 1).
|
|
|
-endif.
|
|
|
|
|
|
--define(CntrRef, ?MODULE).
|
|
|
+-type handler_name() :: atom().
|
|
|
+-type metric_id() :: binary().
|
|
|
+
|
|
|
+-define(CntrRef(Name), {?MODULE, Name}).
|
|
|
-define(SAMPCOUNT_5M, (?SECS_5M div ?SAMPLING)).
|
|
|
|
|
|
--record(rule_speed, {
|
|
|
+%% the speed of 'matched'
|
|
|
+-record(speed, {
|
|
|
max = 0 :: number(),
|
|
|
current = 0 :: number(),
|
|
|
last5m = 0 :: number(),
|
|
|
@@ -74,94 +80,121 @@
|
|
|
|
|
|
-record(state, {
|
|
|
metric_ids = sets:new(),
|
|
|
- rule_speeds :: undefined | #{rule_id() => #rule_speed{}}
|
|
|
+ speeds :: undefined | #{metric_id() => #speed{}}
|
|
|
}).
|
|
|
|
|
|
%%------------------------------------------------------------------------------
|
|
|
%% APIs
|
|
|
%%------------------------------------------------------------------------------
|
|
|
|
|
|
--spec(create_rule_metrics(rule_id()) -> ok).
|
|
|
-create_rule_metrics(Id) ->
|
|
|
- gen_server:call(?MODULE, {create_rule_metrics, Id}).
|
|
|
-
|
|
|
--spec(clear_rule_metrics(rule_id()) -> ok).
|
|
|
-clear_rule_metrics(Id) ->
|
|
|
- gen_server:call(?MODULE, {delete_rule_metrics, Id}).
|
|
|
-
|
|
|
--spec(get(rule_id(), atom()) -> number()).
|
|
|
-get(Id, Metric) ->
|
|
|
- case get_couters_ref(Id) of
|
|
|
+-spec(child_spec(handler_name()) -> supervisor:child_spec()).
|
|
|
+child_spec(Name) ->
|
|
|
+ #{ id => emqx_plugin_libs_metrics
|
|
|
+ , start => {emqx_plugin_libs_metrics, start_link, [Name]}
|
|
|
+ , restart => permanent
|
|
|
+ , shutdown => 5000
|
|
|
+ , type => worker
|
|
|
+ , modules => [emqx_plugin_libs_metrics]
|
|
|
+ }.
|
|
|
+
|
|
|
+-spec(create_metrics(handler_name(), metric_id()) -> ok).
|
|
|
+create_metrics(Name, Id) ->
|
|
|
+ gen_server:call(Name, {create_metrics, Id}).
|
|
|
+
|
|
|
+-spec(clear_metrics(handler_name(), metric_id()) -> ok).
|
|
|
+clear_metrics(Name, Id) ->
|
|
|
+ gen_server:call(Name, {delete_metrics, Id}).
|
|
|
+
|
|
|
+-spec(get(handler_name(), metric_id(), atom()) -> number()).
|
|
|
+get(Name, Id, Metric) ->
|
|
|
+ case get_couters_ref(Name, Id) of
|
|
|
not_found -> 0;
|
|
|
Ref -> counters:get(Ref, metrics_idx(Metric))
|
|
|
end.
|
|
|
|
|
|
--spec(get_rule_speed(rule_id()) -> map()).
|
|
|
-get_rule_speed(Id) ->
|
|
|
- gen_server:call(?MODULE, {get_rule_speed, Id}).
|
|
|
+-spec(get_speed(handler_name(), metric_id()) -> map()).
|
|
|
+get_speed(Name, Id) ->
|
|
|
+ gen_server:call(Name, {get_speed, Id}).
|
|
|
|
|
|
--spec(get_rule_metrics(rule_id()) -> map()).
|
|
|
-get_rule_metrics(Id) ->
|
|
|
- #{max := Max, current := Current, last5m := Last5M} = get_rule_speed(Id),
|
|
|
- #{matched => get_rules_matched(Id),
|
|
|
+-spec(get_metrics(handler_name(), metric_id()) -> map()).
|
|
|
+get_metrics(Name, Id) ->
|
|
|
+ #{max := Max, current := Current, last5m := Last5M} = get_speed(Name, Id),
|
|
|
+ #{matched => get_matched(Name, Id),
|
|
|
+ success => get_success(Name, Id),
|
|
|
+ failed => get_failed(Name, Id),
|
|
|
speed => Current,
|
|
|
speed_max => Max,
|
|
|
speed_last5m => Last5M
|
|
|
}.
|
|
|
|
|
|
--spec inc(rule_id(), atom()) -> ok.
|
|
|
-inc(Id, Metric) ->
|
|
|
- inc(Id, Metric, 1).
|
|
|
+-spec inc(handler_name(), metric_id(), atom()) -> ok.
|
|
|
+inc(Name, Id, Metric) ->
|
|
|
+ inc(Name, Id, Metric, 1).
|
|
|
|
|
|
--spec inc(rule_id(), atom(), pos_integer()) -> ok.
|
|
|
-inc(Id, Metric, Val) ->
|
|
|
- case get_couters_ref(Id) of
|
|
|
+-spec inc(handler_name(), metric_id(), atom(), pos_integer()) -> ok.
|
|
|
+inc(Name, Id, Metric, Val) ->
|
|
|
+ case get_couters_ref(Name, Id) of
|
|
|
not_found ->
|
|
|
%% this may occur when increasing a counter for
|
|
|
%% a rule that was created from a remove node.
|
|
|
- create_rule_metrics(Id),
|
|
|
- counters:add(get_couters_ref(Id), metrics_idx(Metric), Val);
|
|
|
+ create_metrics(Name, Id),
|
|
|
+ counters:add(get_couters_ref(Name, Id), metrics_idx(Metric), Val);
|
|
|
Ref ->
|
|
|
counters:add(Ref, metrics_idx(Metric), Val)
|
|
|
end.
|
|
|
|
|
|
-get_rules_matched(Id) ->
|
|
|
- get(Id, 'rules.matched').
|
|
|
+inc_matched(Name, Id) ->
|
|
|
+ inc(Name, Id, 'matched', 1).
|
|
|
|
|
|
-start_link() ->
|
|
|
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
|
|
+inc_success(Name, Id) ->
|
|
|
+ inc(Name, Id, 'success', 1).
|
|
|
|
|
|
-init([]) ->
|
|
|
+inc_failed(Name, Id) ->
|
|
|
+ inc(Name, Id, 'failed', 1).
|
|
|
+
|
|
|
+get_matched(Name, Id) ->
|
|
|
+ get(Name, Id, 'matched').
|
|
|
+
|
|
|
+get_success(Name, Id) ->
|
|
|
+ get(Name, Id, 'success').
|
|
|
+
|
|
|
+get_failed(Name, Id) ->
|
|
|
+ get(Name, Id, 'failed').
|
|
|
+
|
|
|
+start_link(Name) ->
|
|
|
+ gen_server:start_link({local, Name}, ?MODULE, Name, []).
|
|
|
+
|
|
|
+init(Name) ->
|
|
|
erlang:process_flag(trap_exit, true),
|
|
|
%% the speed metrics
|
|
|
erlang:send_after(timer:seconds(?SAMPLING), self(), ticking),
|
|
|
- persistent_term:put(?CntrRef, #{}),
|
|
|
+ persistent_term:put(?CntrRef(Name), #{}),
|
|
|
{ok, #state{}}.
|
|
|
|
|
|
-handle_call({get_rule_speed, _Id}, _From, State = #state{rule_speeds = undefined}) ->
|
|
|
- {reply, format_rule_speed(#rule_speed{}), State};
|
|
|
-handle_call({get_rule_speed, Id}, _From, State = #state{rule_speeds = RuleSpeeds}) ->
|
|
|
- {reply, case maps:get(Id, RuleSpeeds, undefined) of
|
|
|
- undefined -> format_rule_speed(#rule_speed{});
|
|
|
- Speed -> format_rule_speed(Speed)
|
|
|
+handle_call({get_speed, _Id}, _From, State = #state{speeds = undefined}) ->
|
|
|
+ {reply, format_speed(#speed{}), State};
|
|
|
+handle_call({get_speed, Id}, _From, State = #state{speeds = Speeds}) ->
|
|
|
+ {reply, case maps:get(Id, Speeds, undefined) of
|
|
|
+ undefined -> format_speed(#speed{});
|
|
|
+ Speed -> format_speed(Speed)
|
|
|
end, State};
|
|
|
|
|
|
-handle_call({create_rule_metrics, Id}, _From,
|
|
|
- State = #state{metric_ids = MIDs, rule_speeds = RuleSpeeds}) ->
|
|
|
- {reply, create_counters(Id),
|
|
|
+handle_call({create_metrics, Id}, _From,
|
|
|
+ State = #state{metric_ids = MIDs, speeds = Speeds}) ->
|
|
|
+ {reply, create_counters(get_self_name(), Id),
|
|
|
State#state{metric_ids = sets:add_element(Id, MIDs),
|
|
|
- rule_speeds = case RuleSpeeds of
|
|
|
- undefined -> #{Id => #rule_speed{}};
|
|
|
- _ -> RuleSpeeds#{Id => #rule_speed{}}
|
|
|
+ speeds = case Speeds of
|
|
|
+ undefined -> #{Id => #speed{}};
|
|
|
+ _ -> Speeds#{Id => #speed{}}
|
|
|
end}};
|
|
|
|
|
|
-handle_call({delete_rule_metrics, Id}, _From,
|
|
|
- State = #state{metric_ids = MIDs, rule_speeds = RuleSpeeds}) ->
|
|
|
- {reply, delete_counters(Id),
|
|
|
+handle_call({delete_metrics, Id}, _From,
|
|
|
+ State = #state{metric_ids = MIDs, speeds = Speeds}) ->
|
|
|
+ {reply, delete_counters(get_self_name(), Id),
|
|
|
State#state{metric_ids = sets:del_element(Id, MIDs),
|
|
|
- rule_speeds = case RuleSpeeds of
|
|
|
+ speeds = case Speeds of
|
|
|
undefined -> undefined;
|
|
|
- _ -> maps:remove(Id, RuleSpeeds)
|
|
|
+ _ -> maps:remove(Id, Speeds)
|
|
|
end}};
|
|
|
|
|
|
handle_call(_Request, _From, State) ->
|
|
|
@@ -170,17 +203,17 @@ handle_call(_Request, _From, State) ->
|
|
|
handle_cast(_Msg, State) ->
|
|
|
{noreply, State}.
|
|
|
|
|
|
-handle_info(ticking, State = #state{rule_speeds = undefined}) ->
|
|
|
+handle_info(ticking, State = #state{speeds = undefined}) ->
|
|
|
erlang:send_after(timer:seconds(?SAMPLING), self(), ticking),
|
|
|
{noreply, State};
|
|
|
|
|
|
-handle_info(ticking, State = #state{rule_speeds = RuleSpeeds0}) ->
|
|
|
- RuleSpeeds = maps:map(
|
|
|
- fun(Id, RuleSpeed) ->
|
|
|
- calculate_speed(get_rules_matched(Id), RuleSpeed)
|
|
|
- end, RuleSpeeds0),
|
|
|
+handle_info(ticking, State = #state{speeds = Speeds0}) ->
|
|
|
+ Speeds = maps:map(
|
|
|
+ fun(Id, Speed) ->
|
|
|
+ calculate_speed(get_matched(get_self_name(), Id), Speed)
|
|
|
+ end, Speeds0),
|
|
|
erlang:send_after(timer:seconds(?SAMPLING), self(), ticking),
|
|
|
- {noreply, State#state{rule_speeds = RuleSpeeds}};
|
|
|
+ {noreply, State#state{speeds = Speeds}};
|
|
|
|
|
|
handle_info(_Info, State) ->
|
|
|
{noreply, State}.
|
|
|
@@ -189,37 +222,38 @@ code_change(_OldVsn, State, _Extra) ->
|
|
|
{ok, State}.
|
|
|
|
|
|
terminate(_Reason, #state{metric_ids = MIDs}) ->
|
|
|
- [delete_counters(Id) || Id <- sets:to_list(MIDs)],
|
|
|
- persistent_term:erase(?CntrRef).
|
|
|
+ Name = get_self_name(),
|
|
|
+ [delete_counters(Name, Id) || Id <- sets:to_list(MIDs)],
|
|
|
+ persistent_term:erase(?CntrRef(Name)).
|
|
|
|
|
|
-stop() ->
|
|
|
- gen_server:stop(?MODULE).
|
|
|
+stop(Name) ->
|
|
|
+ gen_server:stop(Name).
|
|
|
|
|
|
%%------------------------------------------------------------------------------
|
|
|
%% Internal Functions
|
|
|
%%------------------------------------------------------------------------------
|
|
|
|
|
|
-create_counters(Id) ->
|
|
|
- case get_couters_ref(Id) of
|
|
|
+create_counters(Name, Id) ->
|
|
|
+ case get_couters_ref(Name, Id) of
|
|
|
not_found ->
|
|
|
- Counters = get_all_counters(),
|
|
|
+ Counters = get_all_counters(Name),
|
|
|
CntrRef = counters:new(max_counters_size(), [write_concurrency]),
|
|
|
- persistent_term:put(?CntrRef, Counters#{Id => CntrRef});
|
|
|
+ persistent_term:put(?CntrRef(Name), Counters#{Id => CntrRef});
|
|
|
_Ref -> ok
|
|
|
end.
|
|
|
|
|
|
-delete_counters(Id) ->
|
|
|
- persistent_term:put(?CntrRef, maps:remove(Id, get_all_counters())).
|
|
|
+delete_counters(Name, Id) ->
|
|
|
+ persistent_term:put(?CntrRef(Name), maps:remove(Id, get_all_counters(Name))).
|
|
|
|
|
|
-get_couters_ref(Id) ->
|
|
|
- maps:get(Id, get_all_counters(), not_found).
|
|
|
+get_couters_ref(Name, Id) ->
|
|
|
+ maps:get(Id, get_all_counters(Name), not_found).
|
|
|
|
|
|
-get_all_counters() ->
|
|
|
- persistent_term:get(?CntrRef, #{}).
|
|
|
+get_all_counters(Name) ->
|
|
|
+ persistent_term:get(?CntrRef(Name), #{}).
|
|
|
|
|
|
calculate_speed(_CurrVal, undefined) ->
|
|
|
undefined;
|
|
|
-calculate_speed(CurrVal, #rule_speed{max = MaxSpeed0, last_v = LastVal,
|
|
|
+calculate_speed(CurrVal, #speed{max = MaxSpeed0, last_v = LastVal,
|
|
|
tick = Tick, last5m_acc = AccSpeed5Min0,
|
|
|
last5m_smpl = Last5MinSamples0}) ->
|
|
|
%% calculate the current speed based on the last value of the counter
|
|
|
@@ -244,22 +278,28 @@ calculate_speed(CurrVal, #rule_speed{max = MaxSpeed0, last_v = LastVal,
|
|
|
Acc, Acc / ?SAMPCOUNT_5M}
|
|
|
end,
|
|
|
|
|
|
- #rule_speed{max = MaxSpeed, current = CurrSpeed, last5m = Last5Min,
|
|
|
+ #speed{max = MaxSpeed, current = CurrSpeed, last5m = Last5Min,
|
|
|
last_v = CurrVal, last5m_acc = Acc5Min,
|
|
|
last5m_smpl = Last5MinSamples, tick = Tick + 1}.
|
|
|
|
|
|
-format_rule_speed(#rule_speed{max = Max, current = Current, last5m = Last5Min}) ->
|
|
|
+format_speed(#speed{max = Max, current = Current, last5m = Last5Min}) ->
|
|
|
#{max => Max, current => precision(Current, 2), last5m => precision(Last5Min, 2)}.
|
|
|
|
|
|
precision(Float, N) ->
|
|
|
Base = math:pow(10, N),
|
|
|
round(Float * Base) / Base.
|
|
|
|
|
|
+get_self_name() ->
|
|
|
+ {registered_name, Name} = process_info(self(), registered_name),
|
|
|
+ Name.
|
|
|
+
|
|
|
%%------------------------------------------------------------------------------
|
|
|
%% Metrics Definitions
|
|
|
%%------------------------------------------------------------------------------
|
|
|
|
|
|
-max_counters_size() -> 2.
|
|
|
-metrics_idx('rules.matched') -> 1;
|
|
|
-metrics_idx(_) -> 2.
|
|
|
+max_counters_size() -> 32.
|
|
|
+metrics_idx('matched') -> 1;
|
|
|
+metrics_idx('success') -> 2;
|
|
|
+metrics_idx('failed') -> 3;
|
|
|
+metrics_idx(_) -> 32.
|
|
|
|