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.