You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ji...@apache.org on 2020/12/03 12:43:43 UTC
[couchdb] 01/01: introduce prometheus endpoint
This is an automated email from the ASF dual-hosted git repository.
jiangphcn pushed a commit to branch prometheus-endpoint
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 7deab47e90da8aa5bade8a58393cd1709d05ee96
Author: jiangph <ji...@cn.ibm.com>
AuthorDate: Thu Dec 3 20:41:36 2020 +0800
introduce prometheus endpoint
---
src/chttpd/src/chttpd_node.erl | 102 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/src/chttpd/src/chttpd_node.erl b/src/chttpd/src/chttpd_node.erl
index 6407df1..ce4c349 100644
--- a/src/chttpd/src/chttpd_node.erl
+++ b/src/chttpd/src/chttpd_node.erl
@@ -122,6 +122,23 @@ handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_system">>]}=Req) -
send_json(Req, EJSON);
handle_node_req(#httpd{path_parts=[_, _Node, <<"_system">>]}=Req) ->
send_method_not_allowed(Req, "GET");
+
+% GET /_node/$node/metrics
+handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"metrics">>]}=Req) ->
+ Stats = call_node(Node, chttpd_node, get_stats, []),
+ EJSON = couch_stats_httpd:to_ejson(Stats),
+ Headers = [{<<"Content-Type">>, <<"text/plain">>}],
+ CouchDB = get_couchdb_stats(),
+ %System = couch_stats_httpd:to_ejson(get_system_stats()),
+ Body = to_bin(lists:map(fun(Line) ->
+ io_lib:format("~s~n", [Line])
+ end, CouchDB)),
+ {ok, Resp} = chttpd:start_response_length(Req, 200, Headers, size(Body)),
+ chttpd:send(Resp, Body);
+handle_node_req(#httpd{path_parts=[_, _Node, <<"metrics">>]}=Req) ->
+ send_method_not_allowed(Req, "GET");
+
+
% POST /_node/$node/_restart
handle_node_req(#httpd{method='POST', path_parts=[_, Node, <<"_restart">>]}=Req) ->
call_node(Node, init, restart, []),
@@ -298,3 +315,88 @@ run_queues() ->
[DCQ | SQs] = lists:reverse(statistics(run_queue_lengths)),
{lists:sum(SQs), DCQ}
end.
+
+json_to_prom(SystemJson) ->
+ SystemMap = jiffy:decode(SystemJson, [return_maps]),
+ SystemList = maps:to_list(SystemMap),
+ ListA = lists:map(fun({Key, Value}) ->
+ couch_to_prom(<<"couchdb">>, Key, Value)
+ end, SystemList),
+ lists:flatten(ListA).
+
+couch_to_prom(Prefix, Key, Value) when is_map(Value) ->
+ case maps:is_key(<<"value">>, Value) of
+ false ->
+ ValueList = maps:to_list(Value),
+ Prefix1 = list_to_binary(binary_to_list(Prefix) ++ "_" ++ binary_to_list(Key)),
+ lists:foldl(fun({K, V}, Results0) ->
+ NewResult = couch_to_prom(Prefix1, K, V),
+ [NewResult | Results0]
+ end, [], ValueList);
+ true ->
+ MV = maps:get(<<"value">>, Value),
+ MD = maps:get(<<"desc">>, Value),
+ Type = maps:get(<<"type">>, Value),
+ to_prom(Prefix, Key, Type, MV)
+ end;
+couch_to_prom(Prefix, Key, Value) ->
+ to_prom(Prefix, Key, Value).
+
+to_prom(Prefix, Metric, Value) ->
+ to_prom_list(Prefix, Metric, Value).
+
+to_prom(Prefix, Metric, <<"counter">>, Value) ->
+ to_prom_list(Prefix, Metric, Value);
+to_prom(Prefix, Metric, <<"gauge">>, Value) ->
+ to_prom_list(Prefix, Metric, Value);
+to_prom(Prefix, Metric, <<"histogram">>, Value) when is_map(Value) ->
+ maps:fold(fun (K, V, Acc) ->
+ case K of
+ <<"percentile">> ->
+ PN = ?l2b(?b2l(Metric) ++ "_percentile"),
+ Quantiles = lists:map(fun([Perc, Val]) ->
+ case Perc of
+ 50 -> to_prom_list(Prefix, PN, [{quantile, <<"0.5">>}], Val);
+ 75 -> to_prom_list(Prefix, PN, [{quantile, <<"0.75">>}], Val);
+ 90 -> to_prom_list(Prefix, PN, [{quantile, <<"0.9">>}], Val);
+ 95 -> to_prom_list(Prefix, PN, [{quantile, <<"0.95">>}], Val);
+ 99 -> to_prom_list(Prefix, PN, [{quantile, <<"0.99">>}], Val);
+ 999 -> to_prom_list(Prefix, PN, [{quantile, <<"0.999">>}], Val)
+ end
+ end, V),
+ [Quantiles, Acc];
+ _ ->
+ [to_prom_list(Prefix, ?l2b(?b2l(Metric) ++ "_" ++ K), V), Acc]
+ end
+ end, [], Value).
+
+
+to_prom_list(Prefix, Metric, Value) ->
+ [to_bin(io_lib:format("~s ~p", [to_prom_name(Prefix, Metric), Value]))].
+
+to_prom_list(Prefix, Metric, Label, Value) ->
+ [to_bin(io_lib:format("~s ~p", [to_prom_name(Prefix, Metric, Label), Value]))].
+
+to_prom_name(Prefix, Metric) ->
+ to_bin(io_lib:format("~s_~s", [Prefix, Metric])).
+
+to_prom_name(Prefix, Metric, Labels) ->
+ LabelParts = lists:map(fun({K, V}) ->
+ lists:flatten(io_lib:format("~s=\"~s\"", [to_bin(K), to_bin(V)]))
+ end, Labels),
+ case length(LabelParts) > 0 of
+ true ->
+ LabelStr = string:join(LabelParts, ", "),
+ to_bin(io_lib:format("~s_~s{~s}", [Prefix, Metric, LabelStr]));
+ false ->
+ to_bin(io_lib:format("~s_~s", [Prefix, Metric]))
+ end.
+
+to_bin(Data) when is_list(Data) ->
+ iolist_to_binary(Data);
+to_bin(Data) when is_atom(Data) ->
+ atom_to_binary(Data, utf8);
+to_bin(Data) when is_integer(Data) ->
+ integer_to_binary(Data);
+to_bin(Data) when is_binary(Data) ->
+ Data.