feat(api): support stats/metrics API aggregate
This commit is contained in:
parent
dcfc705811
commit
740b729778
|
@ -43,6 +43,8 @@
|
||||||
, lookup_client/3
|
, lookup_client/3
|
||||||
, kickout_client/1
|
, kickout_client/1
|
||||||
, list_authz_cache/1
|
, list_authz_cache/1
|
||||||
|
, list_client_subscriptions/1
|
||||||
|
, client_subscriptions/2
|
||||||
, clean_authz_cache/1
|
, clean_authz_cache/1
|
||||||
, clean_authz_cache/2
|
, clean_authz_cache/2
|
||||||
, clean_authz_cache_all/0
|
, clean_authz_cache_all/0
|
||||||
|
@ -269,6 +271,23 @@ kickout_client(Node, ClientId) ->
|
||||||
list_authz_cache(ClientId) ->
|
list_authz_cache(ClientId) ->
|
||||||
call_client(ClientId, list_authz_cache).
|
call_client(ClientId, list_authz_cache).
|
||||||
|
|
||||||
|
list_client_subscriptions(ClientId) ->
|
||||||
|
Results = [client_subscriptions(Node, ClientId) || Node <- ekka_mnesia:running_nodes()],
|
||||||
|
Expected = lists:filter(fun({error, _}) -> false;
|
||||||
|
([]) -> false;
|
||||||
|
(_) -> true
|
||||||
|
end, Results),
|
||||||
|
case Expected of
|
||||||
|
[] -> [];
|
||||||
|
[Result|_] -> Result
|
||||||
|
end.
|
||||||
|
|
||||||
|
client_subscriptions(Node, ClientId) when Node =:= node() ->
|
||||||
|
emqx_broker:subscriptions(ClientId);
|
||||||
|
|
||||||
|
client_subscriptions(Node, ClientId) ->
|
||||||
|
rpc_call(Node, client_subscriptions, [Node, ClientId]).
|
||||||
|
|
||||||
clean_authz_cache(ClientId) ->
|
clean_authz_cache(ClientId) ->
|
||||||
Results = [clean_authz_cache(Node, ClientId) || Node <- ekka_mnesia:running_nodes()],
|
Results = [clean_authz_cache(Node, ClientId) || Node <- ekka_mnesia:running_nodes()],
|
||||||
case lists:any(fun(Item) -> Item =:= ok end, Results) of
|
case lists:any(fun(Item) -> Item =:= ok end, Results) of
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
-export([ clients/2
|
-export([ clients/2
|
||||||
, client/2
|
, client/2
|
||||||
|
, subscriptions/2
|
||||||
, authz_cache/2
|
, authz_cache/2
|
||||||
, subscribe/2
|
, subscribe/2
|
||||||
, subscribe_batch/2]).
|
, subscribe_batch/2]).
|
||||||
|
@ -68,6 +69,7 @@ apis() ->
|
||||||
[ clients_api()
|
[ clients_api()
|
||||||
, client_api()
|
, client_api()
|
||||||
, clients_authz_cache_api()
|
, clients_authz_cache_api()
|
||||||
|
, clients_subscriptions_api()
|
||||||
, subscribe_api()].
|
, subscribe_api()].
|
||||||
|
|
||||||
schemas() ->
|
schemas() ->
|
||||||
|
@ -209,7 +211,17 @@ schemas() ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[Client, AuthzCache].
|
Subscription = #{
|
||||||
|
subscription => #{
|
||||||
|
type => object,
|
||||||
|
properties => #{
|
||||||
|
topic => #{
|
||||||
|
type => string},
|
||||||
|
qos => #{
|
||||||
|
type => integer,
|
||||||
|
enum => [0,1,2]}}}
|
||||||
|
},
|
||||||
|
[Client, AuthzCache, Subscription].
|
||||||
|
|
||||||
clients_api() ->
|
clients_api() ->
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
|
@ -271,6 +283,21 @@ clients_authz_cache_api() ->
|
||||||
<<"200">> => emqx_mgmt_util:response_schema(<<"Delete clients 200 OK">>)}}},
|
<<"200">> => emqx_mgmt_util:response_schema(<<"Delete clients 200 OK">>)}}},
|
||||||
{"/clients/:clientid/authz_cache", Metadata, authz_cache}.
|
{"/clients/:clientid/authz_cache", Metadata, authz_cache}.
|
||||||
|
|
||||||
|
clients_subscriptions_api() ->
|
||||||
|
Metadata = #{
|
||||||
|
get => #{
|
||||||
|
description => <<"Get client subscriptions">>,
|
||||||
|
parameters => [#{
|
||||||
|
name => clientid,
|
||||||
|
in => path,
|
||||||
|
schema => #{type => string},
|
||||||
|
required => true
|
||||||
|
}],
|
||||||
|
responses => #{
|
||||||
|
<<"200">> => emqx_mgmt_util:response_array_schema(<<"Get client subscriptions">>, subscription)}}
|
||||||
|
},
|
||||||
|
{"/clients/:clientid/subscriptions", Metadata, subscriptions}.
|
||||||
|
|
||||||
subscribe_api() ->
|
subscribe_api() ->
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
post => #{
|
post => #{
|
||||||
|
@ -363,6 +390,14 @@ subscribe_batch(post, Request) ->
|
||||||
end || TopicInfo <- TopicInfos],
|
end || TopicInfo <- TopicInfos],
|
||||||
subscribe_batch(#{clientid => ClientID, topics => Topics}).
|
subscribe_batch(#{clientid => ClientID, topics => Topics}).
|
||||||
|
|
||||||
|
subscriptions(get, Request) ->
|
||||||
|
ClientID = cowboy_req:binding(clientid, Request),
|
||||||
|
Subs0 = emqx_mgmt:list_client_subscriptions(ClientID),
|
||||||
|
Subs = lists:map(fun({Topic, SubOpts}) ->
|
||||||
|
#{topic => Topic, qos => maps:get(qos, SubOpts)}
|
||||||
|
end, Subs0),
|
||||||
|
{200, Subs}.
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
%% api apply
|
%% api apply
|
||||||
|
|
||||||
|
|
|
@ -26,269 +26,306 @@ api_spec() ->
|
||||||
{[metrics_api()], [metrics_schema()]}.
|
{[metrics_api()], [metrics_schema()]}.
|
||||||
|
|
||||||
metrics_schema() ->
|
metrics_schema() ->
|
||||||
#{
|
Metric = #{
|
||||||
metrics => #{
|
type => object,
|
||||||
|
properties => properties()
|
||||||
|
},
|
||||||
|
Metrics = #{
|
||||||
|
type => array,
|
||||||
|
items => #{
|
||||||
type => object,
|
type => object,
|
||||||
properties => #{
|
properties => properties()
|
||||||
'actions.failure' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of failure executions of the rule engine action">>},
|
|
||||||
'actions.success' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of successful executions of the rule engine action">>},
|
|
||||||
'bytes.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of bytes received by EMQ X Broker">>},
|
|
||||||
'bytes.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of bytes sent by EMQ X Broker on this connection">>},
|
|
||||||
'client.authenticate' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of client authentications">>},
|
|
||||||
'client.auth.anonymous' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of clients who log in anonymously">>},
|
|
||||||
'client.connect' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of client connections">>},
|
|
||||||
'client.connack' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of CONNACK packet sent">>},
|
|
||||||
'client.connected' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of successful client connections">>},
|
|
||||||
'client.disconnected' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of client disconnects">>},
|
|
||||||
'client.check_authz' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of Authorization rule checks">>},
|
|
||||||
'client.subscribe' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of client subscriptions">>},
|
|
||||||
'client.unsubscribe' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of client unsubscriptions">>},
|
|
||||||
'delivery.dropped.too_large' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"The number of messages that were dropped because the length exceeded the limit when sending">>},
|
|
||||||
'delivery.dropped.queue_full' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages with a non-zero QoS that were dropped because the message queue was full when sending">>},
|
|
||||||
'delivery.dropped.qos0_msg' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages with QoS 0 that were dropped because the message queue was full when sending">>},
|
|
||||||
'delivery.dropped.expired' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages dropped due to message expiration on sending">>},
|
|
||||||
'delivery.dropped.no_local' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages that were dropped due to the No Local subscription option when sending">>},
|
|
||||||
'delivery.dropped' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Total number of discarded messages when sending">>},
|
|
||||||
'messages.delayed' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of delay- published messages stored by EMQ X Broker">>},
|
|
||||||
'messages.delivered' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages forwarded to the subscription process internally by EMQ X Broker">>},
|
|
||||||
'messages.dropped' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Total number of messages dropped by EMQ X Broker before forwarding to the subscription process">>},
|
|
||||||
'messages.dropped.expired' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages dropped due to message expiration when receiving">>},
|
|
||||||
'messages.dropped.no_subscribers' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages dropped due to no subscribers">>},
|
|
||||||
'messages.forward' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages forwarded to other nodes">>},
|
|
||||||
'messages.publish' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages published in addition to system messages">>},
|
|
||||||
'messages.qos0.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of QoS 0 messages received from clients">>},
|
|
||||||
'messages.qos1.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of QoS 1 messages received from clients">>},
|
|
||||||
'messages.qos2.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of QoS 2 messages received from clients">>},
|
|
||||||
'messages.qos0.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of QoS 0 messages sent to clients">>},
|
|
||||||
'messages.qos1.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of QoS 1 messages sent to clients">>},
|
|
||||||
'messages.qos2.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of QoS 2 messages sent to clients">>},
|
|
||||||
'messages.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages received from the client, equal to the sum of messages.qos0.received,messages.qos1.received and messages.qos2.received">>},
|
|
||||||
'messages.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages sent to the client, equal to the sum of messages.qos0.sent,messages.qos1.sent and messages.qos2.sent">>},
|
|
||||||
'messages.retained' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of retained messages stored by EMQ X Broker">>},
|
|
||||||
'messages.acked' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBACK and PUBREC packet">>},
|
|
||||||
'packets.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received packet">>},
|
|
||||||
'packets.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent packet">>},
|
|
||||||
'packets.connect.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received CONNECT packet">>},
|
|
||||||
'packets.connack.auth_error' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received CONNECT packet with failed authentication">>},
|
|
||||||
'packets.connack.error' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received CONNECT packet with unsuccessful connections">>},
|
|
||||||
'packets.connack.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent CONNACK packet">>},
|
|
||||||
'packets.publish.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBLISH packet">>},
|
|
||||||
'packets.publish.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent PUBLISH packet">>},
|
|
||||||
'packets.publish.inuse' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBLISH packet with occupied identifiers">>},
|
|
||||||
'packets.publish.auth_error' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBLISH packets with failed the Authorization check">>},
|
|
||||||
'packets.publish.error' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBLISH packet that cannot be published">>},
|
|
||||||
'packets.publish.dropped' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of messages discarded due to the receiving limit">>},
|
|
||||||
'packets.puback.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBACK packet">>},
|
|
||||||
'packets.puback.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent PUBACK packet">>},
|
|
||||||
'packets.puback.inuse' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBACK packet with occupied identifiers">>},
|
|
||||||
'packets.puback.missed' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received packet with identifiers.">>},
|
|
||||||
'packets.pubrec.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBREC packet">>},
|
|
||||||
'packets.pubrec.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent PUBREC packet">>},
|
|
||||||
'packets.pubrec.inuse' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBREC packet with occupied identifiers">>},
|
|
||||||
'packets.pubrec.missed' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBREC packet with unknown identifiers">>},
|
|
||||||
'packets.pubrel.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBREL packet">>},
|
|
||||||
'packets.pubrel.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent PUBREL packet">>},
|
|
||||||
'packets.pubrel.missed' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBREC packet with unknown identifiers">>},
|
|
||||||
'packets.pubcomp.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBCOMP packet">>},
|
|
||||||
'packets.pubcomp.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent PUBCOMP packet">>},
|
|
||||||
'packets.pubcomp.inuse' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PUBCOMP packet with occupied identifiers">>},
|
|
||||||
'packets.pubcomp.missed' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of missed PUBCOMP packet">>},
|
|
||||||
'packets.subscribe.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received SUBSCRIBE packet">>},
|
|
||||||
'packets.subscribe.error' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received SUBSCRIBE packet with failed subscriptions">>},
|
|
||||||
'packets.subscribe.auth_error' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received SUBACK packet with failed Authorization check">>},
|
|
||||||
'packets.suback.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent SUBACK packet">>},
|
|
||||||
'packets.unsubscribe.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received UNSUBSCRIBE packet">>},
|
|
||||||
'packets.unsubscribe.error' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received UNSUBSCRIBE packet with failed unsubscriptions">>},
|
|
||||||
'packets.unsuback.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent UNSUBACK packet">>},
|
|
||||||
'packets.pingreq.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received PINGREQ packet">>},
|
|
||||||
'packets.pingresp.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent PUBRESP packet">>},
|
|
||||||
'packets.disconnect.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received DISCONNECT packet">>},
|
|
||||||
'packets.disconnect.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent DISCONNECT packet">>},
|
|
||||||
'packets.auth.received' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of received AUTH packet">>},
|
|
||||||
'packets.auth.sent' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sent AUTH packet">>},
|
|
||||||
'rules.matched' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of rule matched">>},
|
|
||||||
'session.created' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sessions created">>},
|
|
||||||
'session.discarded' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sessions dropped because Clean Session or Clean Start is true">>},
|
|
||||||
'session.resumed' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sessions resumed because Clean Session or Clean Start is false">>},
|
|
||||||
'session.takeovered' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of sessions takeovered because Clean Session or Clean Start is false">>},
|
|
||||||
'session.terminated' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of terminated sessions">>}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
MetricsInfo = #{
|
||||||
|
oneOf => [ minirest:ref(<<"metric">>)
|
||||||
|
, minirest:ref(<<"metrics">>)
|
||||||
|
]
|
||||||
|
},
|
||||||
|
#{metric => Metric, metrics => Metrics, metrics_info => MetricsInfo}.
|
||||||
|
|
||||||
|
properties() ->
|
||||||
|
#{
|
||||||
|
'actions.failure' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of failure executions of the rule engine action">>},
|
||||||
|
'actions.success' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of successful executions of the rule engine action">>},
|
||||||
|
'bytes.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of bytes received by EMQ X Broker">>},
|
||||||
|
'bytes.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of bytes sent by EMQ X Broker on this connection">>},
|
||||||
|
'client.authenticate' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of client authentications">>},
|
||||||
|
'client.auth.anonymous' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of clients who log in anonymously">>},
|
||||||
|
'client.connect' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of client connections">>},
|
||||||
|
'client.connack' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of CONNACK packet sent">>},
|
||||||
|
'client.connected' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of successful client connections">>},
|
||||||
|
'client.disconnected' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of client disconnects">>},
|
||||||
|
'client.check_authz' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of Authorization rule checks">>},
|
||||||
|
'client.subscribe' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of client subscriptions">>},
|
||||||
|
'client.unsubscribe' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of client unsubscriptions">>},
|
||||||
|
'delivery.dropped.too_large' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"The number of messages that were dropped because the length exceeded the limit when sending">>},
|
||||||
|
'delivery.dropped.queue_full' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages with a non-zero QoS that were dropped because the message queue was full when sending">>},
|
||||||
|
'delivery.dropped.qos0_msg' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages with QoS 0 that were dropped because the message queue was full when sending">>},
|
||||||
|
'delivery.dropped.expired' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages dropped due to message expiration on sending">>},
|
||||||
|
'delivery.dropped.no_local' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages that were dropped due to the No Local subscription option when sending">>},
|
||||||
|
'delivery.dropped' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Total number of discarded messages when sending">>},
|
||||||
|
'messages.delayed' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of delay- published messages stored by EMQ X Broker">>},
|
||||||
|
'messages.delivered' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages forwarded to the subscription process internally by EMQ X Broker">>},
|
||||||
|
'messages.dropped' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Total number of messages dropped by EMQ X Broker before forwarding to the subscription process">>},
|
||||||
|
'messages.dropped.expired' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages dropped due to message expiration when receiving">>},
|
||||||
|
'messages.dropped.no_subscribers' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages dropped due to no subscribers">>},
|
||||||
|
'messages.forward' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages forwarded to other nodes">>},
|
||||||
|
'messages.publish' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages published in addition to system messages">>},
|
||||||
|
'messages.qos0.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of QoS 0 messages received from clients">>},
|
||||||
|
'messages.qos1.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of QoS 1 messages received from clients">>},
|
||||||
|
'messages.qos2.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of QoS 2 messages received from clients">>},
|
||||||
|
'messages.qos0.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of QoS 0 messages sent to clients">>},
|
||||||
|
'messages.qos1.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of QoS 1 messages sent to clients">>},
|
||||||
|
'messages.qos2.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of QoS 2 messages sent to clients">>},
|
||||||
|
'messages.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages received from the client, equal to the sum of messages.qos0.received,messages.qos1.received and messages.qos2.received">>},
|
||||||
|
'messages.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages sent to the client, equal to the sum of messages.qos0.sent,messages.qos1.sent and messages.qos2.sent">>},
|
||||||
|
'messages.retained' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of retained messages stored by EMQ X Broker">>},
|
||||||
|
'messages.acked' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBACK and PUBREC packet">>},
|
||||||
|
'packets.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received packet">>},
|
||||||
|
'packets.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent packet">>},
|
||||||
|
'packets.connect.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received CONNECT packet">>},
|
||||||
|
'packets.connack.auth_error' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received CONNECT packet with failed authentication">>},
|
||||||
|
'packets.connack.error' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received CONNECT packet with unsuccessful connections">>},
|
||||||
|
'packets.connack.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent CONNACK packet">>},
|
||||||
|
'packets.publish.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBLISH packet">>},
|
||||||
|
'packets.publish.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent PUBLISH packet">>},
|
||||||
|
'packets.publish.inuse' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBLISH packet with occupied identifiers">>},
|
||||||
|
'packets.publish.auth_error' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBLISH packets with failed the Authorization check">>},
|
||||||
|
'packets.publish.error' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBLISH packet that cannot be published">>},
|
||||||
|
'packets.publish.dropped' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of messages discarded due to the receiving limit">>},
|
||||||
|
'packets.puback.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBACK packet">>},
|
||||||
|
'packets.puback.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent PUBACK packet">>},
|
||||||
|
'packets.puback.inuse' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBACK packet with occupied identifiers">>},
|
||||||
|
'packets.puback.missed' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received packet with identifiers.">>},
|
||||||
|
'packets.pubrec.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBREC packet">>},
|
||||||
|
'packets.pubrec.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent PUBREC packet">>},
|
||||||
|
'packets.pubrec.inuse' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBREC packet with occupied identifiers">>},
|
||||||
|
'packets.pubrec.missed' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBREC packet with unknown identifiers">>},
|
||||||
|
'packets.pubrel.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBREL packet">>},
|
||||||
|
'packets.pubrel.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent PUBREL packet">>},
|
||||||
|
'packets.pubrel.missed' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBREC packet with unknown identifiers">>},
|
||||||
|
'packets.pubcomp.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBCOMP packet">>},
|
||||||
|
'packets.pubcomp.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent PUBCOMP packet">>},
|
||||||
|
'packets.pubcomp.inuse' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PUBCOMP packet with occupied identifiers">>},
|
||||||
|
'packets.pubcomp.missed' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of missed PUBCOMP packet">>},
|
||||||
|
'packets.subscribe.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received SUBSCRIBE packet">>},
|
||||||
|
'packets.subscribe.error' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received SUBSCRIBE packet with failed subscriptions">>},
|
||||||
|
'packets.subscribe.auth_error' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received SUBACK packet with failed Authorization check">>},
|
||||||
|
'packets.suback.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent SUBACK packet">>},
|
||||||
|
'packets.unsubscribe.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received UNSUBSCRIBE packet">>},
|
||||||
|
'packets.unsubscribe.error' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received UNSUBSCRIBE packet with failed unsubscriptions">>},
|
||||||
|
'packets.unsuback.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent UNSUBACK packet">>},
|
||||||
|
'packets.pingreq.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received PINGREQ packet">>},
|
||||||
|
'packets.pingresp.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent PUBRESP packet">>},
|
||||||
|
'packets.disconnect.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received DISCONNECT packet">>},
|
||||||
|
'packets.disconnect.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent DISCONNECT packet">>},
|
||||||
|
'packets.auth.received' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of received AUTH packet">>},
|
||||||
|
'packets.auth.sent' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sent AUTH packet">>},
|
||||||
|
'rules.matched' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of rule matched">>},
|
||||||
|
'session.created' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sessions created">>},
|
||||||
|
'session.discarded' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sessions dropped because Clean Session or Clean Start is true">>},
|
||||||
|
'session.resumed' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sessions resumed because Clean Session or Clean Start is false">>},
|
||||||
|
'session.takeovered' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of sessions takeovered because Clean Session or Clean Start is false">>},
|
||||||
|
'session.terminated' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of terminated sessions">>}
|
||||||
}.
|
}.
|
||||||
|
|
||||||
metrics_api() ->
|
metrics_api() ->
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
get => #{
|
get => #{
|
||||||
description => <<"EMQ X metrics">>,
|
description => <<"EMQ X metrics">>,
|
||||||
|
parameters => [#{
|
||||||
|
name => aggregate,
|
||||||
|
in => query,
|
||||||
|
schema => #{type => boolean}
|
||||||
|
}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"200">> => emqx_mgmt_util:response_schema(<<"List all metrics">>, metrics)}}},
|
<<"200">> => #{
|
||||||
|
description => <<"List all metrics">>,
|
||||||
|
content => #{
|
||||||
|
'application/json' => #{
|
||||||
|
schema => minirest:ref(<<"metrics_info">>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{"/metrics", Metadata, list}.
|
{"/metrics", Metadata, list}.
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
%% api apply
|
%% api apply
|
||||||
list(get, _) ->
|
list(get, Request) ->
|
||||||
{200, emqx_mgmt:get_metrics()}.
|
Params = cowboy_req:parse_qs(Request),
|
||||||
|
case proplists:get_value(<<"aggregate">>, Params, undefined) of
|
||||||
|
<<"true">> ->
|
||||||
|
{200, emqx_mgmt:get_metrics()};
|
||||||
|
_ ->
|
||||||
|
Data = [maps:from_list(emqx_mgmt:get_metrics(Node) ++ [{node, Node}]) ||
|
||||||
|
Node <- ekka_mnesia:running_nodes()],
|
||||||
|
{200, Data}
|
||||||
|
end.
|
||||||
|
|
|
@ -22,86 +22,121 @@
|
||||||
-export([list/2]).
|
-export([list/2]).
|
||||||
|
|
||||||
api_spec() ->
|
api_spec() ->
|
||||||
{[stats_api()], [stats_schema()]}.
|
{[stats_api()], stats_schema()}.
|
||||||
|
|
||||||
stats_schema() ->
|
stats_schema() ->
|
||||||
#{
|
Stats = #{
|
||||||
stats => #{
|
type => array,
|
||||||
|
items => #{
|
||||||
type => object,
|
type => object,
|
||||||
properties => #{
|
properties => maps:put('node', #{type => string, description => <<"Node">>}, properties())
|
||||||
'connections.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of current connections">>},
|
|
||||||
'connections.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Historical maximum number of connections">>},
|
|
||||||
'channels.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"sessions.count">>},
|
|
||||||
'channels.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"session.max">>},
|
|
||||||
'sessions.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of current sessions">>},
|
|
||||||
'sessions.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Historical maximum number of sessions">>},
|
|
||||||
'topics.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of current topics">>},
|
|
||||||
'topics.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Historical maximum number of topics">>},
|
|
||||||
'suboptions.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"subscriptions.count">>},
|
|
||||||
'suboptions.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"subscriptions.max">>},
|
|
||||||
'subscribers.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of current subscribers">>},
|
|
||||||
'subscribers.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Historical maximum number of subscribers">>},
|
|
||||||
'subscriptions.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of current subscriptions, including shared subscriptions">>},
|
|
||||||
'subscriptions.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Historical maximum number of subscriptions">>},
|
|
||||||
'subscriptions.shared.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of current shared subscriptions">>},
|
|
||||||
'subscriptions.shared.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Historical maximum number of shared subscriptions">>},
|
|
||||||
'routes.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of current routes">>},
|
|
||||||
'routes.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Historical maximum number of routes">>},
|
|
||||||
'retained.count' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Number of currently retained messages">>},
|
|
||||||
'retained.max' => #{
|
|
||||||
type => integer,
|
|
||||||
description => <<"Historical maximum number of retained messages">>}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Stat = #{
|
||||||
|
type => object,
|
||||||
|
properties => properties()
|
||||||
|
},
|
||||||
|
StatsInfo =#{
|
||||||
|
oneOf => [ minirest:ref(<<"stats">>)
|
||||||
|
, minirest:ref(<<"stat">>)
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[#{stats => Stats, stat => Stat, stats_info => StatsInfo}].
|
||||||
|
|
||||||
|
properties() ->
|
||||||
|
#{
|
||||||
|
'connections.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of current connections">>},
|
||||||
|
'connections.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of connections">>},
|
||||||
|
'channels.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"sessions.count">>},
|
||||||
|
'channels.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"session.max">>},
|
||||||
|
'sessions.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of current sessions">>},
|
||||||
|
'sessions.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of sessions">>},
|
||||||
|
'topics.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of current topics">>},
|
||||||
|
'topics.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of topics">>},
|
||||||
|
'suboptions.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"subscriptions.count">>},
|
||||||
|
'suboptions.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"subscriptions.max">>},
|
||||||
|
'subscribers.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of current subscribers">>},
|
||||||
|
'subscribers.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of subscribers">>},
|
||||||
|
'subscriptions.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of current subscriptions, including shared subscriptions">>},
|
||||||
|
'subscriptions.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of subscriptions">>},
|
||||||
|
'subscriptions.shared.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of current shared subscriptions">>},
|
||||||
|
'subscriptions.shared.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of shared subscriptions">>},
|
||||||
|
'routes.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of current routes">>},
|
||||||
|
'routes.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of routes">>},
|
||||||
|
'retained.count' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Number of currently retained messages">>},
|
||||||
|
'retained.max' => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of retained messages">>}
|
||||||
}.
|
}.
|
||||||
|
|
||||||
stats_api() ->
|
stats_api() ->
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
get => #{
|
get => #{
|
||||||
description => <<"EMQ X stats">>,
|
description => <<"EMQ X stats">>,
|
||||||
|
parameters => [#{
|
||||||
|
name => aggregate,
|
||||||
|
in => query,
|
||||||
|
schema => #{type => boolean}
|
||||||
|
}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"200">> => emqx_mgmt_util:response_schema(<<"List stats ok">>, stats)}}},
|
<<"200">> => #{
|
||||||
|
description => <<"List stats ok">>,
|
||||||
|
content => #{
|
||||||
|
'application/json' => #{
|
||||||
|
schema => minirest:ref(<<"stats_info">>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}},
|
||||||
{"/stats", Metadata, list}.
|
{"/stats", Metadata, list}.
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
%% api apply
|
%% api apply
|
||||||
list(get, _Request) ->
|
list(get, Request) ->
|
||||||
{200, emqx_mgmt:get_stats()}.
|
Params = cowboy_req:parse_qs(Request),
|
||||||
|
case proplists:get_value(<<"aggregate">>, Params, undefined) of
|
||||||
|
<<"true">> ->
|
||||||
|
{200, emqx_mgmt:get_stats()};
|
||||||
|
_ ->
|
||||||
|
Data = [maps:from_list(emqx_mgmt:get_stats(Node) ++ [{node, Node}]) ||
|
||||||
|
Node <- ekka_mnesia:running_nodes()],
|
||||||
|
{200, Data}
|
||||||
|
end.
|
||||||
|
|
|
@ -40,7 +40,7 @@ set_special_configs(_App) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_metrics_api(_) ->
|
t_metrics_api(_) ->
|
||||||
MetricsPath = emqx_mgmt_api_test_util:api_path(["metrics"]),
|
MetricsPath = emqx_mgmt_api_test_util:api_path(["metrics?aggregate=true"]),
|
||||||
SystemMetrics = emqx_mgmt:get_metrics(),
|
SystemMetrics = emqx_mgmt:get_metrics(),
|
||||||
{ok, MetricsResponse} = emqx_mgmt_api_test_util:request_api(get, MetricsPath),
|
{ok, MetricsResponse} = emqx_mgmt_api_test_util:request_api(get, MetricsPath),
|
||||||
Metrics = emqx_json:decode(MetricsResponse, [return_maps]),
|
Metrics = emqx_json:decode(MetricsResponse, [return_maps]),
|
||||||
|
|
|
@ -40,7 +40,7 @@ set_special_configs(_App) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_stats_api(_) ->
|
t_stats_api(_) ->
|
||||||
StatsPath = emqx_mgmt_api_test_util:api_path(["stats"]),
|
StatsPath = emqx_mgmt_api_test_util:api_path(["stats?aggregate=true"]),
|
||||||
SystemStats = emqx_mgmt:get_stats(),
|
SystemStats = emqx_mgmt:get_stats(),
|
||||||
{ok, StatsResponse} = emqx_mgmt_api_test_util:request_api(get, StatsPath),
|
{ok, StatsResponse} = emqx_mgmt_api_test_util:request_api(get, StatsPath),
|
||||||
Stats = emqx_json:decode(StatsResponse, [return_maps]),
|
Stats = emqx_json:decode(StatsResponse, [return_maps]),
|
||||||
|
|
Loading…
Reference in New Issue