emqx/apps/emqx_dashboard/src/emqx_dashboard_monitor_api.erl
2023-06-08 13:58:31 +02:00

198 lines
6.1 KiB
Erlang

%%--------------------------------------------------------------------
%% Copyright (c) 2019-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
%%--------------------------------------------------------------------
-module(emqx_dashboard_monitor_api).
-include("emqx_dashboard.hrl").
-include_lib("typerefl/include/types.hrl").
-include_lib("hocon/include/hocon_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">>],
description => ?DESC(list_monitor),
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">>],
description => ?DESC(list_monitor_node),
parameters => [parameter_node(), parameter_latest()],
responses => #{
200 => hoconsc:mk(hoconsc:array(hoconsc:ref(sampler)), #{}),
404 => emqx_dashboard_swagger:error_codes(['NOT_FOUND'], <<"Node not found">>)
}
}
};
schema("/monitor_current") ->
#{
'operationId' => monitor_current,
get => #{
tags => [<<"Metrics">>],
description => ?DESC(current_stats),
responses => #{
200 => hoconsc:mk(hoconsc:ref(sampler_current), #{})
}
}
};
schema("/monitor_current/nodes/:node") ->
#{
'operationId' => monitor_current,
get => #{
tags => [<<"Metrics">>],
description => ?DESC(current_stats_node),
parameters => [parameter_node()],
responses => #{
200 => hoconsc:mk(hoconsc:ref(sampler_current), #{}),
404 => emqx_dashboard_swagger:error_codes(['NOT_FOUND'], <<"Node not found">>)
}
}
}.
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),
RawNode = maps:get(node, Bindings, <<"all">>),
emqx_utils_api:with_node_or_cluster(RawNode, dashboard_samplers_fun(Latest)).
dashboard_samplers_fun(Latest) ->
fun(NodeOrCluster) ->
case emqx_dashboard_monitor:samplers(NodeOrCluster, Latest) of
{badrpc, _} = Error -> {error, Error};
Samplers -> {ok, Samplers}
end
end.
monitor_current(get, #{bindings := []}) ->
emqx_utils_api:with_node_or_cluster(erlang:node(), fun emqx_dashboard_monitor:current_rate/1);
monitor_current(get, #{bindings := Bindings}) ->
RawNode = maps:get(node, Bindings, <<"all">>),
emqx_utils_api:with_node_or_cluster(RawNode, fun current_rate/1).
current_rate(Node) ->
case emqx_dashboard_monitor:current_rate(Node) of
{badrpc, _} = BadRpc ->
{error, BadRpc};
{ok, _} = OkResult ->
OkResult
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) ->
<<
"Sessions at the time of sampling."
" Can only represent the approximate state"
>>;
swagger_desc(live_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])).