diff --git a/apps/emqx_management/src/emqx_mgmt_api_status.erl b/apps/emqx_management/src/emqx_mgmt_api_status.erl
index 7d5c18e59..c0ee42e2b 100644
--- a/apps/emqx_management/src/emqx_mgmt_api_status.erl
+++ b/apps/emqx_management/src/emqx_mgmt_api_status.erl
@@ -45,6 +45,17 @@ schema("/status") ->
#{
'operationId' => get_status,
get => #{
+ parameters => [
+ {format,
+ hoconsc:mk(
+ string(),
+ #{
+ in => query,
+ default => <<"text">>,
+ desc => ?DESC(get_status_api_format)
+ }
+ )}
+ ],
description => ?DESC(get_status_api),
tags => ?TAGS,
security => [],
@@ -70,7 +81,16 @@ path() ->
"/status".
init(Req0, State) ->
- {Code, Headers, Body} = running_status(),
+ Format =
+ try
+ QS = cowboy_req:parse_qs(Req0),
+ {_, F} = lists:keyfind(<<"format">>, 1, QS),
+ F
+ catch
+ _:_ ->
+ <<"text">>
+ end,
+ {Code, Headers, Body} = running_status(Format),
Req = cowboy_req:reply(Code, Headers, Body, Req0),
{ok, Req, State}.
@@ -78,29 +98,52 @@ init(Req0, State) ->
%% API Handler funcs
%%--------------------------------------------------------------------
-get_status(get, _Params) ->
- running_status().
+get_status(get, Params) ->
+ Format = maps:get(<<"format">>, maps:get(query_string, Params, #{}), <<"text">>),
+ running_status(iolist_to_binary(Format)).
-running_status() ->
+running_status(Format) ->
case emqx_dashboard_listener:is_ready(timer:seconds(20)) of
true ->
- BrokerStatus = broker_status(),
AppStatus = application_status(),
- Body = io_lib:format("Node ~ts is ~ts~nemqx is ~ts", [node(), BrokerStatus, AppStatus]),
+ Body = do_get_status(AppStatus, Format),
StatusCode =
case AppStatus of
running -> 200;
not_running -> 503
end,
+ ContentType =
+ case Format of
+ <<"json">> -> <<"applicatin/json">>;
+ _ -> <<"text/plain">>
+ end,
Headers = #{
- <<"content-type">> => <<"text/plain">>,
+ <<"content-type">> => ContentType,
<<"retry-after">> => <<"15">>
},
- {StatusCode, Headers, list_to_binary(Body)};
+ {StatusCode, Headers, iolist_to_binary(Body)};
false ->
{503, #{<<"retry-after">> => <<"15">>}, <<>>}
end.
+do_get_status(AppStatus, <<"json">>) ->
+ BrokerStatus = broker_status(),
+ emqx_utils_json:encode(#{
+ node_name => atom_to_binary(node(), utf8),
+ rel_vsn => vsn(),
+ broker_status => atom_to_binary(BrokerStatus),
+ app_status => atom_to_binary(AppStatus)
+ });
+do_get_status(AppStatus, _) ->
+ BrokerStatus = broker_status(),
+ io_lib:format("Node ~ts is ~ts~nemqx is ~ts", [node(), BrokerStatus, AppStatus]).
+
+vsn() ->
+ iolist_to_binary([
+ emqx_release:edition_vsn_prefix(),
+ emqx_release:version()
+ ]).
+
broker_status() ->
case emqx:is_running() of
true ->
diff --git a/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl
index f0200c410..e8e0b4ac9 100644
--- a/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl
+++ b/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl
@@ -38,7 +38,10 @@ all() ->
get_status_tests() ->
[
t_status_ok,
- t_status_not_ok
+ t_status_not_ok,
+ t_status_text_format,
+ t_status_json_format,
+ t_status_bad_format_qs
].
groups() ->
@@ -87,8 +90,10 @@ do_request(Opts) ->
headers := Headers,
body := Body0
} = Opts,
+ QS = maps:get(qs, Opts, ""),
URL = ?HOST ++ filename:join(Path0),
- {ok, #{host := Host, port := Port, path := Path}} = emqx_http_lib:uri_parse(URL),
+ {ok, #{host := Host, port := Port, path := Path1}} = emqx_http_lib:uri_parse(URL),
+ Path = Path1 ++ QS,
%% we must not use `httpc' here, because it keeps retrying when it
%% receives a 503 with `retry-after' header, and there's no option
%% to stop that behavior...
@@ -165,3 +170,73 @@ t_status_not_ok(Config) ->
Headers
),
ok.
+
+t_status_text_format(Config) ->
+ Path = ?config(get_status_path, Config),
+ #{
+ body := Resp,
+ status_code := StatusCode
+ } = do_request(#{
+ method => get,
+ path => Path,
+ qs => "?format=text",
+ headers => [],
+ body => no_body
+ }),
+ ?assertEqual(200, StatusCode),
+ ?assertMatch(
+ {match, _},
+ re:run(Resp, <<"emqx is running$">>)
+ ),
+ ok.
+
+t_status_json_format(Config) ->
+ Path = ?config(get_status_path, Config),
+ #{
+ body := Resp,
+ status_code := StatusCode
+ } = do_request(#{
+ method => get,
+ path => Path,
+ qs => "?format=json",
+ headers => [],
+ body => no_body
+ }),
+ ?assertEqual(200, StatusCode),
+ ?assertMatch(
+ #{<<"app_status">> := <<"running">>},
+ emqx_utils_json:decode(Resp)
+ ),
+ ok.
+
+t_status_bad_format_qs(Config) ->
+ lists:foreach(
+ fun(QS) ->
+ test_status_bad_format_qs(QS, Config)
+ end,
+ [
+ "?a=b",
+ "?format=",
+ "?format=x"
+ ]
+ ).
+
+%% when query-sting is invalid, fallback to text format
+test_status_bad_format_qs(QS, Config) ->
+ Path = ?config(get_status_path, Config),
+ #{
+ body := Resp,
+ status_code := StatusCode
+ } = do_request(#{
+ method => get,
+ path => Path,
+ qs => QS,
+ headers => [],
+ body => no_body
+ }),
+ ?assertEqual(200, StatusCode),
+ ?assertMatch(
+ {match, _},
+ re:run(Resp, <<"emqx is running$">>)
+ ),
+ ok.
diff --git a/rel/i18n/emqx_mgmt_api_status.hocon b/rel/i18n/emqx_mgmt_api_status.hocon
index 28278b747..2034d13bc 100644
--- a/rel/i18n/emqx_mgmt_api_status.hocon
+++ b/rel/i18n/emqx_mgmt_api_status.hocon
@@ -1,21 +1,42 @@
emqx_mgmt_api_status {
get_status_api.desc:
-"""Serves as a health check for the node. Returns a plain text response describing the status of the node. This endpoint requires no authentication.
+"""Serves as a health check for the node.
+Returns response to describe the status of the node and the application.
+
+This endpoint requires no authentication.
Returns status code 200 if the EMQX application is up and running, 503 otherwise.
This API was introduced in v5.0.10.
-The GET `/status` endpoint (without the `/api/...` prefix) is also an alias to this endpoint and works in the same way. This alias has been available since v5.0.0."""
+The GET `/status` endpoint (without the `/api/...` prefix) is also an alias to this endpoint and works in the same way.
+This alias has been available since v5.0.0.
+
+Starting from v5.0.25 or e5.0.4, you can also use 'format' parameter to get JSON format information.
+"""
get_status_api.label:
"""Service health check"""
get_status_response200.desc:
-"""Node emqx@127.0.0.1 is started
+"""If 'format' parameter is 'json', then it returns a JSON like below:
+{
+ "rel_vsn": "v5.0.23",
+ "node_name": "emqx@127.0.0.1",
+ "broker_status": "started",
+ "app_status": "running"
+}
+
+Otherwise it returns free text strings as below:
+Node emqx@127.0.0.1 is started
emqx is running"""
get_status_response503.desc:
-"""Node emqx@127.0.0.1 is stopped
-emqx is not_running"""
+"""When EMQX application is temporary not running or being restarted, it may return 'emqx is not_running'.
+If the 'format' parameter is provided 'json', the nthe 'app_status' field in the JSON object is 'not_running'.
+"""
+
+get_status_api_format.desc:
+"""Specify the response format, 'text' (default) to return the HTTP body in free text,
+or 'json' to return the HTTP body with a JSON object."""
}
diff --git a/rel/i18n/zh/emqx_mgmt_api_status.hocon b/rel/i18n/zh/emqx_mgmt_api_status.hocon
index 3625db967..3938f47c1 100644
--- a/rel/i18n/zh/emqx_mgmt_api_status.hocon
+++ b/rel/i18n/zh/emqx_mgmt_api_status.hocon
@@ -1,22 +1,34 @@
emqx_mgmt_api_status {
get_status_api.desc:
-"""作为节点的健康检查。 返回一个纯文本的响应,描述节点的状态。
+"""节点的健康检查。 返回节点状态的描述信息。
如果 EMQX 应用程序已经启动并运行,返回状态代码 200,否则返回 503。
这个API是在v5.0.10中引入的。
-GET `/status`端点(没有`/api/...`前缀)也是这个端点的一个别名,工作方式相同。 这个别名从v5.0.0开始就有了。"""
+GET `/status`端点(没有`/api/...`前缀)也是这个端点的一个别名,工作方式相同。 这个别名从v5.0.0开始就有了。
+自 v5.0.25 和 e5.0.4 开始,可以通过指定 'format' 参数来得到 JSON 格式的信息。"""
get_status_api.label:
"""服务健康检查"""
get_status_response200.desc:
-"""Node emqx@127.0.0.1 is started
+"""如果 'format' 参数为 'json',则返回如下JSON:
+{
+ "rel_vsn": "v5.0.23",
+ "node_name": "emqx@127.0.0.1",
+ "broker_status": "started",
+ "app_status": "running"
+}
+
+否则返回2行自由格式的文本,第一行描述节点的状态,第二行描述 EMQX 应用运行状态。例如:
+Node emqx@127.0.0.1 is started
emqx is running"""
get_status_response503.desc:
-"""Node emqx@127.0.0.1 is stopped
-emqx is not_running"""
+"""如果 EMQX 应用暂时没有启动,或正在重启,则可能返回 'emqx is not_running'"""
+
+get_status_api_format.desc:
+"""指定返回的内容格式。使用 'text'(默认)则返回自由格式的字符串; 'json' 则返回 JSON 格式。"""
}