emqx/apps/emqx_dashboard/src/emqx_dashboard_monitor_api.erl

186 lines
5.8 KiB
Erlang

%%--------------------------------------------------------------------
%% Copyright (c) 2019-2022 EMQ Technologies Co., Ltd. All Rights Reserved.
%%--------------------------------------------------------------------
-module(emqx_dashboard_monitor_api).
-include("emqx_dashboard.hrl").
-include_lib("typerefl/include/types.hrl").
-behaviour(minirest_api).
-export([api_spec/0]).
-export([
paths/0,
schema/1,
fields/1
]).
-export([
monitor/2,
monitor_current/2
]).
api_spec() ->
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true, translate_body => true}).
paths() ->
[
"/monitor",
"/monitor/nodes/:node",
"/monitor_current",
"/monitor_current/nodes/:node"
].
schema("/monitor") ->
#{
'operationId' => monitor,
get => #{
tags => [<<"Metrics">>],
desc => <<"List monitor data.">>,
parameters => [parameter_latest()],
responses => #{
200 => hoconsc:mk(hoconsc:array(hoconsc:ref(sampler)), #{}),
400 => emqx_dashboard_swagger:error_codes(['BAD_RPC'], <<"Bad RPC">>)
}
}
};
schema("/monitor/nodes/:node") ->
#{
'operationId' => monitor,
get => #{
tags => [<<"Metrics">>],
desc => <<"List the monitor data on the node.">>,
parameters => [parameter_node(), parameter_latest()],
responses => #{
200 => hoconsc:mk(hoconsc:array(hoconsc:ref(sampler)), #{}),
400 => emqx_dashboard_swagger:error_codes(['BAD_RPC'], <<"Bad RPC">>)
}
}
};
schema("/monitor_current") ->
#{
'operationId' => monitor_current,
get => #{
tags => [<<"Metrics">>],
desc => <<"Current status. Gauge and rate.">>,
responses => #{
200 => hoconsc:mk(hoconsc:ref(sampler_current), #{})
}
}
};
schema("/monitor_current/nodes/:node") ->
#{
'operationId' => monitor_current,
get => #{
tags => [<<"Metrics">>],
desc => <<"Node current status. Gauge and rate.">>,
parameters => [parameter_node()],
responses => #{
200 => hoconsc:mk(hoconsc:ref(sampler_current), #{}),
400 => emqx_dashboard_swagger:error_codes(['BAD_RPC'], <<"Bad RPC">>)
}
}
}.
parameter_latest() ->
Info = #{
in => query,
required => false,
example => 5 * 60,
desc => <<"The latest N seconds data. Like 300 for 5 min.">>
},
{latest, hoconsc:mk(range(1, inf), Info)}.
parameter_node() ->
Info = #{
in => path,
required => true,
example => node(),
desc => <<"EMQX node name.">>
},
{node, hoconsc:mk(binary(), Info)}.
fields(sampler) ->
Samplers =
[
{SamplerName, hoconsc:mk(integer(), #{desc => swagger_desc(SamplerName)})}
|| SamplerName <- ?SAMPLER_LIST
],
[{time_stamp, hoconsc:mk(non_neg_integer(), #{desc => <<"Timestamp">>})} | Samplers];
fields(sampler_current) ->
Names = maps:values(?DELTA_SAMPLER_RATE_MAP) ++ ?GAUGE_SAMPLER_LIST,
[
{SamplerName, hoconsc:mk(integer(), #{desc => swagger_desc(SamplerName)})}
|| SamplerName <- Names
].
%% -------------------------------------------------------------------------------------------------
%% API
monitor(get, #{query_string := QS, bindings := Bindings}) ->
Latest = maps:get(<<"latest">>, QS, infinity),
Node = binary_to_atom(maps:get(node, Bindings, <<"all">>)),
case emqx_dashboard_monitor:samplers(Node, Latest) of
{badrpc, {Node, Reason}} ->
Message = list_to_binary(io_lib:format("Bad node ~p, rpc failed ~p", [Node, Reason])),
{400, 'BAD_RPC', Message};
Samplers ->
{200, Samplers}
end.
monitor_current(get, #{bindings := Bindings}) ->
NodeOrCluster = binary_to_atom(maps:get(node, Bindings, <<"all">>), utf8),
case emqx_dashboard_monitor:current_rate(NodeOrCluster) of
{ok, CurrentRate} ->
{200, CurrentRate};
{badrpc, {Node, Reason}} ->
Message = list_to_binary(io_lib:format("Bad node ~p, rpc failed ~p", [Node, Reason])),
{400, 'BAD_RPC', Message}
end.
%% -------------------------------------------------------------------------------------------------
%% Internal
swagger_desc(received) ->
swagger_desc_format("Received messages ");
swagger_desc(received_bytes) ->
swagger_desc_format("Received bytes ");
swagger_desc(sent) ->
swagger_desc_format("Sent messages ");
swagger_desc(sent_bytes) ->
swagger_desc_format("Sent bytes ");
swagger_desc(dropped) ->
swagger_desc_format("Dropped messages ");
swagger_desc(subscriptions) ->
<<
"Subscriptions at the time of sampling."
" Can only represent the approximate state"
>>;
swagger_desc(topics) ->
<<
"Count topics at the time of sampling."
" Can only represent the approximate state"
>>;
swagger_desc(connections) ->
<<
"Connections at the time of sampling."
" Can only represent the approximate state"
>>;
swagger_desc(received_msg_rate) ->
swagger_desc_format("Dropped messages ", per);
%swagger_desc(received_bytes_rate) -> swagger_desc_format("Received bytes ", per);
swagger_desc(sent_msg_rate) ->
swagger_desc_format("Sent messages ", per);
%swagger_desc(sent_bytes_rate) -> swagger_desc_format("Sent bytes ", per);
swagger_desc(dropped_msg_rate) ->
swagger_desc_format("Dropped messages ", per).
swagger_desc_format(Format) ->
swagger_desc_format(Format, last).
swagger_desc_format(Format, Type) ->
Interval = emqx_conf:get([dashboard, monitor, interval], ?DEFAULT_SAMPLE_INTERVAL),
list_to_binary(io_lib:format(Format ++ "~p ~p seconds", [Type, Interval])).