style: reformat emqx_machine, emqx_plugin_libs, and emqx_statsd
This commit is contained in:
parent
c32fc33c1a
commit
b445182335
|
@ -1,4 +1,5 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
|
|
||||||
{deps, [ {emqx, {path, "../emqx"}}
|
{deps, [{emqx, {path, "../emqx"}}]}.
|
||||||
]}.
|
|
||||||
|
{project_plugins, [erlfmt]}.
|
||||||
|
|
|
@ -23,12 +23,13 @@
|
||||||
-export([run/0]).
|
-export([run/0]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([ init/1
|
-export([
|
||||||
, handle_call/3
|
init/1,
|
||||||
, handle_cast/2
|
handle_call/3,
|
||||||
, handle_info/2
|
handle_cast/2,
|
||||||
, terminate/2
|
handle_info/2,
|
||||||
, code_change/3
|
terminate/2,
|
||||||
|
code_change/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% 5 minutes
|
%% 5 minutes
|
||||||
|
@ -38,14 +39,14 @@
|
||||||
%% APIs
|
%% APIs
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
|
-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
|
||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||||
|
|
||||||
-spec(run() -> {ok, timer:time()}).
|
-spec run() -> {ok, timer:time()}.
|
||||||
run() -> gen_server:call(?MODULE, run, infinity).
|
run() -> gen_server:call(?MODULE, run, infinity).
|
||||||
|
|
||||||
-spec(stop() -> ok).
|
-spec stop() -> ok.
|
||||||
stop() -> gen_server:stop(?MODULE).
|
stop() -> gen_server:stop(?MODULE).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -58,7 +59,6 @@ init([]) ->
|
||||||
handle_call(run, _From, State) ->
|
handle_call(run, _From, State) ->
|
||||||
{Time, ok} = timer:tc(fun run_gc/0),
|
{Time, ok} = timer:tc(fun run_gc/0),
|
||||||
{reply, {ok, Time div 1000}, State, hibernate};
|
{reply, {ok, Time div 1000}, State, hibernate};
|
||||||
|
|
||||||
handle_call(_Req, _From, State) ->
|
handle_call(_Req, _From, State) ->
|
||||||
{reply, ignored, State}.
|
{reply, ignored, State}.
|
||||||
|
|
||||||
|
@ -68,7 +68,6 @@ handle_cast(_Msg, State) ->
|
||||||
handle_info({timeout, TRef, run}, State = #{timer := TRef}) ->
|
handle_info({timeout, TRef, run}, State = #{timer := TRef}) ->
|
||||||
ok = run_gc(),
|
ok = run_gc(),
|
||||||
{noreply, ensure_timer(State), hibernate};
|
{noreply, ensure_timer(State), hibernate};
|
||||||
|
|
||||||
handle_info(_Info, State) ->
|
handle_info(_Info, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
|
@ -84,7 +83,8 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
|
|
||||||
ensure_timer(State) ->
|
ensure_timer(State) ->
|
||||||
case application:get_env(emqx_machine, global_gc_interval) of
|
case application:get_env(emqx_machine, global_gc_interval) of
|
||||||
undefined -> State;
|
undefined ->
|
||||||
|
State;
|
||||||
{ok, Interval} ->
|
{ok, Interval} ->
|
||||||
TRef = emqx_misc:start_timer(Interval, run),
|
TRef = emqx_misc:start_timer(Interval, run),
|
||||||
State#{timer := TRef}
|
State#{timer := TRef}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_machine,
|
{application, emqx_machine, [
|
||||||
[{id, "emqx_machine"},
|
{id, "emqx_machine"},
|
||||||
{description, "The EMQX Machine"},
|
{description, "The EMQX Machine"},
|
||||||
{vsn, "0.1.0"}, % strict semver, bump manually!
|
% strict semver, bump manually!
|
||||||
|
{vsn, "0.1.0"},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications, [kernel, stdlib]},
|
{applications, [kernel, stdlib]},
|
||||||
|
@ -10,7 +11,8 @@
|
||||||
{env, []},
|
{env, []},
|
||||||
{licenses, ["Apache-2.0"]},
|
{licenses, ["Apache-2.0"]},
|
||||||
{maintainers, ["EMQX Team <contact@emqx.io>"]},
|
{maintainers, ["EMQX Team <contact@emqx.io>"]},
|
||||||
{links, [{"Homepage", "https://emqx.io/"},
|
{links, [
|
||||||
|
{"Homepage", "https://emqx.io/"},
|
||||||
{"Github", "https://github.com/emqx/emqx"}
|
{"Github", "https://github.com/emqx/emqx"}
|
||||||
]}
|
]}
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -16,12 +16,13 @@
|
||||||
|
|
||||||
-module(emqx_machine).
|
-module(emqx_machine).
|
||||||
|
|
||||||
-export([ start/0
|
-export([
|
||||||
, graceful_shutdown/0
|
start/0,
|
||||||
, is_ready/0
|
graceful_shutdown/0,
|
||||||
|
is_ready/0,
|
||||||
|
|
||||||
, node_status/0
|
node_status/0,
|
||||||
, update_vips/0
|
update_vips/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
@ -29,10 +30,12 @@
|
||||||
%% @doc EMQX boot entrypoint.
|
%% @doc EMQX boot entrypoint.
|
||||||
start() ->
|
start() ->
|
||||||
case os:type() of
|
case os:type() of
|
||||||
{win32, nt} -> ok;
|
{win32, nt} ->
|
||||||
|
ok;
|
||||||
_Nix ->
|
_Nix ->
|
||||||
os:set_signal(sighup, ignore),
|
os:set_signal(sighup, ignore),
|
||||||
os:set_signal(sigterm, handle) %% default is handle
|
%% default is handle
|
||||||
|
os:set_signal(sigterm, handle)
|
||||||
end,
|
end,
|
||||||
ok = set_backtrace_depth(),
|
ok = set_backtrace_depth(),
|
||||||
start_sysmon(),
|
start_sysmon(),
|
||||||
|
@ -56,9 +59,12 @@ is_ready() ->
|
||||||
print_otp_version_warning() -> ok.
|
print_otp_version_warning() -> ok.
|
||||||
-else.
|
-else.
|
||||||
print_otp_version_warning() ->
|
print_otp_version_warning() ->
|
||||||
?ULOG("WARNING: Running on Erlang/OTP version ~p. Recommended: 23~n",
|
?ULOG(
|
||||||
[?OTP_RELEASE]).
|
"WARNING: Running on Erlang/OTP version ~p. Recommended: 23~n",
|
||||||
-endif. % OTP_RELEASE > 22
|
[?OTP_RELEASE]
|
||||||
|
).
|
||||||
|
% OTP_RELEASE > 22
|
||||||
|
-endif.
|
||||||
|
|
||||||
start_sysmon() ->
|
start_sysmon() ->
|
||||||
_ = application:load(system_monitor),
|
_ = application:load(system_monitor),
|
||||||
|
@ -76,8 +82,9 @@ start_sysmon() ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
node_status() ->
|
node_status() ->
|
||||||
emqx_json:encode(#{ backend => mria_rlog:backend()
|
emqx_json:encode(#{
|
||||||
, role => mria_rlog:role()
|
backend => mria_rlog:backend(),
|
||||||
|
role => mria_rlog:role()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
update_vips() ->
|
update_vips() ->
|
||||||
|
@ -90,4 +97,5 @@ configure_shard_transports() ->
|
||||||
ShardName = binary_to_existing_atom(ShardBin),
|
ShardName = binary_to_existing_atom(ShardBin),
|
||||||
mria_config:set_shard_transport(ShardName, Transport)
|
mria_config:set_shard_transport(ShardName, Transport)
|
||||||
end,
|
end,
|
||||||
ShardTransports).
|
ShardTransports
|
||||||
|
).
|
||||||
|
|
|
@ -16,8 +16,9 @@
|
||||||
|
|
||||||
-module(emqx_machine_app).
|
-module(emqx_machine_app).
|
||||||
|
|
||||||
-export([ start/2
|
-export([
|
||||||
, stop/1
|
start/2,
|
||||||
|
stop/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-behaviour(application).
|
-behaviour(application).
|
||||||
|
|
|
@ -37,16 +37,18 @@ post_boot() ->
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
print_vsn() -> ok.
|
print_vsn() -> ok.
|
||||||
-else. % TEST
|
% TEST
|
||||||
|
-else.
|
||||||
print_vsn() ->
|
print_vsn() ->
|
||||||
?ULOG("~ts ~ts is running now!~n", [emqx_app:get_description(), emqx_app:get_release()]).
|
?ULOG("~ts ~ts is running now!~n", [emqx_app:get_description(), emqx_app:get_release()]).
|
||||||
-endif. % TEST
|
% TEST
|
||||||
|
-endif.
|
||||||
|
|
||||||
start_autocluster() ->
|
start_autocluster() ->
|
||||||
ekka:callback(stop, fun emqx_machine_boot:stop_apps/0),
|
ekka:callback(stop, fun emqx_machine_boot:stop_apps/0),
|
||||||
ekka:callback(start, fun emqx_machine_boot:ensure_apps_started/0),
|
ekka:callback(start, fun emqx_machine_boot:ensure_apps_started/0),
|
||||||
_ = ekka:autocluster(emqx), %% returns 'ok' or a pid or 'any()' as in spec
|
%% returns 'ok' or a pid or 'any()' as in spec
|
||||||
|
_ = ekka:autocluster(emqx),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
stop_apps() ->
|
stop_apps() ->
|
||||||
|
@ -60,10 +62,12 @@ stop_one_app(App) ->
|
||||||
_ = application:stop(App)
|
_ = application:stop(App)
|
||||||
catch
|
catch
|
||||||
C:E ->
|
C:E ->
|
||||||
?SLOG(error, #{msg => "failed_to_stop_app",
|
?SLOG(error, #{
|
||||||
|
msg => "failed_to_stop_app",
|
||||||
app => App,
|
app => App,
|
||||||
exception => C,
|
exception => C,
|
||||||
reason => E})
|
reason => E
|
||||||
|
})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
ensure_apps_started() ->
|
ensure_apps_started() ->
|
||||||
|
@ -121,16 +125,21 @@ sorted_reboot_apps(Apps) ->
|
||||||
%% Isolated apps without which are not dependency of any other apps are
|
%% Isolated apps without which are not dependency of any other apps are
|
||||||
%% put to the end of the list in the original order.
|
%% put to the end of the list in the original order.
|
||||||
add_apps_to_digraph(G, Apps) ->
|
add_apps_to_digraph(G, Apps) ->
|
||||||
lists:foldl(fun
|
lists:foldl(
|
||||||
|
fun
|
||||||
({App, undefined}, Acc) ->
|
({App, undefined}, Acc) ->
|
||||||
?SLOG(debug, #{msg => "app_is_not_loaded", app => App}),
|
?SLOG(debug, #{msg => "app_is_not_loaded", app => App}),
|
||||||
Acc;
|
Acc;
|
||||||
({App, []}, Acc) ->
|
({App, []}, Acc) ->
|
||||||
Acc ++ [App]; %% use '++' to keep the original order
|
%% use '++' to keep the original order
|
||||||
|
Acc ++ [App];
|
||||||
({App, Deps}, Acc) ->
|
({App, Deps}, Acc) ->
|
||||||
add_app_deps_to_digraph(G, App, Deps),
|
add_app_deps_to_digraph(G, App, Deps),
|
||||||
Acc
|
Acc
|
||||||
end, [], Apps).
|
end,
|
||||||
|
[],
|
||||||
|
Apps
|
||||||
|
).
|
||||||
|
|
||||||
add_app_deps_to_digraph(G, App, undefined) ->
|
add_app_deps_to_digraph(G, App, undefined) ->
|
||||||
?SLOG(debug, #{msg => "app_is_not_loaded", app => App}),
|
?SLOG(debug, #{msg => "app_is_not_loaded", app => App}),
|
||||||
|
@ -141,7 +150,8 @@ add_app_deps_to_digraph(_G, _App, []) ->
|
||||||
add_app_deps_to_digraph(G, App, [Dep | Deps]) ->
|
add_app_deps_to_digraph(G, App, [Dep | Deps]) ->
|
||||||
digraph:add_vertex(G, App),
|
digraph:add_vertex(G, App),
|
||||||
digraph:add_vertex(G, Dep),
|
digraph:add_vertex(G, Dep),
|
||||||
digraph:add_edge(G, Dep, App), %% dep -> app as dependency
|
%% dep -> app as dependency
|
||||||
|
digraph:add_edge(G, Dep, App),
|
||||||
add_app_deps_to_digraph(G, App, Deps).
|
add_app_deps_to_digraph(G, App, Deps).
|
||||||
|
|
||||||
find_loops(G) ->
|
find_loops(G) ->
|
||||||
|
@ -151,4 +161,6 @@ find_loops(G) ->
|
||||||
false -> false;
|
false -> false;
|
||||||
Apps -> {true, Apps}
|
Apps -> {true, Apps}
|
||||||
end
|
end
|
||||||
end, digraph:vertices(G)).
|
end,
|
||||||
|
digraph:vertices(G)
|
||||||
|
).
|
||||||
|
|
|
@ -20,9 +20,16 @@
|
||||||
%% perform graceful shutdown.
|
%% perform graceful shutdown.
|
||||||
-module(emqx_machine_signal_handler).
|
-module(emqx_machine_signal_handler).
|
||||||
|
|
||||||
-export([start/0, init/1, format_status/2,
|
-export([
|
||||||
handle_event/2, handle_call/2, handle_info/2,
|
start/0,
|
||||||
terminate/2, code_change/3]).
|
init/1,
|
||||||
|
format_status/2,
|
||||||
|
handle_event/2,
|
||||||
|
handle_call/2,
|
||||||
|
handle_info/2,
|
||||||
|
terminate/2,
|
||||||
|
code_change/3
|
||||||
|
]).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
|
@ -30,7 +37,8 @@ start() ->
|
||||||
ok = gen_event:swap_sup_handler(
|
ok = gen_event:swap_sup_handler(
|
||||||
erl_signal_server,
|
erl_signal_server,
|
||||||
{erl_signal_handler, []},
|
{erl_signal_handler, []},
|
||||||
{?MODULE, []}).
|
{?MODULE, []}
|
||||||
|
).
|
||||||
|
|
||||||
init({[], _}) -> {ok, #{}}.
|
init({[], _}) -> {ok, #{}}.
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,7 @@
|
||||||
|
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
|
|
||||||
-export([ start_link/0
|
-export([start_link/0]).
|
||||||
]).
|
|
||||||
|
|
||||||
-export([init/1]).
|
-export([init/1]).
|
||||||
|
|
||||||
|
@ -33,7 +32,8 @@ init([]) ->
|
||||||
BootApps = child_worker(emqx_machine_boot, post_boot, [], temporary),
|
BootApps = child_worker(emqx_machine_boot, post_boot, [], temporary),
|
||||||
GlobalGC = child_worker(emqx_global_gc, [], permanent),
|
GlobalGC = child_worker(emqx_global_gc, [], permanent),
|
||||||
Children = [Terminator, BootApps, GlobalGC],
|
Children = [Terminator, BootApps, GlobalGC],
|
||||||
SupFlags = #{strategy => one_for_one,
|
SupFlags = #{
|
||||||
|
strategy => one_for_one,
|
||||||
intensity => 100,
|
intensity => 100,
|
||||||
period => 10
|
period => 10
|
||||||
},
|
},
|
||||||
|
@ -43,7 +43,8 @@ child_worker(M, Args, Restart) ->
|
||||||
child_worker(M, start_link, Args, Restart).
|
child_worker(M, start_link, Args, Restart).
|
||||||
|
|
||||||
child_worker(M, Func, Args, Restart) ->
|
child_worker(M, Func, Args, Restart) ->
|
||||||
#{id => M,
|
#{
|
||||||
|
id => M,
|
||||||
start => {M, Func, Args},
|
start => {M, Func, Args},
|
||||||
restart => Restart,
|
restart => Restart,
|
||||||
shutdown => 5000,
|
shutdown => 5000,
|
||||||
|
|
|
@ -18,15 +18,22 @@
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-export([ start_link/0
|
-export([
|
||||||
, graceful/0
|
start_link/0,
|
||||||
, graceful_wait/0
|
graceful/0,
|
||||||
, is_running/0
|
graceful_wait/0,
|
||||||
|
is_running/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([init/1, format_status/2,
|
-export([
|
||||||
handle_cast/2, handle_call/3, handle_info/2,
|
init/1,
|
||||||
terminate/2, code_change/3]).
|
format_status/2,
|
||||||
|
handle_cast/2,
|
||||||
|
handle_call/3,
|
||||||
|
handle_info/2,
|
||||||
|
terminate/2,
|
||||||
|
code_change/3
|
||||||
|
]).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
|
@ -84,7 +91,8 @@ handle_call(?DO_IT, _From, State) ->
|
||||||
catch
|
catch
|
||||||
C:E:St ->
|
C:E:St ->
|
||||||
Apps = [element(1, A) || A <- application:which_applications()],
|
Apps = [element(1, A) || A <- application:which_applications()],
|
||||||
?SLOG(error, #{msg => "failed_to_stop_apps",
|
?SLOG(error, #{
|
||||||
|
msg => "failed_to_stop_apps",
|
||||||
exception => C,
|
exception => C,
|
||||||
reason => E,
|
reason => E,
|
||||||
stacktrace => St,
|
stacktrace => St,
|
||||||
|
|
|
@ -77,27 +77,35 @@ limit_warning(MF, Args) ->
|
||||||
max_args_warning(MF, Args) ->
|
max_args_warning(MF, Args) ->
|
||||||
ArgsSize = erts_debug:flat_size(Args),
|
ArgsSize = erts_debug:flat_size(Args),
|
||||||
case ArgsSize < ?MAX_ARGS_SIZE of
|
case ArgsSize < ?MAX_ARGS_SIZE of
|
||||||
true -> ok;
|
true ->
|
||||||
|
ok;
|
||||||
false ->
|
false ->
|
||||||
warning("[WARNING] current_args_size:~w, max_args_size:~w", [ArgsSize, ?MAX_ARGS_SIZE]),
|
warning("[WARNING] current_args_size:~w, max_args_size:~w", [ArgsSize, ?MAX_ARGS_SIZE]),
|
||||||
?SLOG(warning, #{msg => "execute_function_in_shell_max_args_size",
|
?SLOG(warning, #{
|
||||||
|
msg => "execute_function_in_shell_max_args_size",
|
||||||
function => MF,
|
function => MF,
|
||||||
args => Args,
|
args => Args,
|
||||||
args_size => ArgsSize,
|
args_size => ArgsSize,
|
||||||
max_heap_size => ?MAX_ARGS_SIZE})
|
max_heap_size => ?MAX_ARGS_SIZE
|
||||||
|
})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
max_heap_size_warning(MF, Args) ->
|
max_heap_size_warning(MF, Args) ->
|
||||||
{heap_size, HeapSize} = erlang:process_info(self(), heap_size),
|
{heap_size, HeapSize} = erlang:process_info(self(), heap_size),
|
||||||
case HeapSize < ?MAX_HEAP_SIZE of
|
case HeapSize < ?MAX_HEAP_SIZE of
|
||||||
true -> ok;
|
true ->
|
||||||
|
ok;
|
||||||
false ->
|
false ->
|
||||||
warning("[WARNING] current_heap_size:~w, max_heap_size_warning:~w", [HeapSize, ?MAX_HEAP_SIZE]),
|
warning("[WARNING] current_heap_size:~w, max_heap_size_warning:~w", [
|
||||||
?SLOG(warning, #{msg => "shell_process_exceed_max_heap_size",
|
HeapSize, ?MAX_HEAP_SIZE
|
||||||
|
]),
|
||||||
|
?SLOG(warning, #{
|
||||||
|
msg => "shell_process_exceed_max_heap_size",
|
||||||
current_heap_size => HeapSize,
|
current_heap_size => HeapSize,
|
||||||
function => MF,
|
function => MF,
|
||||||
args => Args,
|
args => Args,
|
||||||
max_heap_size => ?MAX_HEAP_SIZE})
|
max_heap_size => ?MAX_HEAP_SIZE
|
||||||
|
})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
log(prohibited, MF, Args) ->
|
log(prohibited, MF, Args) ->
|
||||||
|
@ -105,8 +113,11 @@ log(prohibited, MF, Args) ->
|
||||||
?SLOG(error, #{msg => "execute_function_in_shell_prohibited", function => MF, args => Args});
|
?SLOG(error, #{msg => "execute_function_in_shell_prohibited", function => MF, args => Args});
|
||||||
log(exempted, MF, Args) ->
|
log(exempted, MF, Args) ->
|
||||||
limit_warning(MF, Args),
|
limit_warning(MF, Args),
|
||||||
?SLOG(error, #{msg => "execute_dangerous_function_in_shell_exempted", function => MF, args => Args});
|
?SLOG(error, #{
|
||||||
log(ignore, MF, Args) -> limit_warning(MF, Args).
|
msg => "execute_dangerous_function_in_shell_exempted", function => MF, args => Args
|
||||||
|
});
|
||||||
|
log(ignore, MF, Args) ->
|
||||||
|
limit_warning(MF, Args).
|
||||||
|
|
||||||
warning(Format, Args) ->
|
warning(Format, Args) ->
|
||||||
io:format(?RED_BG ++ Format ++ ?RESET ++ "~n", Args).
|
io:format(?RED_BG ++ Format ++ ?RESET ++ "~n", Args).
|
||||||
|
|
|
@ -43,22 +43,23 @@ init_per_suite(Config) ->
|
||||||
%%
|
%%
|
||||||
application:unload(emqx_authz),
|
application:unload(emqx_authz),
|
||||||
emqx_common_test_helpers:start_apps([emqx_conf]),
|
emqx_common_test_helpers:start_apps([emqx_conf]),
|
||||||
application:set_env(emqx_machine, applications, [ emqx_prometheus
|
application:set_env(emqx_machine, applications, [
|
||||||
, emqx_modules
|
emqx_prometheus,
|
||||||
, emqx_dashboard
|
emqx_modules,
|
||||||
, emqx_connector
|
emqx_dashboard,
|
||||||
, emqx_gateway
|
emqx_connector,
|
||||||
, emqx_statsd
|
emqx_gateway,
|
||||||
, emqx_resource
|
emqx_statsd,
|
||||||
, emqx_rule_engine
|
emqx_resource,
|
||||||
, emqx_bridge
|
emqx_rule_engine,
|
||||||
, emqx_plugin_libs
|
emqx_bridge,
|
||||||
, emqx_management
|
emqx_plugin_libs,
|
||||||
, emqx_retainer
|
emqx_management,
|
||||||
, emqx_exhook
|
emqx_retainer,
|
||||||
, emqx_authn
|
emqx_exhook,
|
||||||
, emqx_authz
|
emqx_authn,
|
||||||
, emqx_plugin
|
emqx_authz,
|
||||||
|
emqx_plugin
|
||||||
]),
|
]),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -19,22 +19,26 @@
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
sorted_reboot_apps_test_() ->
|
sorted_reboot_apps_test_() ->
|
||||||
Apps1 = [{1, [2, 3, 4]},
|
Apps1 = [
|
||||||
|
{1, [2, 3, 4]},
|
||||||
{2, [3, 4]}
|
{2, [3, 4]}
|
||||||
],
|
],
|
||||||
Apps2 = [{1, [2, 3, 4]},
|
Apps2 = [
|
||||||
|
{1, [2, 3, 4]},
|
||||||
{2, [3, 4]},
|
{2, [3, 4]},
|
||||||
{5, [4, 3, 2, 1, 1]}
|
{5, [4, 3, 2, 1, 1]}
|
||||||
],
|
],
|
||||||
[fun() -> check_order(Apps1) end,
|
[
|
||||||
|
fun() -> check_order(Apps1) end,
|
||||||
fun() -> check_order(Apps2) end
|
fun() -> check_order(Apps2) end
|
||||||
].
|
].
|
||||||
|
|
||||||
sorted_reboot_apps_cycle_test() ->
|
sorted_reboot_apps_cycle_test() ->
|
||||||
Apps = [{1, [2]}, {2, [1, 3]}],
|
Apps = [{1, [2]}, {2, [1, 3]}],
|
||||||
?assertError({circular_application_dependency, [[1, 2, 1], [2, 1, 2]]},
|
?assertError(
|
||||||
check_order(Apps)).
|
{circular_application_dependency, [[1, 2, 1], [2, 1, 2]]},
|
||||||
|
check_order(Apps)
|
||||||
|
).
|
||||||
|
|
||||||
check_order(Apps) ->
|
check_order(Apps) ->
|
||||||
AllApps = lists:usort(lists:append([[A | Deps] || {A, Deps} <- Apps])),
|
AllApps = lists:usort(lists:append([[A | Deps] || {A, Deps} <- Apps])),
|
||||||
|
@ -47,7 +51,8 @@ check_order(Apps) ->
|
||||||
lists:foldr(fun(A, {I, Acc}) -> {I + 1, [{A, I} | Acc]} end, {1, []}, Sorted),
|
lists:foldr(fun(A, {I, Acc}) -> {I + 1, [{A, I} | Acc]} end, {1, []}, Sorted),
|
||||||
do_check_order(Apps, SortedWithIndex).
|
do_check_order(Apps, SortedWithIndex).
|
||||||
|
|
||||||
do_check_order([], _) -> ok;
|
do_check_order([], _) ->
|
||||||
|
ok;
|
||||||
do_check_order([{A, Deps} | Rest], Sorted) ->
|
do_check_order([{A, Deps} | Rest], Sorted) ->
|
||||||
case lists:filter(fun(Dep) -> is_sorted_before(Dep, A, Sorted) end, Deps) of
|
case lists:filter(fun(Dep) -> is_sorted_before(Dep, A, Sorted) end, Deps) of
|
||||||
[] -> do_check_order(Rest, Sorted);
|
[] -> do_check_order(Rest, Sorted);
|
||||||
|
|
|
@ -35,25 +35,39 @@ end_per_suite(_Config) ->
|
||||||
t_local_allowed(_Config) ->
|
t_local_allowed(_Config) ->
|
||||||
LocalProhibited = [halt, q],
|
LocalProhibited = [halt, q],
|
||||||
State = undefined,
|
State = undefined,
|
||||||
lists:foreach(fun(LocalFunc) ->
|
lists:foreach(
|
||||||
|
fun(LocalFunc) ->
|
||||||
?assertEqual({false, State}, emqx_restricted_shell:local_allowed(LocalFunc, [], State))
|
?assertEqual({false, State}, emqx_restricted_shell:local_allowed(LocalFunc, [], State))
|
||||||
end, LocalProhibited),
|
end,
|
||||||
|
LocalProhibited
|
||||||
|
),
|
||||||
LocalAllowed = [ls, pwd],
|
LocalAllowed = [ls, pwd],
|
||||||
lists:foreach(fun(LocalFunc) ->
|
lists:foreach(
|
||||||
|
fun(LocalFunc) ->
|
||||||
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(LocalFunc, [], State))
|
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(LocalFunc, [], State))
|
||||||
end, LocalAllowed),
|
end,
|
||||||
|
LocalAllowed
|
||||||
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_non_local_allowed(_Config) ->
|
t_non_local_allowed(_Config) ->
|
||||||
RemoteProhibited = [{erlang, halt}, {c, q}, {init, stop}, {init, restart}, {init, reboot}],
|
RemoteProhibited = [{erlang, halt}, {c, q}, {init, stop}, {init, restart}, {init, reboot}],
|
||||||
State = undefined,
|
State = undefined,
|
||||||
lists:foreach(fun(RemoteFunc) ->
|
lists:foreach(
|
||||||
?assertEqual({false, State}, emqx_restricted_shell:non_local_allowed(RemoteFunc, [], State))
|
fun(RemoteFunc) ->
|
||||||
end, RemoteProhibited),
|
?assertEqual(
|
||||||
|
{false, State}, emqx_restricted_shell:non_local_allowed(RemoteFunc, [], State)
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
RemoteProhibited
|
||||||
|
),
|
||||||
RemoteAllowed = [{erlang, date}, {erlang, system_time}],
|
RemoteAllowed = [{erlang, date}, {erlang, system_time}],
|
||||||
lists:foreach(fun(RemoteFunc) ->
|
lists:foreach(
|
||||||
|
fun(RemoteFunc) ->
|
||||||
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(RemoteFunc, [], State))
|
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(RemoteFunc, [], State))
|
||||||
end, RemoteAllowed),
|
end,
|
||||||
|
RemoteAllowed
|
||||||
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_lock(_Config) ->
|
t_lock(_Config) ->
|
||||||
|
@ -62,11 +76,15 @@ t_lock(_Config) ->
|
||||||
?assertEqual({false, State}, emqx_restricted_shell:local_allowed(q, [], State)),
|
?assertEqual({false, State}, emqx_restricted_shell:local_allowed(q, [], State)),
|
||||||
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(ls, [], State)),
|
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(ls, [], State)),
|
||||||
?assertEqual({false, State}, emqx_restricted_shell:non_local_allowed({init, stop}, [], State)),
|
?assertEqual({false, State}, emqx_restricted_shell:non_local_allowed({init, stop}, [], State)),
|
||||||
?assertEqual({true, State}, emqx_restricted_shell:non_local_allowed({inet, getifaddrs}, [], State)),
|
?assertEqual(
|
||||||
|
{true, State}, emqx_restricted_shell:non_local_allowed({inet, getifaddrs}, [], State)
|
||||||
|
),
|
||||||
emqx_restricted_shell:unlock(),
|
emqx_restricted_shell:unlock(),
|
||||||
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(q, [], State)),
|
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(q, [], State)),
|
||||||
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(ls, [], State)),
|
?assertEqual({true, State}, emqx_restricted_shell:local_allowed(ls, [], State)),
|
||||||
?assertEqual({true, State}, emqx_restricted_shell:non_local_allowed({init, stop}, [], State)),
|
?assertEqual({true, State}, emqx_restricted_shell:non_local_allowed({init, stop}, [], State)),
|
||||||
?assertEqual({true, State}, emqx_restricted_shell:non_local_allowed({inet, getifaddrs}, [], State)),
|
?assertEqual(
|
||||||
|
{true, State}, emqx_restricted_shell:non_local_allowed({inet, getifaddrs}, [], State)
|
||||||
|
),
|
||||||
emqx_restricted_shell:lock(),
|
emqx_restricted_shell:lock(),
|
||||||
ok.
|
ok.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
|
|
||||||
{deps, [ {emqx, {path, "../emqx"}}
|
{deps, [{emqx, {path, "../emqx"}}]}.
|
||||||
]}.
|
|
||||||
|
{project_plugins, [erlfmt]}.
|
||||||
|
|
|
@ -17,29 +17,31 @@
|
||||||
-module(emqx_placeholder).
|
-module(emqx_placeholder).
|
||||||
|
|
||||||
%% preprocess and process template string with place holders
|
%% preprocess and process template string with place holders
|
||||||
-export([ preproc_tmpl/1
|
-export([
|
||||||
, preproc_tmpl/2
|
preproc_tmpl/1,
|
||||||
, proc_tmpl/2
|
preproc_tmpl/2,
|
||||||
, proc_tmpl/3
|
proc_tmpl/2,
|
||||||
, preproc_cmd/1
|
proc_tmpl/3,
|
||||||
, proc_cmd/2
|
preproc_cmd/1,
|
||||||
, proc_cmd/3
|
proc_cmd/2,
|
||||||
, preproc_sql/1
|
proc_cmd/3,
|
||||||
, preproc_sql/2
|
preproc_sql/1,
|
||||||
, proc_sql/2
|
preproc_sql/2,
|
||||||
, proc_sql_param_str/2
|
proc_sql/2,
|
||||||
, proc_cql_param_str/2
|
proc_sql_param_str/2,
|
||||||
, preproc_tmpl_deep/1
|
proc_cql_param_str/2,
|
||||||
, preproc_tmpl_deep/2
|
preproc_tmpl_deep/1,
|
||||||
, proc_tmpl_deep/2
|
preproc_tmpl_deep/2,
|
||||||
, proc_tmpl_deep/3
|
proc_tmpl_deep/2,
|
||||||
|
proc_tmpl_deep/3,
|
||||||
|
|
||||||
, bin/1
|
bin/1,
|
||||||
, sql_data/1
|
sql_data/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(EX_PLACE_HOLDER, "(\\$\\{[a-zA-Z0-9\\._]+\\})").
|
-define(EX_PLACE_HOLDER, "(\\$\\{[a-zA-Z0-9\\._]+\\})").
|
||||||
-define(EX_WITHE_CHARS, "\\s"). %% Space and CRLF
|
%% Space and CRLF
|
||||||
|
-define(EX_WITHE_CHARS, "\\s").
|
||||||
|
|
||||||
-type tmpl_token() :: list({var, binary()} | {str, binary()}).
|
-type tmpl_token() :: list({var, binary()} | {str, binary()}).
|
||||||
|
|
||||||
|
@ -48,26 +50,32 @@
|
||||||
-type prepare_statement_key() :: binary().
|
-type prepare_statement_key() :: binary().
|
||||||
|
|
||||||
-type var_trans() ::
|
-type var_trans() ::
|
||||||
fun((FoundValue :: term()) -> binary()) |
|
fun((FoundValue :: term()) -> binary())
|
||||||
fun((Placeholder :: term(), FoundValue :: term()) -> binary()).
|
| fun((Placeholder :: term(), FoundValue :: term()) -> binary()).
|
||||||
|
|
||||||
-type preproc_tmpl_opts() :: #{placeholders => list(binary())}.
|
-type preproc_tmpl_opts() :: #{placeholders => list(binary())}.
|
||||||
|
|
||||||
-type preproc_sql_opts() :: #{placeholders => list(binary()),
|
-type preproc_sql_opts() :: #{
|
||||||
replace_with => '?' | '$n'}.
|
placeholders => list(binary()),
|
||||||
|
replace_with => '?' | '$n'
|
||||||
|
}.
|
||||||
|
|
||||||
-type preproc_deep_opts() :: #{placeholders => list(binary()),
|
-type preproc_deep_opts() :: #{
|
||||||
process_keys => boolean()}.
|
placeholders => list(binary()),
|
||||||
|
process_keys => boolean()
|
||||||
|
}.
|
||||||
|
|
||||||
-type proc_tmpl_opts() :: #{return => rawlist | full_binary,
|
-type proc_tmpl_opts() :: #{
|
||||||
var_trans => var_trans()}.
|
return => rawlist | full_binary,
|
||||||
|
var_trans => var_trans()
|
||||||
|
}.
|
||||||
|
|
||||||
-type deep_template() ::
|
-type deep_template() ::
|
||||||
#{deep_template() => deep_template()} |
|
#{deep_template() => deep_template()}
|
||||||
{tuple, [deep_template()]} |
|
| {tuple, [deep_template()]}
|
||||||
[deep_template()] |
|
| [deep_template()]
|
||||||
{tmpl, tmpl_token()} |
|
| {tmpl, tmpl_token()}
|
||||||
{value, term()}.
|
| {value, term()}.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% APIs
|
%% APIs
|
||||||
|
@ -83,7 +91,6 @@ preproc_tmpl(Str, Opts) ->
|
||||||
Tokens = re:split(Str, RE, [{return, binary}, group, trim]),
|
Tokens = re:split(Str, RE, [{return, binary}, group, trim]),
|
||||||
do_preproc_tmpl(Tokens, []).
|
do_preproc_tmpl(Tokens, []).
|
||||||
|
|
||||||
|
|
||||||
-spec proc_tmpl(tmpl_token(), map()) -> binary().
|
-spec proc_tmpl(tmpl_token(), map()) -> binary().
|
||||||
proc_tmpl(Tokens, Data) ->
|
proc_tmpl(Tokens, Data) ->
|
||||||
proc_tmpl(Tokens, Data, #{return => full_binary}).
|
proc_tmpl(Tokens, Data, #{return => full_binary}).
|
||||||
|
@ -92,20 +99,23 @@ proc_tmpl(Tokens, Data) ->
|
||||||
proc_tmpl(Tokens, Data, Opts = #{return := full_binary}) ->
|
proc_tmpl(Tokens, Data, Opts = #{return := full_binary}) ->
|
||||||
Trans = maps:get(var_trans, Opts, fun emqx_plugin_libs_rule:bin/1),
|
Trans = maps:get(var_trans, Opts, fun emqx_plugin_libs_rule:bin/1),
|
||||||
list_to_binary(
|
list_to_binary(
|
||||||
proc_tmpl(Tokens, Data, #{return => rawlist, var_trans => Trans}));
|
proc_tmpl(Tokens, Data, #{return => rawlist, var_trans => Trans})
|
||||||
|
);
|
||||||
proc_tmpl(Tokens, Data, Opts = #{return := rawlist}) ->
|
proc_tmpl(Tokens, Data, Opts = #{return := rawlist}) ->
|
||||||
Trans = maps:get(var_trans, Opts, undefined),
|
Trans = maps:get(var_trans, Opts, undefined),
|
||||||
lists:map(
|
lists:map(
|
||||||
fun ({str, Str}) -> Str;
|
fun
|
||||||
|
({str, Str}) ->
|
||||||
|
Str;
|
||||||
({var, Phld}) when is_function(Trans, 1) ->
|
({var, Phld}) when is_function(Trans, 1) ->
|
||||||
Trans(get_phld_var(Phld, Data));
|
Trans(get_phld_var(Phld, Data));
|
||||||
({var, Phld}) when is_function(Trans, 2) ->
|
({var, Phld}) when is_function(Trans, 2) ->
|
||||||
Trans(Phld, get_phld_var(Phld, Data));
|
Trans(Phld, get_phld_var(Phld, Data));
|
||||||
({var, Phld}) ->
|
({var, Phld}) ->
|
||||||
get_phld_var(Phld, Data)
|
get_phld_var(Phld, Data)
|
||||||
end, Tokens).
|
end,
|
||||||
|
Tokens
|
||||||
|
).
|
||||||
|
|
||||||
-spec preproc_cmd(binary()) -> tmpl_cmd().
|
-spec preproc_cmd(binary()) -> tmpl_cmd().
|
||||||
preproc_cmd(Str) ->
|
preproc_cmd(Str) ->
|
||||||
|
@ -119,7 +129,6 @@ proc_cmd(Tokens, Data) ->
|
||||||
proc_cmd(Tokens, Data, Opts) ->
|
proc_cmd(Tokens, Data, Opts) ->
|
||||||
[proc_tmpl(Tks, Data, Opts) || Tks <- Tokens].
|
[proc_tmpl(Tks, Data, Opts) || Tks <- Tokens].
|
||||||
|
|
||||||
|
|
||||||
%% preprocess SQL with place holders
|
%% preprocess SQL with place holders
|
||||||
-spec preproc_sql(Sql :: binary()) -> {prepare_statement_key(), tmpl_token()}.
|
-spec preproc_sql(Sql :: binary()) -> {prepare_statement_key(), tmpl_token()}.
|
||||||
preproc_sql(Sql) ->
|
preproc_sql(Sql) ->
|
||||||
|
@ -129,7 +138,6 @@ preproc_sql(Sql) ->
|
||||||
{prepare_statement_key(), tmpl_token()}.
|
{prepare_statement_key(), tmpl_token()}.
|
||||||
preproc_sql(Sql, ReplaceWith) when is_atom(ReplaceWith) ->
|
preproc_sql(Sql, ReplaceWith) when is_atom(ReplaceWith) ->
|
||||||
preproc_sql(Sql, #{replace_with => ReplaceWith});
|
preproc_sql(Sql, #{replace_with => ReplaceWith});
|
||||||
|
|
||||||
preproc_sql(Sql, Opts) ->
|
preproc_sql(Sql, Opts) ->
|
||||||
RE = preproc_var_re(Opts),
|
RE = preproc_var_re(Opts),
|
||||||
ReplaceWith = maps:get(replace_with, Opts, '?'),
|
ReplaceWith = maps:get(replace_with, Opts, '?'),
|
||||||
|
@ -141,22 +149,18 @@ preproc_sql(Sql, Opts) ->
|
||||||
{Sql, []}
|
{Sql, []}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
-spec proc_sql(tmpl_token(), map()) -> list().
|
-spec proc_sql(tmpl_token(), map()) -> list().
|
||||||
proc_sql(Tokens, Data) ->
|
proc_sql(Tokens, Data) ->
|
||||||
proc_tmpl(Tokens, Data, #{return => rawlist, var_trans => fun sql_data/1}).
|
proc_tmpl(Tokens, Data, #{return => rawlist, var_trans => fun sql_data/1}).
|
||||||
|
|
||||||
|
|
||||||
-spec proc_sql_param_str(tmpl_token(), map()) -> binary().
|
-spec proc_sql_param_str(tmpl_token(), map()) -> binary().
|
||||||
proc_sql_param_str(Tokens, Data) ->
|
proc_sql_param_str(Tokens, Data) ->
|
||||||
proc_param_str(Tokens, Data, fun quote_sql/1).
|
proc_param_str(Tokens, Data, fun quote_sql/1).
|
||||||
|
|
||||||
|
|
||||||
-spec proc_cql_param_str(tmpl_token(), map()) -> binary().
|
-spec proc_cql_param_str(tmpl_token(), map()) -> binary().
|
||||||
proc_cql_param_str(Tokens, Data) ->
|
proc_cql_param_str(Tokens, Data) ->
|
||||||
proc_param_str(Tokens, Data, fun quote_cql/1).
|
proc_param_str(Tokens, Data, fun quote_cql/1).
|
||||||
|
|
||||||
|
|
||||||
-spec preproc_tmpl_deep(term()) -> deep_template().
|
-spec preproc_tmpl_deep(term()) -> deep_template().
|
||||||
preproc_tmpl_deep(Data) ->
|
preproc_tmpl_deep(Data) ->
|
||||||
preproc_tmpl_deep(Data, #{process_keys => true}).
|
preproc_tmpl_deep(Data, #{process_keys => true}).
|
||||||
|
@ -164,26 +168,22 @@ preproc_tmpl_deep(Data) ->
|
||||||
-spec preproc_tmpl_deep(term(), preproc_deep_opts()) -> deep_template().
|
-spec preproc_tmpl_deep(term(), preproc_deep_opts()) -> deep_template().
|
||||||
preproc_tmpl_deep(List, Opts) when is_list(List) ->
|
preproc_tmpl_deep(List, Opts) when is_list(List) ->
|
||||||
[preproc_tmpl_deep(El, Opts) || El <- List];
|
[preproc_tmpl_deep(El, Opts) || El <- List];
|
||||||
|
|
||||||
preproc_tmpl_deep(Map, Opts) when is_map(Map) ->
|
preproc_tmpl_deep(Map, Opts) when is_map(Map) ->
|
||||||
maps:from_list(
|
maps:from_list(
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({K, V}) ->
|
fun({K, V}) ->
|
||||||
{preproc_tmpl_deep_map_key(K, Opts),
|
{preproc_tmpl_deep_map_key(K, Opts), preproc_tmpl_deep(V, Opts)}
|
||||||
preproc_tmpl_deep(V, Opts)}
|
|
||||||
end,
|
end,
|
||||||
maps:to_list(Map)));
|
maps:to_list(Map)
|
||||||
|
)
|
||||||
|
);
|
||||||
preproc_tmpl_deep(Binary, Opts) when is_binary(Binary) ->
|
preproc_tmpl_deep(Binary, Opts) when is_binary(Binary) ->
|
||||||
{tmpl, preproc_tmpl(Binary, Opts)};
|
{tmpl, preproc_tmpl(Binary, Opts)};
|
||||||
|
|
||||||
preproc_tmpl_deep(Tuple, Opts) when is_tuple(Tuple) ->
|
preproc_tmpl_deep(Tuple, Opts) when is_tuple(Tuple) ->
|
||||||
{tuple, preproc_tmpl_deep(tuple_to_list(Tuple), Opts)};
|
{tuple, preproc_tmpl_deep(tuple_to_list(Tuple), Opts)};
|
||||||
|
|
||||||
preproc_tmpl_deep(Other, _Opts) ->
|
preproc_tmpl_deep(Other, _Opts) ->
|
||||||
{value, Other}.
|
{value, Other}.
|
||||||
|
|
||||||
|
|
||||||
-spec proc_tmpl_deep(deep_template(), map()) -> term().
|
-spec proc_tmpl_deep(deep_template(), map()) -> term().
|
||||||
proc_tmpl_deep(DeepTmpl, Data) ->
|
proc_tmpl_deep(DeepTmpl, Data) ->
|
||||||
proc_tmpl_deep(DeepTmpl, Data, #{return => full_binary}).
|
proc_tmpl_deep(DeepTmpl, Data, #{return => full_binary}).
|
||||||
|
@ -191,24 +191,22 @@ proc_tmpl_deep(DeepTmpl, Data) ->
|
||||||
-spec proc_tmpl_deep(deep_template(), map(), proc_tmpl_opts()) -> term().
|
-spec proc_tmpl_deep(deep_template(), map(), proc_tmpl_opts()) -> term().
|
||||||
proc_tmpl_deep(List, Data, Opts) when is_list(List) ->
|
proc_tmpl_deep(List, Data, Opts) when is_list(List) ->
|
||||||
[proc_tmpl_deep(El, Data, Opts) || El <- List];
|
[proc_tmpl_deep(El, Data, Opts) || El <- List];
|
||||||
|
|
||||||
proc_tmpl_deep(Map, Data, Opts) when is_map(Map) ->
|
proc_tmpl_deep(Map, Data, Opts) when is_map(Map) ->
|
||||||
maps:from_list(
|
maps:from_list(
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({K, V}) ->
|
fun({K, V}) ->
|
||||||
{proc_tmpl_deep(K, Data, Opts),
|
{proc_tmpl_deep(K, Data, Opts), proc_tmpl_deep(V, Data, Opts)}
|
||||||
proc_tmpl_deep(V, Data, Opts)}
|
|
||||||
end,
|
end,
|
||||||
maps:to_list(Map)));
|
maps:to_list(Map)
|
||||||
|
)
|
||||||
proc_tmpl_deep({value, Value}, _Data, _Opts) -> Value;
|
);
|
||||||
|
proc_tmpl_deep({value, Value}, _Data, _Opts) ->
|
||||||
proc_tmpl_deep({tmpl, Tokens}, Data, Opts) -> proc_tmpl(Tokens, Data, Opts);
|
Value;
|
||||||
|
proc_tmpl_deep({tmpl, Tokens}, Data, Opts) ->
|
||||||
|
proc_tmpl(Tokens, Data, Opts);
|
||||||
proc_tmpl_deep({tuple, Elements}, Data, Opts) ->
|
proc_tmpl_deep({tuple, Elements}, Data, Opts) ->
|
||||||
list_to_tuple([proc_tmpl_deep(El, Data, Opts) || El <- Elements]).
|
list_to_tuple([proc_tmpl_deep(El, Data, Opts) || El <- Elements]).
|
||||||
|
|
||||||
|
|
||||||
-spec sql_data(term()) -> term().
|
-spec sql_data(term()) -> term().
|
||||||
sql_data(undefined) -> null;
|
sql_data(undefined) -> null;
|
||||||
sql_data(List) when is_list(List) -> List;
|
sql_data(List) when is_list(List) -> List;
|
||||||
|
@ -218,7 +216,6 @@ sql_data(Bool) when is_boolean(Bool) -> Bool;
|
||||||
sql_data(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8);
|
sql_data(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8);
|
||||||
sql_data(Map) when is_map(Map) -> emqx_json:encode(Map).
|
sql_data(Map) when is_map(Map) -> emqx_json:encode(Map).
|
||||||
|
|
||||||
|
|
||||||
-spec bin(term()) -> binary().
|
-spec bin(term()) -> binary().
|
||||||
bin(Val) -> emqx_plugin_libs_rule:bin(Val).
|
bin(Val) -> emqx_plugin_libs_rule:bin(Val).
|
||||||
|
|
||||||
|
@ -228,7 +225,8 @@ bin(Val) -> emqx_plugin_libs_rule:bin(Val).
|
||||||
|
|
||||||
proc_param_str(Tokens, Data, Quote) ->
|
proc_param_str(Tokens, Data, Quote) ->
|
||||||
iolist_to_binary(
|
iolist_to_binary(
|
||||||
proc_tmpl(Tokens, Data, #{return => rawlist, var_trans => Quote})).
|
proc_tmpl(Tokens, Data, #{return => rawlist, var_trans => Quote})
|
||||||
|
).
|
||||||
|
|
||||||
%% backward compatibility for hot upgrading from =< e4.2.1
|
%% backward compatibility for hot upgrading from =< e4.2.1
|
||||||
get_phld_var(Fun, Data) when is_function(Fun) ->
|
get_phld_var(Fun, Data) when is_function(Fun) ->
|
||||||
|
@ -252,19 +250,20 @@ do_preproc_tmpl([[Str, Phld] | Tokens], Acc) ->
|
||||||
put_head(
|
put_head(
|
||||||
var,
|
var,
|
||||||
parse_nested(unwrap(Phld)),
|
parse_nested(unwrap(Phld)),
|
||||||
put_head(str, Str, Acc)));
|
put_head(str, Str, Acc)
|
||||||
|
)
|
||||||
|
);
|
||||||
do_preproc_tmpl([[Str] | Tokens], Acc) ->
|
do_preproc_tmpl([[Str] | Tokens], Acc) ->
|
||||||
do_preproc_tmpl(
|
do_preproc_tmpl(
|
||||||
Tokens,
|
Tokens,
|
||||||
put_head(str, Str, Acc)).
|
put_head(str, Str, Acc)
|
||||||
|
).
|
||||||
|
|
||||||
put_head(_Type, <<>>, List) -> List;
|
put_head(_Type, <<>>, List) -> List;
|
||||||
put_head(Type, Term, List) ->
|
put_head(Type, Term, List) -> [{Type, Term} | List].
|
||||||
[{Type, Term} | List].
|
|
||||||
|
|
||||||
preproc_tmpl_deep_map_key(Key, #{process_keys := true} = Opts) ->
|
preproc_tmpl_deep_map_key(Key, #{process_keys := true} = Opts) ->
|
||||||
preproc_tmpl_deep(Key, Opts);
|
preproc_tmpl_deep(Key, Opts);
|
||||||
|
|
||||||
preproc_tmpl_deep_map_key(Key, _) ->
|
preproc_tmpl_deep_map_key(Key, _) ->
|
||||||
{value, Key}.
|
{value, Key}.
|
||||||
|
|
||||||
|
@ -274,12 +273,16 @@ replace_with(Tmpl, RE, '$n') ->
|
||||||
Parts = re:split(Tmpl, RE, [{return, binary}, trim, group]),
|
Parts = re:split(Tmpl, RE, [{return, binary}, trim, group]),
|
||||||
{Res, _} =
|
{Res, _} =
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun([Tkn, _Phld], {Acc, Seq}) ->
|
fun
|
||||||
|
([Tkn, _Phld], {Acc, Seq}) ->
|
||||||
Seq1 = erlang:integer_to_binary(Seq),
|
Seq1 = erlang:integer_to_binary(Seq),
|
||||||
{<<Acc/binary, Tkn/binary, "$", Seq1/binary>>, Seq + 1};
|
{<<Acc/binary, Tkn/binary, "$", Seq1/binary>>, Seq + 1};
|
||||||
([Tkn], {Acc, Seq}) ->
|
([Tkn], {Acc, Seq}) ->
|
||||||
{<<Acc/binary, Tkn/binary>>, Seq}
|
{<<Acc/binary, Tkn/binary>>, Seq}
|
||||||
end, {<<>>, 1}, Parts),
|
end,
|
||||||
|
{<<>>, 1},
|
||||||
|
Parts
|
||||||
|
),
|
||||||
Res.
|
Res.
|
||||||
|
|
||||||
parse_nested(Attr) ->
|
parse_nested(Attr) ->
|
||||||
|
@ -301,7 +304,8 @@ quote(Str, ReplaceWith) when
|
||||||
is_list(Str);
|
is_list(Str);
|
||||||
is_binary(Str);
|
is_binary(Str);
|
||||||
is_atom(Str);
|
is_atom(Str);
|
||||||
is_map(Str) ->
|
is_map(Str)
|
||||||
|
->
|
||||||
[$', escape_apo(bin(Str), ReplaceWith), $'];
|
[$', escape_apo(bin(Str), ReplaceWith), $'];
|
||||||
quote(Val, _) ->
|
quote(Val, _) ->
|
||||||
bin(Val).
|
bin(Val).
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_plugin_libs,
|
{application, emqx_plugin_libs, [
|
||||||
[{description, "EMQX Plugin utility libs"},
|
{description, "EMQX Plugin utility libs"},
|
||||||
{vsn, "4.3.1"},
|
{vsn, "4.3.1"},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
{applications, [kernel, stdlib]},
|
{applications, [kernel, stdlib]},
|
||||||
|
|
|
@ -19,33 +19,35 @@
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
%% API functions
|
%% API functions
|
||||||
-export([ start_link/1
|
-export([
|
||||||
, stop/1
|
start_link/1,
|
||||||
, child_spec/1
|
stop/1,
|
||||||
|
child_spec/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ inc/3
|
-export([
|
||||||
, inc/4
|
inc/3,
|
||||||
, get/3
|
inc/4,
|
||||||
, get_rate/2
|
get/3,
|
||||||
, get_counters/2
|
get_rate/2,
|
||||||
, create_metrics/3
|
get_counters/2,
|
||||||
, create_metrics/4
|
create_metrics/3,
|
||||||
, clear_metrics/2
|
create_metrics/4,
|
||||||
, reset_metrics/2
|
clear_metrics/2,
|
||||||
, has_metrics/2
|
reset_metrics/2,
|
||||||
|
has_metrics/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ get_metrics/2
|
-export([get_metrics/2]).
|
||||||
]).
|
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([ init/1
|
-export([
|
||||||
, handle_call/3
|
init/1,
|
||||||
, handle_info/2
|
handle_call/3,
|
||||||
, handle_cast/2
|
handle_info/2,
|
||||||
, code_change/3
|
handle_cast/2,
|
||||||
, terminate/2
|
code_change/3,
|
||||||
|
terminate/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-ifndef(TEST).
|
-ifndef(TEST).
|
||||||
|
@ -95,58 +97,63 @@
|
||||||
%% APIs
|
%% APIs
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
-spec(child_spec(handler_name()) -> supervisor:child_spec()).
|
-spec child_spec(handler_name()) -> supervisor:child_spec().
|
||||||
child_spec(Name) ->
|
child_spec(Name) ->
|
||||||
#{ id => emqx_plugin_libs_metrics
|
#{
|
||||||
, start => {emqx_plugin_libs_metrics, start_link, [Name]}
|
id => emqx_plugin_libs_metrics,
|
||||||
, restart => permanent
|
start => {emqx_plugin_libs_metrics, start_link, [Name]},
|
||||||
, shutdown => 5000
|
restart => permanent,
|
||||||
, type => worker
|
shutdown => 5000,
|
||||||
, modules => [emqx_plugin_libs_metrics]
|
type => worker,
|
||||||
|
modules => [emqx_plugin_libs_metrics]
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-spec(create_metrics(handler_name(), metric_id(), [atom()]) -> ok | {error, term()}).
|
-spec create_metrics(handler_name(), metric_id(), [atom()]) -> ok | {error, term()}.
|
||||||
create_metrics(Name, Id, Metrics) ->
|
create_metrics(Name, Id, Metrics) ->
|
||||||
create_metrics(Name, Id, Metrics, Metrics).
|
create_metrics(Name, Id, Metrics, Metrics).
|
||||||
|
|
||||||
-spec(create_metrics(handler_name(), metric_id(), [atom()], [atom()]) -> ok | {error, term()}).
|
-spec create_metrics(handler_name(), metric_id(), [atom()], [atom()]) -> ok | {error, term()}.
|
||||||
create_metrics(Name, Id, Metrics, RateMetrics) ->
|
create_metrics(Name, Id, Metrics, RateMetrics) ->
|
||||||
gen_server:call(Name, {create_metrics, Id, Metrics, RateMetrics}).
|
gen_server:call(Name, {create_metrics, Id, Metrics, RateMetrics}).
|
||||||
|
|
||||||
-spec(clear_metrics(handler_name(), metric_id()) -> ok).
|
-spec clear_metrics(handler_name(), metric_id()) -> ok.
|
||||||
clear_metrics(Name, Id) ->
|
clear_metrics(Name, Id) ->
|
||||||
gen_server:call(Name, {delete_metrics, Id}).
|
gen_server:call(Name, {delete_metrics, Id}).
|
||||||
|
|
||||||
-spec(reset_metrics(handler_name(), metric_id()) -> ok).
|
-spec reset_metrics(handler_name(), metric_id()) -> ok.
|
||||||
reset_metrics(Name, Id) ->
|
reset_metrics(Name, Id) ->
|
||||||
gen_server:call(Name, {reset_metrics, Id}).
|
gen_server:call(Name, {reset_metrics, Id}).
|
||||||
|
|
||||||
-spec(has_metrics(handler_name(), metric_id()) -> boolean()).
|
-spec has_metrics(handler_name(), metric_id()) -> boolean().
|
||||||
has_metrics(Name, Id) ->
|
has_metrics(Name, Id) ->
|
||||||
case get_ref(Name, Id) of
|
case get_ref(Name, Id) of
|
||||||
not_found -> false;
|
not_found -> false;
|
||||||
_ -> true
|
_ -> true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec(get(handler_name(), metric_id(), atom() | integer()) -> number()).
|
-spec get(handler_name(), metric_id(), atom() | integer()) -> number().
|
||||||
get(Name, Id, Metric) ->
|
get(Name, Id, Metric) ->
|
||||||
case get_ref(Name, Id) of
|
case get_ref(Name, Id) of
|
||||||
not_found -> 0;
|
not_found ->
|
||||||
|
0;
|
||||||
Ref when is_atom(Metric) ->
|
Ref when is_atom(Metric) ->
|
||||||
counters:get(Ref, idx_metric(Name, Id, Metric));
|
counters:get(Ref, idx_metric(Name, Id, Metric));
|
||||||
Ref when is_integer(Metric) ->
|
Ref when is_integer(Metric) ->
|
||||||
counters:get(Ref, Metric)
|
counters:get(Ref, Metric)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec(get_rate(handler_name(), metric_id()) -> map()).
|
-spec get_rate(handler_name(), metric_id()) -> map().
|
||||||
get_rate(Name, Id) ->
|
get_rate(Name, Id) ->
|
||||||
gen_server:call(Name, {get_rate, Id}).
|
gen_server:call(Name, {get_rate, Id}).
|
||||||
|
|
||||||
-spec(get_counters(handler_name(), metric_id()) -> map()).
|
-spec get_counters(handler_name(), metric_id()) -> map().
|
||||||
get_counters(Name, Id) ->
|
get_counters(Name, Id) ->
|
||||||
maps:map(fun(_Metric, Index) ->
|
maps:map(
|
||||||
|
fun(_Metric, Index) ->
|
||||||
get(Name, Id, Index)
|
get(Name, Id, Index)
|
||||||
end, get_indexes(Name, Id)).
|
end,
|
||||||
|
get_indexes(Name, Id)
|
||||||
|
).
|
||||||
|
|
||||||
-spec reset_counters(handler_name(), metric_id()) -> ok.
|
-spec reset_counters(handler_name(), metric_id()) -> ok.
|
||||||
reset_counters(Name, Id) ->
|
reset_counters(Name, Id) ->
|
||||||
|
@ -155,7 +162,7 @@ reset_counters(Name, Id) ->
|
||||||
[counters:put(Ref, Idx, 0) || Idx <- Indexes],
|
[counters:put(Ref, Idx, 0) || Idx <- Indexes],
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec(get_metrics(handler_name(), metric_id()) -> metrics()).
|
-spec get_metrics(handler_name(), metric_id()) -> metrics().
|
||||||
get_metrics(Name, Id) ->
|
get_metrics(Name, Id) ->
|
||||||
#{rate => get_rate(Name, Id), counters => get_counters(Name, Id)}.
|
#{rate => get_rate(Name, Id), counters => get_counters(Name, Id)}.
|
||||||
|
|
||||||
|
@ -180,47 +187,63 @@ init(Name) ->
|
||||||
handle_call({get_rate, _Id}, _From, State = #state{rates = undefined}) ->
|
handle_call({get_rate, _Id}, _From, State = #state{rates = undefined}) ->
|
||||||
{reply, make_rate(0, 0, 0), State};
|
{reply, make_rate(0, 0, 0), State};
|
||||||
handle_call({get_rate, Id}, _From, State = #state{rates = Rates}) ->
|
handle_call({get_rate, Id}, _From, State = #state{rates = Rates}) ->
|
||||||
{reply, case maps:get(Id, Rates, undefined) of
|
{reply,
|
||||||
|
case maps:get(Id, Rates, undefined) of
|
||||||
undefined -> make_rate(0, 0, 0);
|
undefined -> make_rate(0, 0, 0);
|
||||||
RatesPerId -> format_rates_of_id(RatesPerId)
|
RatesPerId -> format_rates_of_id(RatesPerId)
|
||||||
end, State};
|
end, State};
|
||||||
|
handle_call(
|
||||||
handle_call({create_metrics, Id, Metrics, RateMetrics}, _From,
|
{create_metrics, Id, Metrics, RateMetrics},
|
||||||
State = #state{metric_ids = MIDs, rates = Rates}) ->
|
_From,
|
||||||
|
State = #state{metric_ids = MIDs, rates = Rates}
|
||||||
|
) ->
|
||||||
case RateMetrics -- Metrics of
|
case RateMetrics -- Metrics of
|
||||||
[] ->
|
[] ->
|
||||||
RatePerId = maps:from_list([{M, #rate{}} || M <- RateMetrics]),
|
RatePerId = maps:from_list([{M, #rate{}} || M <- RateMetrics]),
|
||||||
Rate1 = case Rates of
|
Rate1 =
|
||||||
|
case Rates of
|
||||||
undefined -> #{Id => RatePerId};
|
undefined -> #{Id => RatePerId};
|
||||||
_ -> Rates#{Id => RatePerId}
|
_ -> Rates#{Id => RatePerId}
|
||||||
end,
|
end,
|
||||||
{reply, create_counters(get_self_name(), Id, Metrics),
|
{reply, create_counters(get_self_name(), Id, Metrics), State#state{
|
||||||
State#state{metric_ids = sets:add_element(Id, MIDs),
|
metric_ids = sets:add_element(Id, MIDs),
|
||||||
rates = Rate1}};
|
rates = Rate1
|
||||||
|
}};
|
||||||
_ ->
|
_ ->
|
||||||
{reply, {error, not_super_set_of, {RateMetrics, Metrics}}, State}
|
{reply, {error, not_super_set_of, {RateMetrics, Metrics}}, State}
|
||||||
end;
|
end;
|
||||||
|
handle_call(
|
||||||
handle_call({delete_metrics, Id}, _From,
|
{delete_metrics, Id},
|
||||||
State = #state{metric_ids = MIDs, rates = Rates}) ->
|
_From,
|
||||||
{reply, delete_counters(get_self_name(), Id),
|
State = #state{metric_ids = MIDs, rates = Rates}
|
||||||
State#state{metric_ids = sets:del_element(Id, MIDs),
|
) ->
|
||||||
rates = case Rates of
|
{reply, delete_counters(get_self_name(), Id), State#state{
|
||||||
|
metric_ids = sets:del_element(Id, MIDs),
|
||||||
|
rates =
|
||||||
|
case Rates of
|
||||||
undefined -> undefined;
|
undefined -> undefined;
|
||||||
_ -> maps:remove(Id, Rates)
|
_ -> maps:remove(Id, Rates)
|
||||||
end}};
|
end
|
||||||
|
}};
|
||||||
handle_call({reset_metrics, Id}, _From,
|
handle_call(
|
||||||
State = #state{rates = Rates}) ->
|
{reset_metrics, Id},
|
||||||
{reply, reset_counters(get_self_name(), Id),
|
_From,
|
||||||
State#state{rates = case Rates of
|
State = #state{rates = Rates}
|
||||||
undefined -> undefined;
|
) ->
|
||||||
_ -> ResetRate =
|
{reply, reset_counters(get_self_name(), Id), State#state{
|
||||||
maps:map(fun(_Key, _Value) -> #rate{} end,
|
rates =
|
||||||
maps:get(Id, Rates, #{})),
|
case Rates of
|
||||||
|
undefined ->
|
||||||
|
undefined;
|
||||||
|
_ ->
|
||||||
|
ResetRate =
|
||||||
|
maps:map(
|
||||||
|
fun(_Key, _Value) -> #rate{} end,
|
||||||
|
maps:get(Id, Rates, #{})
|
||||||
|
),
|
||||||
maps:put(Id, ResetRate, Rates)
|
maps:put(Id, ResetRate, Rates)
|
||||||
end}};
|
end
|
||||||
|
}};
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
{reply, ok, State}.
|
{reply, ok, State}.
|
||||||
|
|
||||||
|
@ -230,17 +253,21 @@ handle_cast(_Msg, State) ->
|
||||||
handle_info(ticking, State = #state{rates = undefined}) ->
|
handle_info(ticking, State = #state{rates = undefined}) ->
|
||||||
erlang:send_after(timer:seconds(?SAMPLING), self(), ticking),
|
erlang:send_after(timer:seconds(?SAMPLING), self(), ticking),
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
|
||||||
handle_info(ticking, State = #state{rates = Rates0}) ->
|
handle_info(ticking, State = #state{rates = Rates0}) ->
|
||||||
Rates =
|
Rates =
|
||||||
maps:map(fun(Id, RatesPerID) ->
|
maps:map(
|
||||||
maps:map(fun(Metric, Rate) ->
|
fun(Id, RatesPerID) ->
|
||||||
|
maps:map(
|
||||||
|
fun(Metric, Rate) ->
|
||||||
calculate_rate(get(get_self_name(), Id, Metric), Rate)
|
calculate_rate(get(get_self_name(), Id, Metric), Rate)
|
||||||
end, RatesPerID)
|
end,
|
||||||
end, Rates0),
|
RatesPerID
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
Rates0
|
||||||
|
),
|
||||||
erlang:send_after(timer:seconds(?SAMPLING), self(), ticking),
|
erlang:send_after(timer:seconds(?SAMPLING), self(), ticking),
|
||||||
{noreply, State#state{rates = Rates}};
|
{noreply, State#state{rates = Rates}};
|
||||||
|
|
||||||
handle_info(_Info, State) ->
|
handle_info(_Info, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
|
@ -269,12 +296,17 @@ create_counters(Name, Id, Metrics) ->
|
||||||
Indexes = maps:from_list(lists:zip(Metrics, lists:seq(1, Size))),
|
Indexes = maps:from_list(lists:zip(Metrics, lists:seq(1, Size))),
|
||||||
Counters = get_pterm(Name),
|
Counters = get_pterm(Name),
|
||||||
CntrRef = counters:new(Size, [write_concurrency]),
|
CntrRef = counters:new(Size, [write_concurrency]),
|
||||||
persistent_term:put(?CntrRef(Name),
|
persistent_term:put(
|
||||||
Counters#{Id => #{ref => CntrRef, indexes => Indexes}}),
|
?CntrRef(Name),
|
||||||
|
Counters#{Id => #{ref => CntrRef, indexes => Indexes}}
|
||||||
|
),
|
||||||
%% restore the old counters
|
%% restore the old counters
|
||||||
lists:foreach(fun({Metric, N}) ->
|
lists:foreach(
|
||||||
|
fun({Metric, N}) ->
|
||||||
inc(Name, Id, Metric, N)
|
inc(Name, Id, Metric, N)
|
||||||
end, maps:to_list(OlderCounters)).
|
end,
|
||||||
|
maps:to_list(OlderCounters)
|
||||||
|
).
|
||||||
|
|
||||||
delete_counters(Name, Id) ->
|
delete_counters(Name, Id) ->
|
||||||
persistent_term:put(?CntrRef(Name), maps:remove(Id, get_pterm(Name))).
|
persistent_term:put(?CntrRef(Name), maps:remove(Id, get_pterm(Name))).
|
||||||
|
@ -299,9 +331,13 @@ get_pterm(Name) ->
|
||||||
|
|
||||||
calculate_rate(_CurrVal, undefined) ->
|
calculate_rate(_CurrVal, undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
calculate_rate(CurrVal, #rate{max = MaxRate0, last_v = LastVal,
|
calculate_rate(CurrVal, #rate{
|
||||||
tick = Tick, last5m_acc = AccRate5Min0,
|
max = MaxRate0,
|
||||||
last5m_smpl = Last5MinSamples0}) ->
|
last_v = LastVal,
|
||||||
|
tick = Tick,
|
||||||
|
last5m_acc = AccRate5Min0,
|
||||||
|
last5m_smpl = Last5MinSamples0
|
||||||
|
}) ->
|
||||||
%% calculate the current rate based on the last value of the counter
|
%% calculate the current rate based on the last value of the counter
|
||||||
CurrRate = (CurrVal - LastVal) / ?SAMPLING,
|
CurrRate = (CurrVal - LastVal) / ?SAMPLING,
|
||||||
|
|
||||||
|
@ -317,31 +353,39 @@ calculate_rate(CurrVal, #rate{max = MaxRate0, last_v = LastVal,
|
||||||
case Tick =< ?SAMPCOUNT_5M of
|
case Tick =< ?SAMPCOUNT_5M of
|
||||||
true ->
|
true ->
|
||||||
Acc = AccRate5Min0 + CurrRate,
|
Acc = AccRate5Min0 + CurrRate,
|
||||||
{lists:reverse([CurrRate | lists:reverse(Last5MinSamples0)]),
|
{lists:reverse([CurrRate | lists:reverse(Last5MinSamples0)]), Acc, Acc / Tick};
|
||||||
Acc, Acc / Tick};
|
|
||||||
false ->
|
false ->
|
||||||
[FirstRate | Rates] = Last5MinSamples0,
|
[FirstRate | Rates] = Last5MinSamples0,
|
||||||
Acc = AccRate5Min0 + CurrRate - FirstRate,
|
Acc = AccRate5Min0 + CurrRate - FirstRate,
|
||||||
{lists:reverse([CurrRate | lists:reverse(Rates)]),
|
{lists:reverse([CurrRate | lists:reverse(Rates)]), Acc, Acc / ?SAMPCOUNT_5M}
|
||||||
Acc, Acc / ?SAMPCOUNT_5M}
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
#rate{max = MaxRate, current = CurrRate, last5m = Last5Min,
|
#rate{
|
||||||
last_v = CurrVal, last5m_acc = Acc5Min,
|
max = MaxRate,
|
||||||
last5m_smpl = Last5MinSamples, tick = Tick + 1}.
|
current = CurrRate,
|
||||||
|
last5m = Last5Min,
|
||||||
|
last_v = CurrVal,
|
||||||
|
last5m_acc = Acc5Min,
|
||||||
|
last5m_smpl = Last5MinSamples,
|
||||||
|
tick = Tick + 1
|
||||||
|
}.
|
||||||
|
|
||||||
format_rates_of_id(RatesPerId) ->
|
format_rates_of_id(RatesPerId) ->
|
||||||
maps:map(fun(_Metric, Rates) ->
|
maps:map(
|
||||||
|
fun(_Metric, Rates) ->
|
||||||
format_rate(Rates)
|
format_rate(Rates)
|
||||||
end, RatesPerId).
|
end,
|
||||||
|
RatesPerId
|
||||||
|
).
|
||||||
|
|
||||||
format_rate(#rate{max = Max, current = Current, last5m = Last5Min}) ->
|
format_rate(#rate{max = Max, current = Current, last5m = Last5Min}) ->
|
||||||
make_rate(Current, Max, Last5Min).
|
make_rate(Current, Max, Last5Min).
|
||||||
|
|
||||||
make_rate(Current, Max, Last5Min) ->
|
make_rate(Current, Max, Last5Min) ->
|
||||||
#{ current => precision(Current, 2)
|
#{
|
||||||
, max => precision(Max, 2)
|
current => precision(Current, 2),
|
||||||
, last5m => precision(Last5Min, 2)
|
max => precision(Max, 2),
|
||||||
|
last5m => precision(Last5Min, 2)
|
||||||
}.
|
}.
|
||||||
|
|
||||||
precision(Float, N) ->
|
precision(Float, N) ->
|
||||||
|
|
|
@ -16,10 +16,11 @@
|
||||||
|
|
||||||
-module(emqx_plugin_libs_pool).
|
-module(emqx_plugin_libs_pool).
|
||||||
|
|
||||||
-export([ start_pool/3
|
-export([
|
||||||
, stop_pool/1
|
start_pool/3,
|
||||||
, pool_name/1
|
stop_pool/1,
|
||||||
, health_check/3
|
pool_name/1,
|
||||||
|
health_check/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
@ -36,8 +37,11 @@ start_pool(Name, Mod, Options) ->
|
||||||
stop_pool(Name),
|
stop_pool(Name),
|
||||||
start_pool(Name, Mod, Options);
|
start_pool(Name, Mod, Options);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?SLOG(error, #{msg => "start_ecpool_error", pool_name => Name,
|
?SLOG(error, #{
|
||||||
reason => Reason}),
|
msg => "start_ecpool_error",
|
||||||
|
pool_name => Name,
|
||||||
|
reason => Reason
|
||||||
|
}),
|
||||||
{error, {start_pool_failed, Name, Reason}}
|
{error, {start_pool_failed, Name, Reason}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -48,18 +52,24 @@ stop_pool(Name) ->
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
ok;
|
ok;
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?SLOG(error, #{msg => "stop_ecpool_failed", pool_name => Name,
|
?SLOG(error, #{
|
||||||
reason => Reason}),
|
msg => "stop_ecpool_failed",
|
||||||
|
pool_name => Name,
|
||||||
|
reason => Reason
|
||||||
|
}),
|
||||||
error({stop_pool_failed, Name, Reason})
|
error({stop_pool_failed, Name, Reason})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
health_check(PoolName, CheckFunc, State) when is_function(CheckFunc) ->
|
health_check(PoolName, CheckFunc, State) when is_function(CheckFunc) ->
|
||||||
Status = [begin
|
Status = [
|
||||||
|
begin
|
||||||
case ecpool_worker:client(Worker) of
|
case ecpool_worker:client(Worker) of
|
||||||
{ok, Conn} -> CheckFunc(Conn);
|
{ok, Conn} -> CheckFunc(Conn);
|
||||||
_ -> false
|
_ -> false
|
||||||
end
|
end
|
||||||
end || {_WorkerName, Worker} <- ecpool:workers(PoolName)],
|
end
|
||||||
|
|| {_WorkerName, Worker} <- ecpool:workers(PoolName)
|
||||||
|
],
|
||||||
case length(Status) > 0 andalso lists:all(fun(St) -> St =:= true end, Status) of
|
case length(Status) > 0 andalso lists:all(fun(St) -> St =:= true end, Status) of
|
||||||
true -> {ok, State};
|
true -> {ok, State};
|
||||||
false -> {error, health_check_failed, State}
|
false -> {error, health_check_failed, State}
|
||||||
|
|
|
@ -18,106 +18,109 @@
|
||||||
-elvis([{elvis_style, god_modules, disable}]).
|
-elvis([{elvis_style, god_modules, disable}]).
|
||||||
|
|
||||||
%% preprocess and process template string with place holders
|
%% preprocess and process template string with place holders
|
||||||
-export([ preproc_tmpl/1
|
-export([
|
||||||
, proc_tmpl/2
|
preproc_tmpl/1,
|
||||||
, proc_tmpl/3
|
proc_tmpl/2,
|
||||||
, preproc_cmd/1
|
proc_tmpl/3,
|
||||||
, proc_cmd/2
|
preproc_cmd/1,
|
||||||
, proc_cmd/3
|
proc_cmd/2,
|
||||||
, preproc_sql/1
|
proc_cmd/3,
|
||||||
, preproc_sql/2
|
preproc_sql/1,
|
||||||
, proc_sql/2
|
preproc_sql/2,
|
||||||
, proc_sql_param_str/2
|
proc_sql/2,
|
||||||
, proc_cql_param_str/2
|
proc_sql_param_str/2,
|
||||||
|
proc_cql_param_str/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% type converting
|
%% type converting
|
||||||
-export([ str/1
|
-export([
|
||||||
, bin/1
|
str/1,
|
||||||
, bool/1
|
bin/1,
|
||||||
, int/1
|
bool/1,
|
||||||
, float/1
|
int/1,
|
||||||
, map/1
|
float/1,
|
||||||
, utf8_bin/1
|
map/1,
|
||||||
, utf8_str/1
|
utf8_bin/1,
|
||||||
, number_to_binary/1
|
utf8_str/1,
|
||||||
, atom_key/1
|
number_to_binary/1,
|
||||||
, unsafe_atom_key/1
|
atom_key/1,
|
||||||
|
unsafe_atom_key/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% connectivity check
|
%% connectivity check
|
||||||
-export([ http_connectivity/1
|
-export([
|
||||||
, http_connectivity/2
|
http_connectivity/1,
|
||||||
, tcp_connectivity/2
|
http_connectivity/2,
|
||||||
, tcp_connectivity/3
|
tcp_connectivity/2,
|
||||||
|
tcp_connectivity/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ now_ms/0
|
-export([
|
||||||
, can_topic_match_oneof/2
|
now_ms/0,
|
||||||
|
can_topic_match_oneof/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([cluster_call/3]).
|
-export([cluster_call/3]).
|
||||||
|
|
||||||
-compile({no_auto_import,
|
-compile({no_auto_import, [float/1]}).
|
||||||
[ float/1
|
|
||||||
]}).
|
|
||||||
|
|
||||||
-define(EX_PLACE_HOLDER, "(\\$\\{[a-zA-Z0-9\\._]+\\})").
|
-define(EX_PLACE_HOLDER, "(\\$\\{[a-zA-Z0-9\\._]+\\})").
|
||||||
|
|
||||||
-define(EX_WITHE_CHARS, "\\s"). %% Space and CRLF
|
%% Space and CRLF
|
||||||
|
-define(EX_WITHE_CHARS, "\\s").
|
||||||
|
|
||||||
-type(uri_string() :: iodata()).
|
-type uri_string() :: iodata().
|
||||||
|
|
||||||
-type(tmpl_token() :: list({var, binary()} | {str, binary()})).
|
-type tmpl_token() :: list({var, binary()} | {str, binary()}).
|
||||||
|
|
||||||
-type(tmpl_cmd() :: list(tmpl_token())).
|
-type tmpl_cmd() :: list(tmpl_token()).
|
||||||
|
|
||||||
-type(prepare_statement_key() :: binary()).
|
-type prepare_statement_key() :: binary().
|
||||||
|
|
||||||
%% preprocess template string with place holders
|
%% preprocess template string with place holders
|
||||||
-spec(preproc_tmpl(binary()) -> tmpl_token()).
|
-spec preproc_tmpl(binary()) -> tmpl_token().
|
||||||
preproc_tmpl(Str) ->
|
preproc_tmpl(Str) ->
|
||||||
emqx_placeholder:preproc_tmpl(Str).
|
emqx_placeholder:preproc_tmpl(Str).
|
||||||
|
|
||||||
-spec(proc_tmpl(tmpl_token(), map()) -> binary()).
|
-spec proc_tmpl(tmpl_token(), map()) -> binary().
|
||||||
proc_tmpl(Tokens, Data) ->
|
proc_tmpl(Tokens, Data) ->
|
||||||
emqx_placeholder:proc_tmpl(Tokens, Data).
|
emqx_placeholder:proc_tmpl(Tokens, Data).
|
||||||
|
|
||||||
-spec(proc_tmpl(tmpl_token(), map(), map()) -> binary() | list()).
|
-spec proc_tmpl(tmpl_token(), map(), map()) -> binary() | list().
|
||||||
proc_tmpl(Tokens, Data, Opts) ->
|
proc_tmpl(Tokens, Data, Opts) ->
|
||||||
emqx_placeholder:proc_tmpl(Tokens, Data, Opts).
|
emqx_placeholder:proc_tmpl(Tokens, Data, Opts).
|
||||||
|
|
||||||
-spec(preproc_cmd(binary()) -> tmpl_cmd()).
|
-spec preproc_cmd(binary()) -> tmpl_cmd().
|
||||||
preproc_cmd(Str) ->
|
preproc_cmd(Str) ->
|
||||||
emqx_placeholder:preproc_cmd(Str).
|
emqx_placeholder:preproc_cmd(Str).
|
||||||
|
|
||||||
-spec(proc_cmd([tmpl_token()], map()) -> binary() | list()).
|
-spec proc_cmd([tmpl_token()], map()) -> binary() | list().
|
||||||
proc_cmd(Tokens, Data) ->
|
proc_cmd(Tokens, Data) ->
|
||||||
emqx_placeholder:proc_cmd(Tokens, Data).
|
emqx_placeholder:proc_cmd(Tokens, Data).
|
||||||
-spec(proc_cmd([tmpl_token()], map(), map()) -> list()).
|
-spec proc_cmd([tmpl_token()], map(), map()) -> list().
|
||||||
proc_cmd(Tokens, Data, Opts) ->
|
proc_cmd(Tokens, Data, Opts) ->
|
||||||
emqx_placeholder:proc_cmd(Tokens, Data, Opts).
|
emqx_placeholder:proc_cmd(Tokens, Data, Opts).
|
||||||
|
|
||||||
%% preprocess SQL with place holders
|
%% preprocess SQL with place holders
|
||||||
-spec(preproc_sql(Sql::binary()) -> {prepare_statement_key(), tmpl_token()}).
|
-spec preproc_sql(Sql :: binary()) -> {prepare_statement_key(), tmpl_token()}.
|
||||||
preproc_sql(Sql) ->
|
preproc_sql(Sql) ->
|
||||||
emqx_placeholder:preproc_sql(Sql).
|
emqx_placeholder:preproc_sql(Sql).
|
||||||
|
|
||||||
-spec(preproc_sql(Sql::binary(), ReplaceWith :: '?' | '$n')
|
-spec preproc_sql(Sql :: binary(), ReplaceWith :: '?' | '$n') ->
|
||||||
-> {prepare_statement_key(), tmpl_token()}).
|
{prepare_statement_key(), tmpl_token()}.
|
||||||
|
|
||||||
preproc_sql(Sql, ReplaceWith) ->
|
preproc_sql(Sql, ReplaceWith) ->
|
||||||
emqx_placeholder:preproc_sql(Sql, ReplaceWith).
|
emqx_placeholder:preproc_sql(Sql, ReplaceWith).
|
||||||
|
|
||||||
-spec(proc_sql(tmpl_token(), map()) -> list()).
|
-spec proc_sql(tmpl_token(), map()) -> list().
|
||||||
proc_sql(Tokens, Data) ->
|
proc_sql(Tokens, Data) ->
|
||||||
emqx_placeholder:proc_sql(Tokens, Data).
|
emqx_placeholder:proc_sql(Tokens, Data).
|
||||||
|
|
||||||
-spec(proc_sql_param_str(tmpl_token(), map()) -> binary()).
|
-spec proc_sql_param_str(tmpl_token(), map()) -> binary().
|
||||||
proc_sql_param_str(Tokens, Data) ->
|
proc_sql_param_str(Tokens, Data) ->
|
||||||
emqx_placeholder:proc_sql_param_str(Tokens, Data).
|
emqx_placeholder:proc_sql_param_str(Tokens, Data).
|
||||||
|
|
||||||
-spec(proc_cql_param_str(tmpl_token(), map()) -> binary()).
|
-spec proc_cql_param_str(tmpl_token(), map()) -> binary().
|
||||||
proc_cql_param_str(Tokens, Data) ->
|
proc_cql_param_str(Tokens, Data) ->
|
||||||
emqx_placeholder:proc_cql_param_str(Tokens, Data).
|
emqx_placeholder:proc_cql_param_str(Tokens, Data).
|
||||||
|
|
||||||
|
@ -133,19 +136,22 @@ unsafe_atom_key(Key) ->
|
||||||
atom_key(Key) when is_atom(Key) ->
|
atom_key(Key) when is_atom(Key) ->
|
||||||
Key;
|
Key;
|
||||||
atom_key(Key) when is_binary(Key) ->
|
atom_key(Key) when is_binary(Key) ->
|
||||||
try binary_to_existing_atom(Key, utf8)
|
try
|
||||||
catch error:badarg -> error({invalid_key, Key})
|
binary_to_existing_atom(Key, utf8)
|
||||||
|
catch
|
||||||
|
error:badarg -> error({invalid_key, Key})
|
||||||
end;
|
end;
|
||||||
atom_key(Keys = [_Key | _]) -> %% nested keys
|
%% nested keys
|
||||||
|
atom_key(Keys = [_Key | _]) ->
|
||||||
[atom_key(SubKey) || SubKey <- Keys];
|
[atom_key(SubKey) || SubKey <- Keys];
|
||||||
atom_key(Key) ->
|
atom_key(Key) ->
|
||||||
error({invalid_key, Key}).
|
error({invalid_key, Key}).
|
||||||
|
|
||||||
-spec(http_connectivity(uri_string()) -> ok | {error, Reason :: term()}).
|
-spec http_connectivity(uri_string()) -> ok | {error, Reason :: term()}.
|
||||||
http_connectivity(Url) ->
|
http_connectivity(Url) ->
|
||||||
http_connectivity(Url, 3000).
|
http_connectivity(Url, 3000).
|
||||||
|
|
||||||
-spec(http_connectivity(uri_string(), integer()) -> ok | {error, Reason :: term()}).
|
-spec http_connectivity(uri_string(), integer()) -> ok | {error, Reason :: term()}.
|
||||||
http_connectivity(Url, Timeout) ->
|
http_connectivity(Url, Timeout) ->
|
||||||
case emqx_http_lib:uri_parse(Url) of
|
case emqx_http_lib:uri_parse(Url) of
|
||||||
{ok, #{host := Host, port := Port}} ->
|
{ok, #{host := Host, port := Port}} ->
|
||||||
|
@ -154,20 +160,27 @@ http_connectivity(Url, Timeout) ->
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec tcp_connectivity(Host :: inet:socket_address() | inet:hostname(),
|
-spec tcp_connectivity(
|
||||||
Port :: inet:port_number())
|
Host :: inet:socket_address() | inet:hostname(),
|
||||||
-> ok | {error, Reason :: term()}.
|
Port :: inet:port_number()
|
||||||
|
) ->
|
||||||
|
ok | {error, Reason :: term()}.
|
||||||
tcp_connectivity(Host, Port) ->
|
tcp_connectivity(Host, Port) ->
|
||||||
tcp_connectivity(Host, Port, 3000).
|
tcp_connectivity(Host, Port, 3000).
|
||||||
|
|
||||||
-spec(tcp_connectivity(Host :: inet:socket_address() | inet:hostname(),
|
-spec tcp_connectivity(
|
||||||
|
Host :: inet:socket_address() | inet:hostname(),
|
||||||
Port :: inet:port_number(),
|
Port :: inet:port_number(),
|
||||||
Timeout :: integer())
|
Timeout :: integer()
|
||||||
-> ok | {error, Reason :: term()}).
|
) ->
|
||||||
|
ok | {error, Reason :: term()}.
|
||||||
tcp_connectivity(Host, Port, Timeout) ->
|
tcp_connectivity(Host, Port, Timeout) ->
|
||||||
case gen_tcp:connect(Host, Port, emqx_misc:ipv6_probe([]), Timeout) of
|
case gen_tcp:connect(Host, Port, emqx_misc:ipv6_probe([]), Timeout) of
|
||||||
{ok, Sock} -> gen_tcp:close(Sock), ok;
|
{ok, Sock} ->
|
||||||
{error, Reason} -> {error, Reason}
|
gen_tcp:close(Sock),
|
||||||
|
ok;
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
str(Bin) when is_binary(Bin) -> binary_to_list(Bin);
|
str(Bin) when is_binary(Bin) -> binary_to_list(Bin);
|
||||||
|
@ -179,7 +192,8 @@ str(List) when is_list(List) ->
|
||||||
true -> List;
|
true -> List;
|
||||||
false -> binary_to_list(emqx_json:encode(List))
|
false -> binary_to_list(emqx_json:encode(List))
|
||||||
end;
|
end;
|
||||||
str(Data) -> error({invalid_str, Data}).
|
str(Data) ->
|
||||||
|
error({invalid_str, Data}).
|
||||||
|
|
||||||
utf8_bin(Str) when is_binary(Str); is_list(Str) ->
|
utf8_bin(Str) when is_binary(Str); is_list(Str) ->
|
||||||
unicode:characters_to_binary(Str);
|
unicode:characters_to_binary(Str);
|
||||||
|
@ -200,36 +214,49 @@ bin(List) when is_list(List) ->
|
||||||
true -> list_to_binary(List);
|
true -> list_to_binary(List);
|
||||||
false -> emqx_json:encode(List)
|
false -> emqx_json:encode(List)
|
||||||
end;
|
end;
|
||||||
bin(Data) -> error({invalid_bin, Data}).
|
bin(Data) ->
|
||||||
|
error({invalid_bin, Data}).
|
||||||
|
|
||||||
int(List) when is_list(List) ->
|
int(List) when is_list(List) ->
|
||||||
try list_to_integer(List)
|
try
|
||||||
catch error:badarg ->
|
list_to_integer(List)
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
int(list_to_float(List))
|
int(list_to_float(List))
|
||||||
end;
|
end;
|
||||||
int(Bin) when is_binary(Bin) ->
|
int(Bin) when is_binary(Bin) ->
|
||||||
try binary_to_integer(Bin)
|
try
|
||||||
catch error:badarg ->
|
binary_to_integer(Bin)
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
int(binary_to_float(Bin))
|
int(binary_to_float(Bin))
|
||||||
end;
|
end;
|
||||||
int(Int) when is_integer(Int) -> Int;
|
int(Int) when is_integer(Int) -> Int;
|
||||||
int(Float) when is_float(Float) -> erlang:floor(Float);
|
int(Float) when is_float(Float) -> erlang:floor(Float);
|
||||||
int(true) -> 1;
|
int(true) ->
|
||||||
int(false) -> 0;
|
1;
|
||||||
int(Data) -> error({invalid_number, Data}).
|
int(false) ->
|
||||||
|
0;
|
||||||
|
int(Data) ->
|
||||||
|
error({invalid_number, Data}).
|
||||||
|
|
||||||
float(List) when is_list(List) ->
|
float(List) when is_list(List) ->
|
||||||
try list_to_float(List)
|
try
|
||||||
catch error:badarg ->
|
list_to_float(List)
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
float(list_to_integer(List))
|
float(list_to_integer(List))
|
||||||
end;
|
end;
|
||||||
float(Bin) when is_binary(Bin) ->
|
float(Bin) when is_binary(Bin) ->
|
||||||
try binary_to_float(Bin)
|
try
|
||||||
catch error:badarg ->
|
binary_to_float(Bin)
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
float(binary_to_integer(Bin))
|
float(binary_to_integer(Bin))
|
||||||
end;
|
end;
|
||||||
float(Num) when is_number(Num) -> erlang:float(Num);
|
float(Num) when is_number(Num) -> erlang:float(Num);
|
||||||
float(Data) -> error({invalid_number, Data}).
|
float(Data) ->
|
||||||
|
error({invalid_number, Data}).
|
||||||
|
|
||||||
map(Bin) when is_binary(Bin) ->
|
map(Bin) when is_binary(Bin) ->
|
||||||
case emqx_json:decode(Bin, [return_maps]) of
|
case emqx_json:decode(Bin, [return_maps]) of
|
||||||
|
@ -238,16 +265,23 @@ map(Bin) when is_binary(Bin) ->
|
||||||
end;
|
end;
|
||||||
map(List) when is_list(List) -> maps:from_list(List);
|
map(List) when is_list(List) -> maps:from_list(List);
|
||||||
map(Map) when is_map(Map) -> Map;
|
map(Map) when is_map(Map) -> Map;
|
||||||
map(Data) -> error({invalid_map, Data}).
|
map(Data) ->
|
||||||
|
error({invalid_map, Data}).
|
||||||
|
|
||||||
|
bool(Bool) when
|
||||||
bool(Bool) when Bool == true;
|
Bool == true;
|
||||||
Bool == <<"true">>;
|
Bool == <<"true">>;
|
||||||
Bool == 1 -> true;
|
Bool == 1
|
||||||
bool(Bool) when Bool == false;
|
->
|
||||||
|
true;
|
||||||
|
bool(Bool) when
|
||||||
|
Bool == false;
|
||||||
Bool == <<"false">>;
|
Bool == <<"false">>;
|
||||||
Bool == 0 -> false;
|
Bool == 0
|
||||||
bool(Bool) -> error({invalid_boolean, Bool}).
|
->
|
||||||
|
false;
|
||||||
|
bool(Bool) ->
|
||||||
|
error({invalid_boolean, Bool}).
|
||||||
|
|
||||||
number_to_binary(Int) when is_integer(Int) ->
|
number_to_binary(Int) when is_integer(Int) ->
|
||||||
integer_to_binary(Int);
|
integer_to_binary(Int);
|
||||||
|
@ -263,9 +297,12 @@ now_ms() ->
|
||||||
erlang:system_time(millisecond).
|
erlang:system_time(millisecond).
|
||||||
|
|
||||||
can_topic_match_oneof(Topic, Filters) ->
|
can_topic_match_oneof(Topic, Filters) ->
|
||||||
lists:any(fun(Fltr) ->
|
lists:any(
|
||||||
|
fun(Fltr) ->
|
||||||
emqx_topic:match(Topic, Fltr)
|
emqx_topic:match(Topic, Fltr)
|
||||||
end, Filters).
|
end,
|
||||||
|
Filters
|
||||||
|
).
|
||||||
|
|
||||||
cluster_call(Module, Func, Args) ->
|
cluster_call(Module, Func, Args) ->
|
||||||
{ok, _TnxId, Result} = emqx_cluster_rpc:multicall(Module, Func, Args),
|
{ok, _TnxId, Result} = emqx_cluster_rpc:multicall(Module, Func, Args),
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
|
|
||||||
-behaviour(emqx_bpapi).
|
-behaviour(emqx_bpapi).
|
||||||
|
|
||||||
-export([ introduced_in/0
|
-export([
|
||||||
|
introduced_in/0,
|
||||||
|
|
||||||
, get_metrics/3
|
get_metrics/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("emqx/include/bpapi.hrl").
|
-include_lib("emqx/include/bpapi.hrl").
|
||||||
|
@ -28,9 +29,10 @@
|
||||||
introduced_in() ->
|
introduced_in() ->
|
||||||
"5.0.0".
|
"5.0.0".
|
||||||
|
|
||||||
-spec get_metrics( node()
|
-spec get_metrics(
|
||||||
, emqx_plugin_libs_metrics:handler_name()
|
node(),
|
||||||
, emqx_plugin_libs_metrics:metric_id()
|
emqx_plugin_libs_metrics:handler_name(),
|
||||||
|
emqx_plugin_libs_metrics:metric_id()
|
||||||
) -> emqx_plugin_libs_metrics:metrics() | {badrpc, _}.
|
) -> emqx_plugin_libs_metrics:metrics() | {badrpc, _}.
|
||||||
get_metrics(Node, HandlerName, MetricId) ->
|
get_metrics(Node, HandlerName, MetricId) ->
|
||||||
rpc:call(Node, emqx_plugin_libs_metrics, get_metrics, [HandlerName, MetricId]).
|
rpc:call(Node, emqx_plugin_libs_metrics, get_metrics, [HandlerName, MetricId]).
|
||||||
|
|
|
@ -23,54 +23,72 @@
|
||||||
|
|
||||||
all() -> emqx_common_test_helpers:all(?MODULE).
|
all() -> emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
|
|
||||||
t_proc_tmpl(_) ->
|
t_proc_tmpl(_) ->
|
||||||
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
||||||
Tks = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b},c:${c},d:${d}">>),
|
Tks = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b},c:${c},d:${d}">>),
|
||||||
?assertEqual(<<"a:1,b:1,c:1.0,d:{\"d1\":\"hi\"}">>,
|
?assertEqual(
|
||||||
emqx_placeholder:proc_tmpl(Tks, Selected)).
|
<<"a:1,b:1,c:1.0,d:{\"d1\":\"hi\"}">>,
|
||||||
|
emqx_placeholder:proc_tmpl(Tks, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_proc_tmpl_path(_) ->
|
t_proc_tmpl_path(_) ->
|
||||||
Selected = #{d => #{d1 => <<"hi">>}},
|
Selected = #{d => #{d1 => <<"hi">>}},
|
||||||
Tks = emqx_placeholder:preproc_tmpl(<<"d.d1:${d.d1}">>),
|
Tks = emqx_placeholder:preproc_tmpl(<<"d.d1:${d.d1}">>),
|
||||||
?assertEqual(<<"d.d1:hi">>,
|
?assertEqual(
|
||||||
emqx_placeholder:proc_tmpl(Tks, Selected)).
|
<<"d.d1:hi">>,
|
||||||
|
emqx_placeholder:proc_tmpl(Tks, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_proc_tmpl_custom_ph(_) ->
|
t_proc_tmpl_custom_ph(_) ->
|
||||||
Selected = #{a => <<"a">>, b => <<"b">>},
|
Selected = #{a => <<"a">>, b => <<"b">>},
|
||||||
Tks = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b}">>, #{placeholders => [<<"${a}">>]}),
|
Tks = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b}">>, #{placeholders => [<<"${a}">>]}),
|
||||||
?assertEqual(<<"a:a,b:${b}">>,
|
?assertEqual(
|
||||||
emqx_placeholder:proc_tmpl(Tks, Selected)).
|
<<"a:a,b:${b}">>,
|
||||||
|
emqx_placeholder:proc_tmpl(Tks, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_proc_tmpl1(_) ->
|
t_proc_tmpl1(_) ->
|
||||||
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
||||||
Tks = emqx_placeholder:preproc_tmpl(<<"a:$a,b:b},c:{c},d:${d">>),
|
Tks = emqx_placeholder:preproc_tmpl(<<"a:$a,b:b},c:{c},d:${d">>),
|
||||||
?assertEqual(<<"a:$a,b:b},c:{c},d:${d">>,
|
?assertEqual(
|
||||||
emqx_placeholder:proc_tmpl(Tks, Selected)).
|
<<"a:$a,b:b},c:{c},d:${d">>,
|
||||||
|
emqx_placeholder:proc_tmpl(Tks, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_proc_cmd(_) ->
|
t_proc_cmd(_) ->
|
||||||
Selected = #{v0 => <<"x">>, v1 => <<"1">>, v2 => #{d1 => <<"hi">>}},
|
Selected = #{v0 => <<"x">>, v1 => <<"1">>, v2 => #{d1 => <<"hi">>}},
|
||||||
Tks = emqx_placeholder:preproc_cmd(<<"hset name a:${v0} ${v1} b ${v2} ">>),
|
Tks = emqx_placeholder:preproc_cmd(<<"hset name a:${v0} ${v1} b ${v2} ">>),
|
||||||
?assertEqual([<<"hset">>, <<"name">>,
|
?assertEqual(
|
||||||
<<"a:x">>, <<"1">>,
|
[
|
||||||
<<"b">>, <<"{\"d1\":\"hi\"}">>],
|
<<"hset">>,
|
||||||
emqx_placeholder:proc_cmd(Tks, Selected)).
|
<<"name">>,
|
||||||
|
<<"a:x">>,
|
||||||
|
<<"1">>,
|
||||||
|
<<"b">>,
|
||||||
|
<<"{\"d1\":\"hi\"}">>
|
||||||
|
],
|
||||||
|
emqx_placeholder:proc_cmd(Tks, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_preproc_sql(_) ->
|
t_preproc_sql(_) ->
|
||||||
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
||||||
{PrepareStatement, ParamsTokens} =
|
{PrepareStatement, ParamsTokens} =
|
||||||
emqx_placeholder:preproc_sql(<<"a:${a},b:${b},c:${c},d:${d}">>, '?'),
|
emqx_placeholder:preproc_sql(<<"a:${a},b:${b},c:${c},d:${d}">>, '?'),
|
||||||
?assertEqual(<<"a:?,b:?,c:?,d:?">>, PrepareStatement),
|
?assertEqual(<<"a:?,b:?,c:?,d:?">>, PrepareStatement),
|
||||||
?assertEqual([<<"1">>,1,1.0,<<"{\"d1\":\"hi\"}">>],
|
?assertEqual(
|
||||||
emqx_placeholder:proc_sql(ParamsTokens, Selected)).
|
[<<"1">>, 1, 1.0, <<"{\"d1\":\"hi\"}">>],
|
||||||
|
emqx_placeholder:proc_sql(ParamsTokens, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_preproc_sql1(_) ->
|
t_preproc_sql1(_) ->
|
||||||
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
||||||
{PrepareStatement, ParamsTokens} =
|
{PrepareStatement, ParamsTokens} =
|
||||||
emqx_placeholder:preproc_sql(<<"a:${a},b:${b},c:${c},d:${d}">>, '$n'),
|
emqx_placeholder:preproc_sql(<<"a:${a},b:${b},c:${c},d:${d}">>, '$n'),
|
||||||
?assertEqual(<<"a:$1,b:$2,c:$3,d:$4">>, PrepareStatement),
|
?assertEqual(<<"a:$1,b:$2,c:$3,d:$4">>, PrepareStatement),
|
||||||
?assertEqual([<<"1">>,1,1.0,<<"{\"d1\":\"hi\"}">>],
|
?assertEqual(
|
||||||
emqx_placeholder:proc_sql(ParamsTokens, Selected)).
|
[<<"1">>, 1, 1.0, <<"{\"d1\":\"hi\"}">>],
|
||||||
|
emqx_placeholder:proc_sql(ParamsTokens, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_preproc_sql2(_) ->
|
t_preproc_sql2(_) ->
|
||||||
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
||||||
|
@ -82,49 +100,72 @@ t_preproc_sql2(_) ->
|
||||||
t_preproc_sql3(_) ->
|
t_preproc_sql3(_) ->
|
||||||
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
||||||
ParamsTokens = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b},c:${c},d:${d}">>),
|
ParamsTokens = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b},c:${c},d:${d}">>),
|
||||||
?assertEqual(<<"a:'1',b:1,c:1.0,d:'{\"d1\":\"hi\"}'">>,
|
?assertEqual(
|
||||||
emqx_placeholder:proc_sql_param_str(ParamsTokens, Selected)).
|
<<"a:'1',b:1,c:1.0,d:'{\"d1\":\"hi\"}'">>,
|
||||||
|
emqx_placeholder:proc_sql_param_str(ParamsTokens, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_preproc_sql4(_) ->
|
t_preproc_sql4(_) ->
|
||||||
%% with apostrophes
|
%% with apostrophes
|
||||||
%% https://github.com/emqx/emqx/issues/4135
|
%% https://github.com/emqx/emqx/issues/4135
|
||||||
Selected = #{a => <<"1''2">>, b => 1, c => 1.0,
|
Selected = #{
|
||||||
d => #{d1 => <<"someone's phone">>}},
|
a => <<"1''2">>,
|
||||||
|
b => 1,
|
||||||
|
c => 1.0,
|
||||||
|
d => #{d1 => <<"someone's phone">>}
|
||||||
|
},
|
||||||
ParamsTokens = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b},c:${c},d:${d}">>),
|
ParamsTokens = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b},c:${c},d:${d}">>),
|
||||||
?assertEqual(<<"a:'1\\'\\'2',b:1,c:1.0,d:'{\"d1\":\"someone\\'s phone\"}'">>,
|
?assertEqual(
|
||||||
emqx_placeholder:proc_sql_param_str(ParamsTokens, Selected)).
|
<<"a:'1\\'\\'2',b:1,c:1.0,d:'{\"d1\":\"someone\\'s phone\"}'">>,
|
||||||
|
emqx_placeholder:proc_sql_param_str(ParamsTokens, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_preproc_sql5(_) ->
|
t_preproc_sql5(_) ->
|
||||||
%% with apostrophes for cassandra
|
%% with apostrophes for cassandra
|
||||||
%% https://github.com/emqx/emqx/issues/4148
|
%% https://github.com/emqx/emqx/issues/4148
|
||||||
Selected = #{a => <<"1''2">>, b => 1, c => 1.0,
|
Selected = #{
|
||||||
d => #{d1 => <<"someone's phone">>}},
|
a => <<"1''2">>,
|
||||||
|
b => 1,
|
||||||
|
c => 1.0,
|
||||||
|
d => #{d1 => <<"someone's phone">>}
|
||||||
|
},
|
||||||
ParamsTokens = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b},c:${c},d:${d}">>),
|
ParamsTokens = emqx_placeholder:preproc_tmpl(<<"a:${a},b:${b},c:${c},d:${d}">>),
|
||||||
?assertEqual(<<"a:'1''''2',b:1,c:1.0,d:'{\"d1\":\"someone''s phone\"}'">>,
|
?assertEqual(
|
||||||
emqx_placeholder:proc_cql_param_str(ParamsTokens, Selected)).
|
<<"a:'1''''2',b:1,c:1.0,d:'{\"d1\":\"someone''s phone\"}'">>,
|
||||||
|
emqx_placeholder:proc_cql_param_str(ParamsTokens, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_preproc_sql6(_) ->
|
t_preproc_sql6(_) ->
|
||||||
Selected = #{a => <<"a">>, b => <<"b">>},
|
Selected = #{a => <<"a">>, b => <<"b">>},
|
||||||
{PrepareStatement, ParamsTokens} = emqx_placeholder:preproc_sql(
|
{PrepareStatement, ParamsTokens} = emqx_placeholder:preproc_sql(
|
||||||
<<"a:${a},b:${b}">>,
|
<<"a:${a},b:${b}">>,
|
||||||
#{replace_with => '$n',
|
#{
|
||||||
placeholders => [<<"${a}">>]}),
|
replace_with => '$n',
|
||||||
|
placeholders => [<<"${a}">>]
|
||||||
|
}
|
||||||
|
),
|
||||||
?assertEqual(<<"a:$1,b:${b}">>, PrepareStatement),
|
?assertEqual(<<"a:$1,b:${b}">>, PrepareStatement),
|
||||||
?assertEqual([<<"a">>],
|
?assertEqual(
|
||||||
emqx_placeholder:proc_sql(ParamsTokens, Selected)).
|
[<<"a">>],
|
||||||
|
emqx_placeholder:proc_sql(ParamsTokens, Selected)
|
||||||
|
).
|
||||||
|
|
||||||
t_preproc_tmpl_deep(_) ->
|
t_preproc_tmpl_deep(_) ->
|
||||||
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
||||||
|
|
||||||
Tmpl0 = emqx_placeholder:preproc_tmpl_deep(
|
Tmpl0 = emqx_placeholder:preproc_tmpl_deep(
|
||||||
#{<<"${a}">> => [<<"${b}">>, "c", 2, 3.0, '${d}', {[<<"${c}">>], 0}]}),
|
#{<<"${a}">> => [<<"${b}">>, "c", 2, 3.0, '${d}', {[<<"${c}">>], 0}]}
|
||||||
|
),
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
#{<<"1">> => [<<"1">>, "c", 2, 3.0, '${d}', {[<<"1.0">>], 0}]},
|
#{<<"1">> => [<<"1">>, "c", 2, 3.0, '${d}', {[<<"1.0">>], 0}]},
|
||||||
emqx_placeholder:proc_tmpl_deep(Tmpl0, Selected)),
|
emqx_placeholder:proc_tmpl_deep(Tmpl0, Selected)
|
||||||
|
),
|
||||||
|
|
||||||
Tmpl1 = emqx_placeholder:preproc_tmpl_deep(
|
Tmpl1 = emqx_placeholder:preproc_tmpl_deep(
|
||||||
#{<<"${a}">> => [<<"${b}">>, "c", 2, 3.0, '${d}', {[<<"${c}">>], 0}]},
|
#{<<"${a}">> => [<<"${b}">>, "c", 2, 3.0, '${d}', {[<<"${c}">>], 0}]},
|
||||||
#{process_keys => false}),
|
#{process_keys => false}
|
||||||
|
),
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
#{<<"${a}">> => [<<"1">>, "c", 2, 3.0, '${d}', {[<<"1.0">>], 0}]},
|
#{<<"${a}">> => [<<"1">>, "c", 2, 3.0, '${d}', {[<<"1.0">>], 0}]},
|
||||||
emqx_placeholder:proc_tmpl_deep(Tmpl1, Selected)).
|
emqx_placeholder:proc_tmpl_deep(Tmpl1, Selected)
|
||||||
|
).
|
||||||
|
|
|
@ -52,7 +52,8 @@ t_get_metrics(_) ->
|
||||||
Metrics = [a, b, c],
|
Metrics = [a, b, c],
|
||||||
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, Metrics),
|
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, Metrics),
|
||||||
%% all the metrics are set to zero at start
|
%% all the metrics are set to zero at start
|
||||||
?assertMatch(#{
|
?assertMatch(
|
||||||
|
#{
|
||||||
rate := #{
|
rate := #{
|
||||||
a := #{current := 0.0, max := 0.0, last5m := 0.0},
|
a := #{current := 0.0, max := 0.0, last5m := 0.0},
|
||||||
b := #{current := 0.0, max := 0.0, last5m := 0.0},
|
b := #{current := 0.0, max := 0.0, last5m := 0.0},
|
||||||
|
@ -63,13 +64,16 @@ t_get_metrics(_) ->
|
||||||
b := 0,
|
b := 0,
|
||||||
c := 0
|
c := 0
|
||||||
}
|
}
|
||||||
}, emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)),
|
},
|
||||||
|
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)
|
||||||
|
),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, a),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, a),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, b),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, b),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
||||||
ct:sleep(1500),
|
ct:sleep(1500),
|
||||||
?LET(#{
|
?LET(
|
||||||
|
#{
|
||||||
rate := #{
|
rate := #{
|
||||||
a := #{current := CurrA, max := MaxA, last5m := _},
|
a := #{current := CurrA, max := MaxA, last5m := _},
|
||||||
b := #{current := CurrB, max := MaxB, last5m := _},
|
b := #{current := CurrB, max := MaxB, last5m := _},
|
||||||
|
@ -80,16 +84,25 @@ t_get_metrics(_) ->
|
||||||
b := 1,
|
b := 1,
|
||||||
c := 2
|
c := 2
|
||||||
}
|
}
|
||||||
}, emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>),
|
},
|
||||||
{?assert(CurrA > 0), ?assert(CurrB > 0), ?assert(CurrC > 0),
|
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>),
|
||||||
?assert(MaxA > 0), ?assert(MaxB > 0), ?assert(MaxC > 0)}),
|
{
|
||||||
|
?assert(CurrA > 0),
|
||||||
|
?assert(CurrB > 0),
|
||||||
|
?assert(CurrC > 0),
|
||||||
|
?assert(MaxA > 0),
|
||||||
|
?assert(MaxB > 0),
|
||||||
|
?assert(MaxC > 0)
|
||||||
|
}
|
||||||
|
),
|
||||||
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"testid">>).
|
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"testid">>).
|
||||||
|
|
||||||
t_reset_metrics(_) ->
|
t_reset_metrics(_) ->
|
||||||
Metrics = [a, b, c],
|
Metrics = [a, b, c],
|
||||||
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, Metrics),
|
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, Metrics),
|
||||||
%% all the metrics are set to zero at start
|
%% all the metrics are set to zero at start
|
||||||
?assertMatch(#{
|
?assertMatch(
|
||||||
|
#{
|
||||||
rate := #{
|
rate := #{
|
||||||
a := #{current := 0.0, max := 0.0, last5m := 0.0},
|
a := #{current := 0.0, max := 0.0, last5m := 0.0},
|
||||||
b := #{current := 0.0, max := 0.0, last5m := 0.0},
|
b := #{current := 0.0, max := 0.0, last5m := 0.0},
|
||||||
|
@ -100,14 +113,17 @@ t_reset_metrics(_) ->
|
||||||
b := 0,
|
b := 0,
|
||||||
c := 0
|
c := 0
|
||||||
}
|
}
|
||||||
}, emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)),
|
},
|
||||||
|
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)
|
||||||
|
),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, a),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, a),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, b),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, b),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
||||||
ct:sleep(1500),
|
ct:sleep(1500),
|
||||||
ok = emqx_plugin_libs_metrics:reset_metrics(?NAME, <<"testid">>),
|
ok = emqx_plugin_libs_metrics:reset_metrics(?NAME, <<"testid">>),
|
||||||
?LET(#{
|
?LET(
|
||||||
|
#{
|
||||||
rate := #{
|
rate := #{
|
||||||
a := #{current := CurrA, max := MaxA, last5m := _},
|
a := #{current := CurrA, max := MaxA, last5m := _},
|
||||||
b := #{current := CurrB, max := MaxB, last5m := _},
|
b := #{current := CurrB, max := MaxB, last5m := _},
|
||||||
|
@ -118,19 +134,32 @@ t_reset_metrics(_) ->
|
||||||
b := 0,
|
b := 0,
|
||||||
c := 0
|
c := 0
|
||||||
}
|
}
|
||||||
}, emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>),
|
},
|
||||||
{?assert(CurrA == 0), ?assert(CurrB == 0), ?assert(CurrC == 0),
|
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>),
|
||||||
?assert(MaxA == 0), ?assert(MaxB == 0), ?assert(MaxC == 0)}),
|
{
|
||||||
|
?assert(CurrA == 0),
|
||||||
|
?assert(CurrB == 0),
|
||||||
|
?assert(CurrC == 0),
|
||||||
|
?assert(MaxA == 0),
|
||||||
|
?assert(MaxB == 0),
|
||||||
|
?assert(MaxC == 0)
|
||||||
|
}
|
||||||
|
),
|
||||||
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"testid">>).
|
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"testid">>).
|
||||||
|
|
||||||
t_get_metrics_2(_) ->
|
t_get_metrics_2(_) ->
|
||||||
Metrics = [a, b, c],
|
Metrics = [a, b, c],
|
||||||
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, Metrics,
|
ok = emqx_plugin_libs_metrics:create_metrics(
|
||||||
[a]),
|
?NAME,
|
||||||
|
<<"testid">>,
|
||||||
|
Metrics,
|
||||||
|
[a]
|
||||||
|
),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, a),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, a),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, b),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, b),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
||||||
?assertMatch(#{
|
?assertMatch(
|
||||||
|
#{
|
||||||
rate := Rate = #{
|
rate := Rate = #{
|
||||||
a := #{current := _, max := _, last5m := _}
|
a := #{current := _, max := _, last5m := _}
|
||||||
},
|
},
|
||||||
|
@ -139,13 +168,16 @@ t_get_metrics_2(_) ->
|
||||||
b := 1,
|
b := 1,
|
||||||
c := 1
|
c := 1
|
||||||
}
|
}
|
||||||
} when map_size(Rate) =:= 1, emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)),
|
} when map_size(Rate) =:= 1,
|
||||||
|
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)
|
||||||
|
),
|
||||||
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"testid">>).
|
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"testid">>).
|
||||||
|
|
||||||
t_recreate_metrics(_) ->
|
t_recreate_metrics(_) ->
|
||||||
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, [a]),
|
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, [a]),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, a),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, a),
|
||||||
?assertMatch(#{
|
?assertMatch(
|
||||||
|
#{
|
||||||
rate := R = #{
|
rate := R = #{
|
||||||
a := #{current := _, max := _, last5m := _}
|
a := #{current := _, max := _, last5m := _}
|
||||||
},
|
},
|
||||||
|
@ -153,12 +185,14 @@ t_recreate_metrics(_) ->
|
||||||
a := 1
|
a := 1
|
||||||
}
|
}
|
||||||
} when map_size(R) == 1 andalso map_size(C) == 1,
|
} when map_size(R) == 1 andalso map_size(C) == 1,
|
||||||
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)),
|
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)
|
||||||
|
),
|
||||||
%% we create the metrics again, to add some counters
|
%% we create the metrics again, to add some counters
|
||||||
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, [a, b, c]),
|
ok = emqx_plugin_libs_metrics:create_metrics(?NAME, <<"testid">>, [a, b, c]),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, b),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, b),
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"testid">>, c),
|
||||||
?assertMatch(#{
|
?assertMatch(
|
||||||
|
#{
|
||||||
rate := R = #{
|
rate := R = #{
|
||||||
a := #{current := _, max := _, last5m := _},
|
a := #{current := _, max := _, last5m := _},
|
||||||
b := #{current := _, max := _, last5m := _},
|
b := #{current := _, max := _, last5m := _},
|
||||||
|
@ -168,7 +202,8 @@ t_recreate_metrics(_) ->
|
||||||
a := 1, b := 1, c := 1
|
a := 1, b := 1, c := 1
|
||||||
}
|
}
|
||||||
} when map_size(R) == 3 andalso map_size(C) == 3,
|
} when map_size(R) == 3 andalso map_size(C) == 3,
|
||||||
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)),
|
emqx_plugin_libs_metrics:get_metrics(?NAME, <<"testid">>)
|
||||||
|
),
|
||||||
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"testid">>).
|
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"testid">>).
|
||||||
|
|
||||||
t_inc_matched(_) ->
|
t_inc_matched(_) ->
|
||||||
|
@ -192,15 +227,17 @@ t_rate(_) ->
|
||||||
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"rule:2">>, 'rules.matched'),
|
ok = emqx_plugin_libs_metrics:inc(?NAME, <<"rule:2">>, 'rules.matched'),
|
||||||
?assertEqual(2, emqx_plugin_libs_metrics:get(?NAME, <<"rule1">>, 'rules.matched')),
|
?assertEqual(2, emqx_plugin_libs_metrics:get(?NAME, <<"rule1">>, 'rules.matched')),
|
||||||
ct:sleep(1000),
|
ct:sleep(1000),
|
||||||
?LET(#{'rules.matched' := #{max := Max, current := Current}},
|
?LET(
|
||||||
|
#{'rules.matched' := #{max := Max, current := Current}},
|
||||||
emqx_plugin_libs_metrics:get_rate(?NAME, <<"rule1">>),
|
emqx_plugin_libs_metrics:get_rate(?NAME, <<"rule1">>),
|
||||||
{?assert(Max =< 2),
|
{?assert(Max =< 2), ?assert(Current =< 2)}
|
||||||
?assert(Current =< 2)}),
|
),
|
||||||
ct:sleep(2100),
|
ct:sleep(2100),
|
||||||
?LET(#{'rules.matched' := #{max := Max, current := Current, last5m := Last5Min}}, emqx_plugin_libs_metrics:get_rate(?NAME, <<"rule1">>),
|
?LET(
|
||||||
{?assert(Max =< 2),
|
#{'rules.matched' := #{max := Max, current := Current, last5m := Last5Min}},
|
||||||
?assert(Current == 0),
|
emqx_plugin_libs_metrics:get_rate(?NAME, <<"rule1">>),
|
||||||
?assert(Last5Min =< 0.67)}),
|
{?assert(Max =< 2), ?assert(Current == 0), ?assert(Last5Min =< 0.67)}
|
||||||
|
),
|
||||||
ct:sleep(3000),
|
ct:sleep(3000),
|
||||||
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"rule1">>),
|
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"rule1">>),
|
||||||
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"rule:2">>).
|
ok = emqx_plugin_libs_metrics:clear_metrics(?NAME, <<"rule:2">>).
|
||||||
|
|
|
@ -28,10 +28,12 @@ all() -> emqx_common_test_helpers:all(?MODULE).
|
||||||
t_http_connectivity(_) ->
|
t_http_connectivity(_) ->
|
||||||
{ok, Socket} = gen_tcp:listen(?PORT, []),
|
{ok, Socket} = gen_tcp:listen(?PORT, []),
|
||||||
ok = emqx_plugin_libs_rule:http_connectivity(
|
ok = emqx_plugin_libs_rule:http_connectivity(
|
||||||
"http://127.0.0.1:"++emqx_plugin_libs_rule:str(?PORT), 1000),
|
"http://127.0.0.1:" ++ emqx_plugin_libs_rule:str(?PORT), 1000
|
||||||
|
),
|
||||||
gen_tcp:close(Socket),
|
gen_tcp:close(Socket),
|
||||||
{error, _} = emqx_plugin_libs_rule:http_connectivity(
|
{error, _} = emqx_plugin_libs_rule:http_connectivity(
|
||||||
"http://127.0.0.1:"++emqx_plugin_libs_rule:str(?PORT), 1000).
|
"http://127.0.0.1:" ++ emqx_plugin_libs_rule:str(?PORT), 1000
|
||||||
|
).
|
||||||
|
|
||||||
t_tcp_connectivity(_) ->
|
t_tcp_connectivity(_) ->
|
||||||
{ok, Socket} = gen_tcp:listen(?PORT, []),
|
{ok, Socket} = gen_tcp:listen(?PORT, []),
|
||||||
|
@ -59,7 +61,8 @@ t_bin(_) ->
|
||||||
?assertError(_, emqx_plugin_libs_rule:bin({a, v})).
|
?assertError(_, emqx_plugin_libs_rule:bin({a, v})).
|
||||||
|
|
||||||
t_atom_key(_) ->
|
t_atom_key(_) ->
|
||||||
_ = erlang, _ = port,
|
_ = erlang,
|
||||||
|
_ = port,
|
||||||
?assertEqual([erlang], emqx_plugin_libs_rule:atom_key([<<"erlang">>])),
|
?assertEqual([erlang], emqx_plugin_libs_rule:atom_key([<<"erlang">>])),
|
||||||
?assertEqual([erlang, port], emqx_plugin_libs_rule:atom_key([<<"erlang">>, port])),
|
?assertEqual([erlang, port], emqx_plugin_libs_rule:atom_key([<<"erlang">>, port])),
|
||||||
?assertEqual([erlang, port], emqx_plugin_libs_rule:atom_key([<<"erlang">>, <<"port">>])),
|
?assertEqual([erlang, port], emqx_plugin_libs_rule:atom_key([<<"erlang">>, <<"port">>])),
|
||||||
|
@ -70,8 +73,12 @@ t_atom_key(_) ->
|
||||||
|
|
||||||
t_unsafe_atom_key(_) ->
|
t_unsafe_atom_key(_) ->
|
||||||
?assertEqual([xyz876gv], emqx_plugin_libs_rule:unsafe_atom_key([<<"xyz876gv">>])),
|
?assertEqual([xyz876gv], emqx_plugin_libs_rule:unsafe_atom_key([<<"xyz876gv">>])),
|
||||||
?assertEqual([xyz876gv33, port],
|
?assertEqual(
|
||||||
emqx_plugin_libs_rule:unsafe_atom_key([<<"xyz876gv33">>, port])),
|
[xyz876gv33, port],
|
||||||
?assertEqual([xyz876gv331, port1221],
|
emqx_plugin_libs_rule:unsafe_atom_key([<<"xyz876gv33">>, port])
|
||||||
emqx_plugin_libs_rule:unsafe_atom_key([<<"xyz876gv331">>, <<"port1221">>])),
|
),
|
||||||
|
?assertEqual(
|
||||||
|
[xyz876gv331, port1221],
|
||||||
|
emqx_plugin_libs_rule:unsafe_atom_key([<<"xyz876gv331">>, <<"port1221">>])
|
||||||
|
),
|
||||||
?assertEqual(xyz876gv3312, emqx_plugin_libs_rule:unsafe_atom_key(<<"xyz876gv3312">>)).
|
?assertEqual(xyz876gv3312, emqx_plugin_libs_rule:unsafe_atom_key(<<"xyz876gv3312">>)).
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
{deps, [ {emqx, {path, "../emqx"}}
|
{deps, [
|
||||||
, {estatsd, {git, "https://github.com/emqx/estatsd", {tag, "0.1.0"}}}
|
{emqx, {path, "../emqx"}},
|
||||||
|
{estatsd, {git, "https://github.com/emqx/estatsd", {tag, "0.1.0"}}}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{shell, [
|
{shell, [
|
||||||
% {config, "config/sys.config"},
|
% {config, "config/sys.config"},
|
||||||
{apps, [emqx_statsd]}
|
{apps, [emqx_statsd]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
|
{project_plugins, [erlfmt]}.
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_statsd,
|
{application, emqx_statsd, [
|
||||||
[{description, "An OTP application"},
|
{description, "An OTP application"},
|
||||||
{vsn, "5.0.0"},
|
{vsn, "5.0.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_statsd_app, []}},
|
{mod, {emqx_statsd_app, []}},
|
||||||
{applications,
|
{applications, [
|
||||||
[kernel,
|
kernel,
|
||||||
stdlib,
|
stdlib,
|
||||||
estatsd,
|
estatsd,
|
||||||
emqx
|
emqx
|
||||||
|
|
|
@ -27,26 +27,28 @@
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
-export([ update/1
|
-export([
|
||||||
, start/0
|
update/1,
|
||||||
, stop/0
|
start/0,
|
||||||
, restart/0
|
stop/0,
|
||||||
|
restart/0,
|
||||||
%% for rpc
|
%% for rpc
|
||||||
, do_start/0
|
do_start/0,
|
||||||
, do_stop/0
|
do_stop/0,
|
||||||
, do_restart/0
|
do_restart/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% Interface
|
%% Interface
|
||||||
-export([start_link/1]).
|
-export([start_link/1]).
|
||||||
|
|
||||||
%% Internal Exports
|
%% Internal Exports
|
||||||
-export([ init/1
|
-export([
|
||||||
, handle_call/3
|
init/1,
|
||||||
, handle_cast/2
|
handle_call/3,
|
||||||
, handle_info/2
|
handle_cast/2,
|
||||||
, code_change/3
|
handle_info/2,
|
||||||
, terminate/2
|
code_change/3,
|
||||||
|
terminate/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
|
@ -57,9 +59,13 @@
|
||||||
}).
|
}).
|
||||||
|
|
||||||
update(Config) ->
|
update(Config) ->
|
||||||
case emqx_conf:update([statsd],
|
case
|
||||||
|
emqx_conf:update(
|
||||||
|
[statsd],
|
||||||
Config,
|
Config,
|
||||||
#{rawconf_with_defaults => true, override_to => cluster}) of
|
#{rawconf_with_defaults => true, override_to => cluster}
|
||||||
|
)
|
||||||
|
of
|
||||||
{ok, #{raw_config := NewConfigRows}} ->
|
{ok, #{raw_config := NewConfigRows}} ->
|
||||||
ok = stop(),
|
ok = stop(),
|
||||||
case maps:get(<<"enable">>, Config, true) of
|
case maps:get(<<"enable">>, Config, true) of
|
||||||
|
@ -95,17 +101,27 @@ init([Opts]) ->
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
Tags = tags(maps:get(tags, Opts, #{})),
|
Tags = tags(maps:get(tags, Opts, #{})),
|
||||||
{Host, Port} = maps:get(server, Opts, {?DEFAULT_HOST, ?DEFAULT_PORT}),
|
{Host, Port} = maps:get(server, Opts, {?DEFAULT_HOST, ?DEFAULT_PORT}),
|
||||||
Opts1 = maps:without([sample_time_interval,
|
Opts1 = maps:without(
|
||||||
flush_time_interval], Opts#{tags => Tags,
|
[
|
||||||
|
sample_time_interval,
|
||||||
|
flush_time_interval
|
||||||
|
],
|
||||||
|
Opts#{
|
||||||
|
tags => Tags,
|
||||||
host => Host,
|
host => Host,
|
||||||
port => Port,
|
port => Port,
|
||||||
prefix => <<"emqx">>}),
|
prefix => <<"emqx">>
|
||||||
|
}
|
||||||
|
),
|
||||||
{ok, Pid} = estatsd:start_link(maps:to_list(Opts1)),
|
{ok, Pid} = estatsd:start_link(maps:to_list(Opts1)),
|
||||||
SampleTimeInterval = maps:get(sample_time_interval, Opts, ?DEFAULT_FLUSH_TIME_INTERVAL),
|
SampleTimeInterval = maps:get(sample_time_interval, Opts, ?DEFAULT_FLUSH_TIME_INTERVAL),
|
||||||
FlushTimeInterval = maps:get(flush_time_interval, Opts, ?DEFAULT_FLUSH_TIME_INTERVAL),
|
FlushTimeInterval = maps:get(flush_time_interval, Opts, ?DEFAULT_FLUSH_TIME_INTERVAL),
|
||||||
{ok, ensure_timer(#state{sample_time_interval = SampleTimeInterval,
|
{ok,
|
||||||
|
ensure_timer(#state{
|
||||||
|
sample_time_interval = SampleTimeInterval,
|
||||||
flush_time_interval = FlushTimeInterval,
|
flush_time_interval = FlushTimeInterval,
|
||||||
estatsd_pid = Pid})}.
|
estatsd_pid = Pid
|
||||||
|
})}.
|
||||||
|
|
||||||
handle_call(_Req, _From, State) ->
|
handle_call(_Req, _From, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
@ -113,20 +129,25 @@ handle_call(_Req, _From, State) ->
|
||||||
handle_cast(_Msg, State) ->
|
handle_cast(_Msg, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
handle_info({timeout, Ref, sample_timeout},
|
handle_info(
|
||||||
State = #state{sample_time_interval = SampleTimeInterval,
|
{timeout, Ref, sample_timeout},
|
||||||
|
State = #state{
|
||||||
|
sample_time_interval = SampleTimeInterval,
|
||||||
flush_time_interval = FlushTimeInterval,
|
flush_time_interval = FlushTimeInterval,
|
||||||
estatsd_pid = Pid,
|
estatsd_pid = Pid,
|
||||||
timer = Ref}) ->
|
timer = Ref
|
||||||
|
}
|
||||||
|
) ->
|
||||||
Metrics = emqx_metrics:all() ++ emqx_stats:getstats() ++ emqx_vm_data(),
|
Metrics = emqx_metrics:all() ++ emqx_stats:getstats() ++ emqx_vm_data(),
|
||||||
SampleRate = SampleTimeInterval / FlushTimeInterval,
|
SampleRate = SampleTimeInterval / FlushTimeInterval,
|
||||||
StatsdMetrics = [{gauge, trans_metrics_name(Name), Value, SampleRate, []} || {Name, Value} <- Metrics],
|
StatsdMetrics = [
|
||||||
|
{gauge, trans_metrics_name(Name), Value, SampleRate, []}
|
||||||
|
|| {Name, Value} <- Metrics
|
||||||
|
],
|
||||||
estatsd:submit(Pid, StatsdMetrics),
|
estatsd:submit(Pid, StatsdMetrics),
|
||||||
{noreply, ensure_timer(State)};
|
{noreply, ensure_timer(State)};
|
||||||
|
|
||||||
handle_info({'EXIT', Pid, Error}, State = #state{estatsd_pid = Pid}) ->
|
handle_info({'EXIT', Pid, Error}, State = #state{estatsd_pid = Pid}) ->
|
||||||
{stop, {shutdown, Error}, State};
|
{stop, {shutdown, Error}, State};
|
||||||
|
|
||||||
handle_info(_Msg, State) ->
|
handle_info(_Msg, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
|
@ -145,25 +166,36 @@ trans_metrics_name(Name) ->
|
||||||
binary_to_atom(<<"emqx.", Name0/binary>>, utf8).
|
binary_to_atom(<<"emqx.", Name0/binary>>, utf8).
|
||||||
|
|
||||||
emqx_vm_data() ->
|
emqx_vm_data() ->
|
||||||
Idle = case cpu_sup:util([detailed]) of
|
Idle =
|
||||||
{_, 0, 0, _} -> 0; %% Not support for Windows
|
case cpu_sup:util([detailed]) of
|
||||||
|
%% Not support for Windows
|
||||||
|
{_, 0, 0, _} -> 0;
|
||||||
{_Num, _Use, IdleList, _} -> proplists:get_value(idle, IdleList, 0)
|
{_Num, _Use, IdleList, _} -> proplists:get_value(idle, IdleList, 0)
|
||||||
end,
|
end,
|
||||||
RunQueue = erlang:statistics(run_queue),
|
RunQueue = erlang:statistics(run_queue),
|
||||||
[{run_queue, RunQueue},
|
[
|
||||||
|
{run_queue, RunQueue},
|
||||||
{cpu_idle, Idle},
|
{cpu_idle, Idle},
|
||||||
{cpu_use, 100 - Idle}] ++ emqx_vm:mem_info().
|
{cpu_use, 100 - Idle}
|
||||||
|
] ++ emqx_vm:mem_info().
|
||||||
|
|
||||||
tags(Map) ->
|
tags(Map) ->
|
||||||
Tags = maps:to_list(Map),
|
Tags = maps:to_list(Map),
|
||||||
[{atom_to_binary(Key, utf8), Value} || {Key, Value} <- Tags].
|
[{atom_to_binary(Key, utf8), Value} || {Key, Value} <- Tags].
|
||||||
|
|
||||||
|
|
||||||
ensure_timer(State = #state{sample_time_interval = SampleTimeInterval}) ->
|
ensure_timer(State = #state{sample_time_interval = SampleTimeInterval}) ->
|
||||||
State#state{timer = emqx_misc:start_timer(SampleTimeInterval, sample_timeout)}.
|
State#state{timer = emqx_misc:start_timer(SampleTimeInterval, sample_timeout)}.
|
||||||
|
|
||||||
check_multicall_result({Results, []}) ->
|
check_multicall_result({Results, []}) ->
|
||||||
case lists:all(fun(ok) -> true; (_) -> false end, Results) of
|
case
|
||||||
|
lists:all(
|
||||||
|
fun
|
||||||
|
(ok) -> true;
|
||||||
|
(_) -> false
|
||||||
|
end,
|
||||||
|
Results
|
||||||
|
)
|
||||||
|
of
|
||||||
true -> ok;
|
true -> ok;
|
||||||
false -> error({bad_result, Results})
|
false -> error({bad_result, Results})
|
||||||
end;
|
end;
|
||||||
|
|
|
@ -26,9 +26,10 @@
|
||||||
|
|
||||||
-export([statsd/2]).
|
-export([statsd/2]).
|
||||||
|
|
||||||
-export([ api_spec/0
|
-export([
|
||||||
, paths/0
|
api_spec/0,
|
||||||
, schema/1
|
paths/0,
|
||||||
|
schema/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(API_TAG_STATSD, [<<"statsd">>]).
|
-define(API_TAG_STATSD, [<<"statsd">>]).
|
||||||
|
@ -36,7 +37,6 @@
|
||||||
|
|
||||||
-define(INTERNAL_ERROR, 'INTERNAL_ERROR').
|
-define(INTERNAL_ERROR, 'INTERNAL_ERROR').
|
||||||
|
|
||||||
|
|
||||||
api_spec() ->
|
api_spec() ->
|
||||||
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
||||||
|
|
||||||
|
@ -44,18 +44,21 @@ paths() ->
|
||||||
["/statsd"].
|
["/statsd"].
|
||||||
|
|
||||||
schema("/statsd") ->
|
schema("/statsd") ->
|
||||||
#{ 'operationId' => statsd
|
#{
|
||||||
, get =>
|
'operationId' => statsd,
|
||||||
#{ description => <<"Get statsd config">>
|
get =>
|
||||||
, tags => ?API_TAG_STATSD
|
#{
|
||||||
, responses =>
|
description => <<"Get statsd config">>,
|
||||||
|
tags => ?API_TAG_STATSD,
|
||||||
|
responses =>
|
||||||
#{200 => statsd_config_schema()}
|
#{200 => statsd_config_schema()}
|
||||||
}
|
},
|
||||||
, put =>
|
put =>
|
||||||
#{ description => <<"Set statsd config">>
|
#{
|
||||||
, tags => ?API_TAG_STATSD
|
description => <<"Set statsd config">>,
|
||||||
, 'requestBody' => statsd_config_schema()
|
tags => ?API_TAG_STATSD,
|
||||||
, responses =>
|
'requestBody' => statsd_config_schema(),
|
||||||
|
responses =>
|
||||||
#{200 => statsd_config_schema()}
|
#{200 => statsd_config_schema()}
|
||||||
}
|
}
|
||||||
}.
|
}.
|
||||||
|
@ -67,18 +70,19 @@ schema("/statsd") ->
|
||||||
statsd_config_schema() ->
|
statsd_config_schema() ->
|
||||||
emqx_dashboard_swagger:schema_with_example(
|
emqx_dashboard_swagger:schema_with_example(
|
||||||
ref(?SCHEMA_MODULE, "statsd"),
|
ref(?SCHEMA_MODULE, "statsd"),
|
||||||
statsd_example()).
|
statsd_example()
|
||||||
|
).
|
||||||
|
|
||||||
statsd_example() ->
|
statsd_example() ->
|
||||||
#{ enable => true
|
#{
|
||||||
, flush_time_interval => "32s"
|
enable => true,
|
||||||
, sample_time_interval => "32s"
|
flush_time_interval => "32s",
|
||||||
, server => "127.0.0.1:8125"
|
sample_time_interval => "32s",
|
||||||
|
server => "127.0.0.1:8125"
|
||||||
}.
|
}.
|
||||||
|
|
||||||
statsd(get, _Params) ->
|
statsd(get, _Params) ->
|
||||||
{200, emqx:get_raw_config([<<"statsd">>], #{})};
|
{200, emqx:get_raw_config([<<"statsd">>], #{})};
|
||||||
|
|
||||||
statsd(put, #{body := Body}) ->
|
statsd(put, #{body := Body}) ->
|
||||||
case emqx_statsd:update(Body) of
|
case emqx_statsd:update(Body) of
|
||||||
{ok, NewConfig} ->
|
{ok, NewConfig} ->
|
||||||
|
|
|
@ -20,8 +20,9 @@
|
||||||
|
|
||||||
-include("emqx_statsd.hrl").
|
-include("emqx_statsd.hrl").
|
||||||
|
|
||||||
-export([ start/2
|
-export([
|
||||||
, stop/1
|
start/2,
|
||||||
|
stop/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
start(_StartType, _StartArgs) ->
|
start(_StartType, _StartArgs) ->
|
||||||
|
|
|
@ -23,10 +23,12 @@
|
||||||
|
|
||||||
-export([to_ip_port/1]).
|
-export([to_ip_port/1]).
|
||||||
|
|
||||||
-export([ namespace/0
|
-export([
|
||||||
, roots/0
|
namespace/0,
|
||||||
, fields/1
|
roots/0,
|
||||||
, desc/1]).
|
fields/1,
|
||||||
|
desc/1
|
||||||
|
]).
|
||||||
|
|
||||||
-typerefl_from_string({ip_port/0, emqx_statsd_schema, to_ip_port}).
|
-typerefl_from_string({ip_port/0, emqx_statsd_schema, to_ip_port}).
|
||||||
|
|
||||||
|
@ -35,19 +37,23 @@ namespace() -> "statsd".
|
||||||
roots() -> ["statsd"].
|
roots() -> ["statsd"].
|
||||||
|
|
||||||
fields("statsd") ->
|
fields("statsd") ->
|
||||||
[ {enable, hoconsc:mk(boolean(),
|
[
|
||||||
#{ default => false
|
{enable,
|
||||||
, required => true
|
hoconsc:mk(
|
||||||
, desc => ?DESC(enable)
|
boolean(),
|
||||||
})}
|
#{
|
||||||
, {server, fun server/1}
|
default => false,
|
||||||
, {sample_time_interval, fun sample_interval/1}
|
required => true,
|
||||||
, {flush_time_interval, fun flush_interval/1}
|
desc => ?DESC(enable)
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{server, fun server/1},
|
||||||
|
{sample_time_interval, fun sample_interval/1},
|
||||||
|
{flush_time_interval, fun flush_interval/1}
|
||||||
].
|
].
|
||||||
|
|
||||||
desc("statsd") -> ?DESC(statsd);
|
desc("statsd") -> ?DESC(statsd);
|
||||||
desc(_) ->
|
desc(_) -> undefined.
|
||||||
undefined.
|
|
||||||
|
|
||||||
server(type) -> emqx_schema:ip_port();
|
server(type) -> emqx_schema:ip_port();
|
||||||
server(required) -> true;
|
server(required) -> true;
|
||||||
|
@ -74,5 +80,6 @@ to_ip_port(Str) ->
|
||||||
{ok, R} -> {ok, {R, list_to_integer(Port)}};
|
{ok, R} -> {ok, {R, list_to_integer(Port)}};
|
||||||
_ -> {error, Str}
|
_ -> {error, Str}
|
||||||
end;
|
end;
|
||||||
_ -> {error, Str}
|
_ ->
|
||||||
|
{error, Str}
|
||||||
end.
|
end.
|
||||||
|
|
|
@ -7,21 +7,24 @@
|
||||||
|
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
|
|
||||||
-export([ start_link/0
|
-export([
|
||||||
, ensure_child_started/1
|
start_link/0,
|
||||||
, ensure_child_started/2
|
ensure_child_started/1,
|
||||||
, ensure_child_stopped/1
|
ensure_child_started/2,
|
||||||
|
ensure_child_stopped/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([init/1]).
|
-export([init/1]).
|
||||||
|
|
||||||
%% Helper macro for declaring children of supervisor
|
%% Helper macro for declaring children of supervisor
|
||||||
-define(CHILD(Mod, Opts), #{id => Mod,
|
-define(CHILD(Mod, Opts), #{
|
||||||
|
id => Mod,
|
||||||
start => {Mod, start_link, [Opts]},
|
start => {Mod, start_link, [Opts]},
|
||||||
restart => permanent,
|
restart => permanent,
|
||||||
shutdown => 5000,
|
shutdown => 5000,
|
||||||
type => worker,
|
type => worker,
|
||||||
modules => [Mod]}).
|
modules => [Mod]
|
||||||
|
}).
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
|
|
||||||
-behaviour(emqx_bpapi).
|
-behaviour(emqx_bpapi).
|
||||||
|
|
||||||
-export([ introduced_in/0
|
-export([
|
||||||
|
introduced_in/0,
|
||||||
|
|
||||||
, start/1
|
start/1,
|
||||||
, stop/1
|
stop/1,
|
||||||
, restart/1
|
restart/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("emqx/include/bpapi.hrl").
|
-include_lib("emqx/include/bpapi.hrl").
|
||||||
|
|
|
@ -21,8 +21,7 @@ t_statsd(_) ->
|
||||||
receive
|
receive
|
||||||
{udp, _Socket, _Host, _Port, Bin} ->
|
{udp, _Socket, _Host, _Port, Bin} ->
|
||||||
?assert(length(Bin) > 50)
|
?assert(length(Bin) > 50)
|
||||||
after
|
after 11 * 1000 ->
|
||||||
11*1000 ->
|
|
||||||
?assert(true, failed)
|
?assert(true, failed)
|
||||||
end,
|
end,
|
||||||
gen_udp:close(Socket).
|
gen_udp:close(Socket).
|
||||||
|
|
|
@ -15,6 +15,7 @@ APPS+=( 'apps/emqx_exhook')
|
||||||
APPS+=( 'apps/emqx_retainer' 'apps/emqx_slow_subs')
|
APPS+=( 'apps/emqx_retainer' 'apps/emqx_slow_subs')
|
||||||
APPS+=( 'apps/emqx_management')
|
APPS+=( 'apps/emqx_management')
|
||||||
APPS+=( 'apps/emqx_psk')
|
APPS+=( 'apps/emqx_psk')
|
||||||
|
APPS+=( 'apps/emqx_plugin_libs' 'apps/emqx_machine' 'apps/emqx_statsd' )
|
||||||
|
|
||||||
for app in "${APPS[@]}"; do
|
for app in "${APPS[@]}"; do
|
||||||
echo "$app ..."
|
echo "$app ..."
|
||||||
|
|
Loading…
Reference in New Issue