From 2a7bd74ef1b7509f0f670b324488b5f2d81a682e Mon Sep 17 00:00:00 2001 From: EMQ-YangM Date: Thu, 28 Apr 2022 11:08:44 +0800 Subject: [PATCH] feat: authn add new metrics --- apps/emqx/src/emqx_authentication.erl | 12 +++ .../i18n/emqx_authn_schema_i18n.conf | 11 +++ apps/emqx_authn/src/emqx_authn_api.erl | 79 ++++++++++--------- apps/emqx_authn/src/emqx_authn_schema.erl | 12 +++ apps/emqx_authn/src/emqx_authn_sup.erl | 3 +- apps/emqx_authn/test/emqx_authn_api_SUITE.erl | 68 +++++++++++++++- 6 files changed, 142 insertions(+), 43 deletions(-) diff --git a/apps/emqx/src/emqx_authentication.erl b/apps/emqx/src/emqx_authentication.erl index 92bb66dfd..0b5948955 100644 --- a/apps/emqx/src/emqx_authentication.erl +++ b/apps/emqx/src/emqx_authentication.erl @@ -609,6 +609,9 @@ handle_create_authenticator(Chain, Config, Providers) -> ?CHAINS_TAB, Chain#chain{authenticators = NAuthenticators} ), + + ok = emqx_plugin_libs_metrics:create_metrics(authn_metrics, AuthenticatorID, + [matched, success, failed, ignore], [matched]), {ok, serialize_authenticator(Authenticator)}; {error, Reason} -> {error, Reason} @@ -618,8 +621,10 @@ handle_create_authenticator(Chain, Config, Providers) -> do_authenticate([], _) -> {stop, {error, not_authorized}}; do_authenticate([#authenticator{id = ID, provider = Provider, state = State} | More], Credential) -> + emqx_plugin_libs_metrics:inc(authn_metrics, ID, matched), try Provider:authenticate(Credential, State) of ignore -> + ok = emqx_plugin_libs_metrics:inc(authn_metrics, ID, ignore), do_authenticate(More, Credential); Result -> %% {ok, Extra} @@ -627,6 +632,12 @@ do_authenticate([#authenticator{id = ID, provider = Provider, state = State} | M %% {continue, AuthCache} %% {continue, AuthData, AuthCache} %% {error, Reason} + case Result of + {ok, _} -> + emqx_plugin_libs_metrics:inc(authn_metrics, ID, success); + {error, _} -> + emqx_plugin_libs_metrics:inc(authn_metrics, ID, failed) + end, {stop, Result} catch Class:Reason:Stacktrace -> @@ -637,6 +648,7 @@ do_authenticate([#authenticator{id = ID, provider = Provider, state = State} | M stacktrace => Stacktrace, authenticator => ID }), + emqx_plugin_libs_metrics:inc(authn_metrics, ID, ignore), do_authenticate(More, Credential) end. diff --git a/apps/emqx_authn/i18n/emqx_authn_schema_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_schema_i18n.conf index 6caa1808f..371939daa 100644 --- a/apps/emqx_authn/i18n/emqx_authn_schema_i18n.conf +++ b/apps/emqx_authn/i18n/emqx_authn_schema_i18n.conf @@ -76,6 +76,17 @@ emqx_authn_schema { } } + node_error { + desc { + en: """The error of node.""" + zh: """节点上产生的错误。""" + } + label: { + en: """Error in Node""" + zh: """节点产生的错误""" + } + } + matched { desc { en: """Count of this resource is queried.""" diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index 0e7f03ce7..49ab90856 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -902,6 +902,8 @@ create_authenticator(ConfKeyPath, ChainName, Config) -> raw_config := AuthenticatorsConfig }} -> {ok, AuthenticatorConfig} = find_config(ID, AuthenticatorsConfig), + ok = emqx_plugin_libs_metrics:create_metrics(authn_metrics, ID, + [matched, success, failed, ignore], [matched]), {200, maps:put(id, ID, convert_certs(fill_defaults(AuthenticatorConfig)))}; {error, {_PrePostConfigUpdate, emqx_authentication, Reason}} -> serialize_error(Reason); @@ -930,61 +932,55 @@ list_authenticator(_, ConfKeyPath, AuthenticatorID) -> serialize_error(Reason) end. +resource_provider() -> + [ + emqx_authn_mysql, + emqx_authn_pgsql, + emqx_authn_mongodb, + emqx_authn_redis, + emqx_authn_http + ]. + lookup_from_local_node(ChainName, AuthenticatorID) -> NodeId = node(self()), case emqx_authentication:lookup_authenticator(ChainName, AuthenticatorID) of {ok, #{provider := Provider, state := State}} -> + Metrics = emqx_plugin_libs_metrics:get_metrics(authn_metrics, AuthenticatorID), case lists:member(Provider, resource_provider()) of false -> - {error, {NodeId, resource_unsupport_metrics_and_status}}; + {ok, {NodeId, connected, Metrics}}; true -> #{resource_id := ResourceId} = State, case emqx_resource:get_instance(ResourceId) of {error, not_found} -> {error, {NodeId, not_found_resource}}; - {ok, _, #{status := Status, metrics := Metrics}} -> + {ok, _, #{status := Status}} -> {ok, {NodeId, Status, Metrics}} end end; {error, Reason} -> - {error, {NodeId, Reason}} + {error, {NodeId, list_to_binary(io_lib:format("~p", [Reason]))}} end. -resource_provider() -> - [ - emqx_authn_mysql, - emqx_authn_pgsql, - emqx_authn_mongodb, - emqx_authn_redis, - emqx_authn_http - ]. - lookup_from_all_nodes(ChainName, AuthenticatorID) -> Nodes = mria_mnesia:running_nodes(), case is_ok(emqx_authn_proto_v1:lookup_from_all_nodes(Nodes, ChainName, AuthenticatorID)) of {ok, ResList} -> - {StatusMap, MetricsMap, _} = make_result_map(ResList), + {StatusMap, MetricsMap, ErrorMap} = make_result_map(ResList), AggregateStatus = aggregate_status(maps:values(StatusMap)), AggregateMetrics = aggregate_metrics(maps:values(MetricsMap)), Fun = fun(_, V1) -> restructure_map(V1) end, MKMap = fun(Name) -> fun({Key, Val}) -> #{node => Key, Name => Val} end end, HelpFun = fun(M, Name) -> lists:map(MKMap(Name), maps:to_list(M)) end, - case AggregateStatus of - empty_metrics_and_status -> - {400, #{ - code => <<"BAD_REQUEST">>, - message => <<"Resource Not Support Status">> - }}; - _ -> - {200, #{ - node_status => HelpFun(StatusMap, status), - node_metrics => HelpFun(maps:map(Fun, MetricsMap), metrics), - status => AggregateStatus, - metrics => restructure_map(AggregateMetrics) - }} - end; + {200, #{ + node_metrics => HelpFun(maps:map(Fun, MetricsMap), metrics), + metrics => restructure_map(AggregateMetrics), + node_status => HelpFun(StatusMap, status), + status => AggregateStatus, + node_error => HelpFun(maps:map(Fun, ErrorMap), reason) + }}; {error, ErrL} -> - {500, #{ + {400, #{ code => <<"INTERNAL_ERROR">>, message => list_to_binary(io_lib:format("~p", [ErrL])) }} @@ -1019,27 +1015,28 @@ aggregate_metrics([HeadMetrics | AllMetrics]) -> make_result_map(ResList) -> Fun = fun(Elem, {StatusMap, MetricsMap, ErrorMap}) -> - case Elem of - {ok, {NodeId, Status, Metrics}} -> - { - maps:put(NodeId, Status, StatusMap), - maps:put(NodeId, Metrics, MetricsMap), - ErrorMap - }; - {error, {NodeId, Reason}} -> - {StatusMap, MetricsMap, maps:put(NodeId, Reason, ErrorMap)} - end + case Elem of + {ok, {NodeId, Status, Metrics}} -> + { + maps:put(NodeId, Status, StatusMap), + maps:put(NodeId, Metrics, MetricsMap), + ErrorMap + }; + {error, {NodeId, Reason}} -> + {StatusMap, MetricsMap, maps:put(NodeId, Reason, ErrorMap)} + end end, lists:foldl(Fun, {maps:new(), maps:new(), maps:new()}, ResList). restructure_map(#{ - counters := #{failed := Failed, matched := Match, success := Succ}, + counters := #{failed := Failed, matched := Match, success := Succ, ignore := Ignore}, rate := #{matched := #{current := Rate, last5m := Rate5m, max := RateMax}} }) -> #{ matched => Match, success => Succ, failed => Failed, + ignore => Ignore, rate => Rate, rate_last5m => Rate5m, rate_max => RateMax @@ -1083,6 +1080,7 @@ update_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Config) -> delete_authenticator(ConfKeyPath, ChainName, AuthenticatorID) -> case update_config(ConfKeyPath, {delete_authenticator, ChainName, AuthenticatorID}) of {ok, _} -> + emqx_plugin_libs_metrics:clear_metrics(authn_metrics, AuthenticatorID), {204}; {error, {_PrePostConfigUpdate, emqx_authentication, Reason}} -> serialize_error(Reason); @@ -1439,10 +1437,12 @@ status_metrics_example() -> matched => 0, success => 0, failed => 0, + ignore => 0, rate => 0.0, rate_last5m => 0.0, rate_max => 0.0 }, + node_error => [], node_metrics => [ #{ node => node(), @@ -1450,6 +1450,7 @@ status_metrics_example() -> matched => 0, success => 0, failed => 0, + ignore => 0, rate => 0.0, rate_last5m => 0.0, rate_max => 0.0 diff --git a/apps/emqx_authn/src/emqx_authn_schema.erl b/apps/emqx_authn/src/emqx_authn_schema.erl index ef20e36cf..7ac6422d9 100644 --- a/apps/emqx_authn/src/emqx_authn_schema.erl +++ b/apps/emqx_authn/src/emqx_authn_schema.erl @@ -100,6 +100,11 @@ fields("metrics_status_fields") -> mk( hoconsc:array(ref(?MODULE, "node_status")), #{desc => ?DESC("node_status")} + )}, + {"node_error", + mk( + hoconsc:array(ref(?MODULE, "node_error")), + #{desc => ?DESC("node_error")} )} ]; fields("metrics") -> @@ -107,6 +112,7 @@ fields("metrics") -> {"matched", mk(integer(), #{desc => ?DESC("matched")})}, {"success", mk(integer(), #{desc => ?DESC("success")})}, {"failed", mk(integer(), #{desc => ?DESC("failed")})}, + {"ignore", mk(integer(), #{desc => ?DESC("failed")})}, {"rate", mk(float(), #{desc => ?DESC("rate")})}, {"rate_max", mk(float(), #{desc => ?DESC("rate_max")})}, {"rate_last5m", mk(float(), #{desc => ?DESC("rate_last5m")})} @@ -120,6 +126,12 @@ fields("node_status") -> [ node_name(), {"status", mk(status(), #{desc => ?DESC("node_status")})} + ]; + +fields("node_error") -> + [ + node_name(), + {"error", mk(string(), #{desc => ?DESC("node_error")})} ]. status() -> diff --git a/apps/emqx_authn/src/emqx_authn_sup.erl b/apps/emqx_authn/src/emqx_authn_sup.erl index d88acfacc..6dbe605fe 100644 --- a/apps/emqx_authn/src/emqx_authn_sup.erl +++ b/apps/emqx_authn/src/emqx_authn_sup.erl @@ -27,5 +27,6 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> - ChildSpecs = [], + Metrics = emqx_plugin_libs_metrics:child_spec(authn_metrics), + ChildSpecs = [Metrics], {ok, {{one_for_one, 10, 10}, ChildSpecs}}. diff --git a/apps/emqx_authn/test/emqx_authn_api_SUITE.erl b/apps/emqx_authn/test/emqx_authn_api_SUITE.erl index e082c93b4..bcae2864e 100644 --- a/apps/emqx_authn/test/emqx_authn_api_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_api_SUITE.erl @@ -129,7 +129,8 @@ t_aggregate_metrics(_) -> rate => 0.0, rate_last5m => 0.0, rate_max => 0.1, - success => 1 + success => 1, + ignore => 1 } }, 'emqx@node2.emqx.io' => #{ @@ -140,7 +141,8 @@ t_aggregate_metrics(_) -> rate => 0.0, rate_last5m => 0.0, rate_max => 0.1, - success => 1 + success => 1, + ignore => 2 } } }, @@ -154,7 +156,8 @@ t_aggregate_metrics(_) -> rate => 0.0, rate_last5m => 0.0, rate_max => 0.2, - success => 2 + success => 2, + ignore => 3 } }, Res @@ -299,6 +302,37 @@ test_authenticator_users(PathPrefix) -> emqx_authn_test_lib:built_in_database_example() ), + {ok, Client} = emqtt:start_link( + [ {username, <<"u_event">>} + , {clientid, <<"c_event">>} + , {proto_ver, v5} + , {properties, #{'Session-Expiry-Interval' => 60}} + ]), + + process_flag(trap_exit, true), + ?assertMatch({error, _}, emqtt:connect(Client)), + timer:sleep(300), + + + UsersUri0 = uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database", "status"]), + {ok, 200, PageData0} = request(get, UsersUri0), + case PathPrefix of + [] -> + #{ <<"metrics">> := #{ + <<"matched">> := 1, + <<"success">> := 0, + <<"ignore">> := 1 + } + } = jiffy:decode(PageData0, [return_maps]); + ["listeners",'tcp:default'] -> + #{ <<"metrics">> := #{ + <<"matched">> := 3, + <<"success">> := 1, + <<"ignore">> := 2 + } + } = jiffy:decode(PageData0, [return_maps]) + end, + InvalidUsers = [ #{clientid => <<"u1">>, password => <<"p1">>}, #{user_id => <<"u2">>}, @@ -325,6 +359,34 @@ test_authenticator_users(PathPrefix) -> ValidUsers ), + {ok, Client1} = emqtt:start_link( + [ {username, <<"u1">>} + , {password, <<"p1">>} + , {clientid, <<"c_event">>} + , {proto_ver, v5} + , {properties, #{'Session-Expiry-Interval' => 60}} + ]), + {ok, _} = emqtt:connect(Client1), + timer:sleep(300), + UsersUri01 = uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database", "status"]), + {ok, 200, PageData01} = request(get, UsersUri01), + case PathPrefix of + [] -> + #{ <<"metrics">> := #{ + <<"matched">> := 2, + <<"success">> := 1, + <<"ignore">> := 1 + } + } = jiffy:decode(PageData01, [return_maps]); + ["listeners",'tcp:default'] -> + #{ <<"metrics">> := #{ + <<"matched">> := 4, + <<"success">> := 2, + <<"ignore">> := 2 + } + } = jiffy:decode(PageData01, [return_maps]) + end, + {ok, 200, Page1Data} = request(get, UsersUri ++ "?page=1&limit=2"), #{