You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ch...@apache.org on 2023/01/17 17:41:27 UTC

[couchdb] branch add-report-logging created (now 9ce6bdc81)

This is an automated email from the ASF dual-hosted git repository.

chewbranca pushed a change to branch add-report-logging
in repository https://gitbox.apache.org/repos/asf/couchdb.git


      at 9ce6bdc81 Add measures logging

This branch includes the following new commits:

     new f6ea77301 Add couch_log:report/2 to log structured data
     new 9ce6bdc81 Add measures logging

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[couchdb] 01/02: Add couch_log:report/2 to log structured data

Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

chewbranca pushed a commit to branch add-report-logging
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit f6ea77301becd8c481a0f14e828eeeec7d1f1180
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Fri Sep 23 13:49:56 2022 -0700

    Add couch_log:report/2 to log structured data
---
 src/couch_log/include/couch_log.hrl                |  3 +-
 src/couch_log/src/couch_log.erl                    | 13 ++++-
 src/couch_log/src/couch_log_config.erl             |  3 ++
 src/couch_log/src/couch_log_config_dyn.erl         |  1 +
 src/couch_log/src/couch_log_formatter.erl          | 56 ++++++++++++++++++++--
 src/couch_log/src/couch_log_sup.erl                |  2 +
 src/couch_log/src/couch_log_util.erl               | 13 ++++-
 src/couch_log/src/couch_log_writer_file.erl        |  2 +-
 src/couch_log/src/couch_log_writer_syslog.erl      | 28 +++++++++--
 src/couch_log/test/eunit/couch_log_config_test.erl | 18 +++++++
 .../test/eunit/couch_log_formatter_test.erl        | 11 +++++
 src/couch_log/test/eunit/couch_log_writer_ets.erl  |  7 +--
 .../test/eunit/couch_log_writer_syslog_test.erl    | 42 ++++++++++++++++
 13 files changed, 182 insertions(+), 17 deletions(-)

diff --git a/src/couch_log/include/couch_log.hrl b/src/couch_log/include/couch_log.hrl
index fa544a88b..eabc9a7e3 100644
--- a/src/couch_log/include/couch_log.hrl
+++ b/src/couch_log/include/couch_log.hrl
@@ -15,7 +15,8 @@
     pid,
     msg,
     msg_id,
-    time_stamp
+    time_stamp,
+    type
 }).
 
 
diff --git a/src/couch_log/src/couch_log.erl b/src/couch_log/src/couch_log.erl
index b8a1ca4bd..d519082ce 100644
--- a/src/couch_log/src/couch_log.erl
+++ b/src/couch_log/src/couch_log.erl
@@ -21,7 +21,7 @@
     critical/2,
     alert/2,
     emergency/2,
-
+    report/2,
     set_level/1
 ]).
 
@@ -49,16 +49,25 @@ alert(Fmt, Args) -> log(alert, Fmt, Args).
 -spec emergency(string(), list()) -> ok.
 emergency(Fmt, Args) -> log(emergency, Fmt, Args).
 
+-spec report(string(), map()) -> ok.
+report(ReportId, Meta) when is_map(Meta) ->
+    couch_stats:increment_counter([couch_log, level, report]),
+    Entry = couch_log_formatter:format(report, self(), ReportId, "", [], Meta),
+    ok = couch_log_server:report(Entry).
+
 -spec set_level(atom() | string() | integer()) -> true.
 set_level(Level) ->
     config:set("log", "level", couch_log_util:level_to_string(Level)).
 
 -spec log(atom(), string(), list()) -> ok.
 log(Level, Fmt, Args) ->
+    log(Level, undefined, Fmt, Args, #{}).
+
+log(Level, Type, Fmt, Args, Meta) ->
     case couch_log_util:should_log(Level) of
         true ->
             couch_stats:increment_counter([couch_log, level, Level]),
-            Entry = couch_log_formatter:format(Level, self(), Fmt, Args),
+            Entry = couch_log_formatter:format(Level, self(), Type, Fmt, Args, Meta),
             ok = couch_log_server:log(Entry);
         false ->
             ok
diff --git a/src/couch_log/src/couch_log_config.erl b/src/couch_log/src/couch_log_config.erl
index 925973178..c3fd8fe85 100644
--- a/src/couch_log/src/couch_log_config.erl
+++ b/src/couch_log/src/couch_log_config.erl
@@ -43,6 +43,7 @@ entries() ->
     [
         {level, "level", "info"},
         {level_int, "level", "info"},
+        {report_level, "report_level", "info"},
         {max_message_size, "max_message_size", "16000"},
         {strip_last_msg, "strip_last_msg", "true"},
         {filter_fields, "filter_fields", "[pid, registered_name, error_info, messages]"}
@@ -87,6 +88,8 @@ forms() ->
 
 transform(level, LevelStr) ->
     couch_log_util:level_to_atom(LevelStr);
+transform(report_level, LevelStr) ->
+    couch_log_util:level_to_atom(LevelStr);
 transform(level_int, LevelStr) ->
     Level = couch_log_util:level_to_atom(LevelStr),
     couch_log_util:level_to_integer(Level);
diff --git a/src/couch_log/src/couch_log_config_dyn.erl b/src/couch_log/src/couch_log_config_dyn.erl
index ff781d3a0..1b1b3bd8e 100644
--- a/src/couch_log/src/couch_log_config_dyn.erl
+++ b/src/couch_log/src/couch_log_config_dyn.erl
@@ -22,6 +22,7 @@
 ]).
 
 get(level) -> info;
+get(report_level) -> info;
 get(level_int) -> 2;
 get(max_message_size) -> 16000;
 get(strip_last_msg) -> true;
diff --git a/src/couch_log/src/couch_log_formatter.erl b/src/couch_log/src/couch_log_formatter.erl
index 2ce0fba6d..e1110b6d0 100644
--- a/src/couch_log/src/couch_log_formatter.erl
+++ b/src/couch_log/src/couch_log_formatter.erl
@@ -17,6 +17,7 @@
 -module(couch_log_formatter).
 
 -export([
+    format/6,
     format/4,
     format/3,
     format/1,
@@ -32,12 +33,26 @@
 -define(DEFAULT_TRUNCATION, 1024).
 
 format(Level, Pid, Fmt, Args) ->
+    format(Level, Pid, undefined, Fmt, Args, #{}).
+
+format(report = Level, Pid, Type, Fmt, Args, Meta) ->
     #log_entry{
         level = couch_log_util:level_to_atom(Level),
         pid = Pid,
-        msg = maybe_truncate(Fmt, Args),
+        msg = maybe_truncate(Fmt, Args, Meta, false),
         msg_id = couch_log_util:get_msg_id(),
-        time_stamp = couch_log_util:iso8601_timestamp()
+        time_stamp = couch_log_util:iso8601_timestamp(),
+        type = Type
+    };
+
+format(Level, Pid, Type, Fmt, Args, Meta) ->
+    #log_entry{
+        level = couch_log_util:level_to_atom(Level),
+        pid = Pid,
+        msg = maybe_truncate(Fmt, Args, Meta, true),
+        msg_id = couch_log_util:get_msg_id(),
+        time_stamp = couch_log_util:iso8601_timestamp(),
+        type = Type
     }.
 
 format(Level, Pid, Msg) ->
@@ -365,9 +380,16 @@ format_args([H | T], FmtAcc, ArgsAcc) ->
     {Str, _} = couch_log_trunc_io:print(H, 100),
     format_args(T, ["~s" | FmtAcc], [Str | ArgsAcc]).
 
-maybe_truncate(Fmt, Args) ->
+maybe_truncate(Fmt, Args, Meta, TruncateMeta) ->
     MaxMsgSize = couch_log_config:get(max_message_size),
-    couch_log_trunc_io:format(Fmt, Args, MaxMsgSize).
+    case format_meta(Meta) of
+        "" ->
+            couch_log_trunc_io:format(Fmt, Args, MaxMsgSize);
+        MetaStr when TruncateMeta ->
+            couch_log_trunc_io:format(["[", MetaStr, "] " | Fmt], Args, MaxMsgSize);
+        MetaStr ->
+            ["[", MetaStr, "] " | couch_log_trunc_io:format(Fmt, Args, MaxMsgSize)]
+    end.
 
 maybe_truncate(Msg) ->
     MaxMsgSize = couch_log_config:get(max_message_size),
@@ -436,3 +458,29 @@ get_value(Key, List, Default) ->
 
 supervisor_name({local, Name}) -> Name;
 supervisor_name(Name) -> Name.
+
+format_meta(Meta) ->
+    %% https://www.rfc-editor.org/rfc/rfc5424.html#section-6.3
+    %% iut="3" eventSource="Application" eventID="1011"
+    string:join(maps:fold(fun(K, V, Acc) ->
+        [to_str(K, V) | Acc]
+    end, [], Meta), " ").
+
+%% passing complex terms as meta value is a mistake so we are going
+%% to eat it, because we cannot bubble up errors from logger
+%% Therefore following are not supported
+%% - lists
+%% - tuples
+%% - maps
+%% However we are not going to try to distinguish lists from string
+%% Atoms would be printed as strings
+to_str(K, _) when not (is_list(K) or is_atom(K)) ->
+    "";
+to_str(K, Term) when is_list(Term) ->
+    io_lib:format("~s=\"~s\"", [K, Term]);
+to_str(_K, Term) when is_tuple(Term) ->
+    "";
+to_str(_K, Term) when is_map(Term) ->
+    "";
+to_str(K, Term) ->
+    io_lib:format("~s=\"~p\"", [K, Term]).
diff --git a/src/couch_log/src/couch_log_sup.erl b/src/couch_log/src/couch_log_sup.erl
index 0167192d8..a7c581ebd 100644
--- a/src/couch_log/src/couch_log_sup.erl
+++ b/src/couch_log/src/couch_log_sup.erl
@@ -59,6 +59,8 @@ handle_config_change("log", Key, _, _, S) ->
     case Key of
         "level" ->
             couch_log_config:reconfigure();
+        "report_level" ->
+            couch_log_config:reconfigure();
         "max_message_size" ->
             couch_log_config:reconfigure();
         "strip_last_msg" ->
diff --git a/src/couch_log/src/couch_log_util.erl b/src/couch_log/src/couch_log_util.erl
index 8be11e12d..ae3b68df2 100644
--- a/src/couch_log/src/couch_log_util.erl
+++ b/src/couch_log/src/couch_log_util.erl
@@ -21,7 +21,9 @@
     level_to_atom/1,
     level_to_string/1,
 
-    string_p/1
+    string_p/1,
+
+    maybe_format_type/1
 ]).
 
 -include("couch_log.hrl").
@@ -29,6 +31,8 @@
 -spec should_log(#log_entry{} | atom()) -> boolean().
 should_log(#log_entry{level = Level}) ->
     should_log(Level);
+should_log(report) ->
+    should_log(couch_log_config:get(report_level));
 should_log(Level) ->
     level_to_integer(Level) >= couch_log_config:get(level_int).
 
@@ -145,3 +149,10 @@ string_p1([]) ->
     true;
 string_p1(_) ->
     false.
+
+maybe_format_type(#log_entry{type = undefined} = Entry) ->
+    Entry;
+maybe_format_type(#log_entry{type = Type, msg = [$[ | Msg]} = Entry) ->
+    Entry#log_entry{msg = ["[", Type, " " | Msg]};
+maybe_format_type(#log_entry{} = Entry) ->
+    Entry.
\ No newline at end of file
diff --git a/src/couch_log/src/couch_log_writer_file.erl b/src/couch_log/src/couch_log_writer_file.erl
index 9b7255050..7fd92bd90 100644
--- a/src/couch_log/src/couch_log_writer_file.erl
+++ b/src/couch_log/src/couch_log_writer_file.erl
@@ -77,7 +77,7 @@ write(Entry, St) ->
         msg = Msg,
         msg_id = MsgId,
         time_stamp = TimeStamp
-    } = Entry,
+    } = couch_log_util:maybe_format_type(Entry),
     Fmt = "[~s] ~s ~s ~p ~s ",
     Args = [
         couch_log_util:level_to_string(Level),
diff --git a/src/couch_log/src/couch_log_writer_syslog.erl b/src/couch_log/src/couch_log_writer_syslog.erl
index b95cf018c..d99550bde 100644
--- a/src/couch_log/src/couch_log_writer_syslog.erl
+++ b/src/couch_log/src/couch_log_writer_syslog.erl
@@ -28,7 +28,9 @@
     hostname,
     os_pid,
     appid,
-    facility
+    facility,
+    report_level,
+    enterprise_number
 }).
 
 -define(SYSLOG_VERSION, 1).
@@ -55,7 +57,7 @@ init() ->
                         undefined
                 end
         end,
-
+    Level = list_to_atom(config:get("log", "syslog_report_level", "info")),
     {ok, #st{
         socket = Socket,
         host = Host,
@@ -63,20 +65,36 @@ init() ->
         hostname = net_adm:localhost(),
         os_pid = os:getpid(),
         appid = config:get("log", "syslog_appid", "couchdb"),
-        facility = get_facility(config:get("log", "syslog_facility", "local2"))
+        facility = get_facility(config:get("log", "syslog_facility", "local2")),
+        report_level = Level,
+        enterprise_number = config:get("log", "syslog_enterprise_number")
     }}.
 
 terminate(_Reason, St) ->
     gen_udp:close(St#st.socket).
 
-write(Entry, St) ->
+write(#log_entry{level = report} = Entry, #st{enterprise_number = undefined} = St) ->
+    do_write(Entry#log_entry{level = error}, St);
+write(#log_entry{level = report, type = Type} = Entry0, #st{report_level = Level} = St) ->
+    % append @${enterprise_number} to the type to conform with
+    % https://www.rfc-editor.org/rfc/rfc5424.html#page-15
+    TypeSDID = lists:flatten(io_lib:format("~s@~s", [Type, St#st.enterprise_number])),
+    Entry = Entry0#log_entry{
+        type = TypeSDID,
+        level = Level
+    },
+    do_write(Entry, St);
+write(#log_entry{} = Entry, #st{} = St) ->
+    do_write(Entry, St).
+
+do_write(Entry, St) ->
     #log_entry{
         level = Level,
         pid = Pid,
         msg = Msg,
         msg_id = MsgId,
         time_stamp = TimeStamp
-    } = Entry,
+    } = couch_log_util:maybe_format_type(Entry),
     Fmt = "<~B>~B ~s ~s ~s ~p ~s - ",
     Args = [
         St#st.facility bor get_level(Level),
diff --git a/src/couch_log/test/eunit/couch_log_config_test.erl b/src/couch_log/test/eunit/couch_log_config_test.erl
index df7cdf977..3cf56b003 100644
--- a/src/couch_log/test/eunit/couch_log_config_test.erl
+++ b/src/couch_log/test/eunit/couch_log_config_test.erl
@@ -20,6 +20,7 @@
 couch_log_config_test_() ->
     {setup, fun couch_log_test_util:start/0, fun couch_log_test_util:stop/1, [
         ?T(check_level),
+        ?T(check_report_level),
         ?T(check_max_message_size),
         ?T(check_bad_level),
         ?T(check_bad_max_message_size),
@@ -29,6 +30,23 @@ couch_log_config_test_() ->
         ?T(check_bad_filter_fields)
     ]}.
 
+check_report_level() ->
+    % Default report_level is info
+    ?assertEqual(info, couch_log_config:get(report_level)),
+    couch_log_test_util:with_config_listener(fun() ->
+        config:set("log", "report_level", "emerg"),
+        couch_log_test_util:wait_for_config(),
+        ?assertEqual(emergency, couch_log_config:get(report_level)),
+
+        config:set("log", "report_level", "debug"),
+        couch_log_test_util:wait_for_config(),
+        ?assertEqual(debug, couch_log_config:get(report_level)),
+
+        config:delete("log", "report_level"),
+        couch_log_test_util:wait_for_config(),
+        ?assertEqual(info, couch_log_config:get(report_level))
+    end).
+
 check_level() ->
     % Default level is info
     ?assertEqual(info, couch_log_config:get(level)),
diff --git a/src/couch_log/test/eunit/couch_log_formatter_test.erl b/src/couch_log/test/eunit/couch_log_formatter_test.erl
index a4de74990..00a1a2c96 100644
--- a/src/couch_log/test/eunit/couch_log_formatter_test.erl
+++ b/src/couch_log/test/eunit/couch_log_formatter_test.erl
@@ -25,6 +25,17 @@ truncate_test() ->
     Entry = couch_log_formatter:format(info, self(), Msg),
     ?assert(length(Entry#log_entry.msg) =< 16000).
 
+format_report_test() ->
+    MsgFmt = "This is a reason: ~r",
+    Reason = {foo, [{x, k, 3}, {c, d, 2}]},
+    Entry = couch_log_formatter:format(report, self(), report123, MsgFmt, [Reason], #{
+        foo => 123,
+        bar => "barStr",
+        baz => baz
+    }),
+    Formatted = "[foo=\"123\" baz=\"baz\" bar=\"barStr\"] This is a reason: foo at x:k/3 <= c:d/2",
+    ?assertEqual(Formatted, lists:flatten(Entry#log_entry.msg)).
+
 format_reason_test() ->
     MsgFmt = "This is a reason: ~r",
     Reason = {foo, [{x, k, 3}, {c, d, 2}]},
diff --git a/src/couch_log/test/eunit/couch_log_writer_ets.erl b/src/couch_log/test/eunit/couch_log_writer_ets.erl
index 7ddb9f39e..385aa9676 100644
--- a/src/couch_log/test/eunit/couch_log_writer_ets.erl
+++ b/src/couch_log/test/eunit/couch_log_writer_ets.erl
@@ -30,9 +30,10 @@ terminate(_, _St) ->
     ok.
 
 write(Entry0, St) ->
-    Entry = Entry0#log_entry{
-        msg = lists:flatten(Entry0#log_entry.msg),
-        time_stamp = lists:flatten(Entry0#log_entry.time_stamp)
+    Entry1 = couch_log_util:maybe_format_type(Entry0),
+    Entry = Entry1#log_entry{
+        msg = lists:flatten(Entry1#log_entry.msg),
+        time_stamp = lists:flatten(Entry1#log_entry.time_stamp)
     },
     Ignored = application:get_env(couch_log, ignored_pids, []),
     case lists:member(Entry#log_entry.pid, Ignored) of
diff --git a/src/couch_log/test/eunit/couch_log_writer_syslog_test.erl b/src/couch_log/test/eunit/couch_log_writer_syslog_test.erl
index 5a3f89520..bfac2bc92 100644
--- a/src/couch_log/test/eunit/couch_log_writer_syslog_test.erl
+++ b/src/couch_log/test/eunit/couch_log_writer_syslog_test.erl
@@ -31,6 +31,12 @@ couch_log_writer_syslog_test_() ->
                 [{gen_udp, [unstick]}],
                 fun check_udp_send/0
             )
+        end,
+        fun() ->
+            couch_log_test_util:with_meck(
+                [{gen_udp, [unstick]}],
+                fun check_format/0
+            )
         end
     ]}.
 
@@ -81,6 +87,42 @@ check_udp_send() ->
     ?assert(meck:called(gen_udp, close, 1)),
     ?assert(meck:validate(gen_udp)).
 
+check_format() ->
+    meck:expect(gen_udp, open, 1, {ok, socket}),
+    meck:expect(gen_udp, send, 4, ok),
+    meck:expect(gen_udp, close, fun(socket) -> ok end),
+    config:set("log", "syslog_host", "localhost"),
+    config:set("log", "syslog_enterprise_number", "12345"),
+    try
+        Entry = #log_entry{
+            level = report,
+            pid = list_to_pid("<0.1.0>"),
+            msg = "[foo=1] stuff",
+            msg_id = "msg_id",
+            time_stamp = "time_stamp",
+            type = report123
+        },
+        {ok, St} = ?WRITER:init(),
+        {ok, NewSt} = ?WRITER:write(Entry, St),
+        ok = ?WRITER:terminate(stop, NewSt)
+    after
+        config:delete("log", "syslog_host")
+    end,
+
+    ?assert(meck:called(gen_udp, open, 1)),
+    Packet = lists:flatten(meck:capture(first, gen_udp, send, '_', 4)),
+    [SeverityAndVsn, TS, _Host, AppId, Pid, MsgId, _ | Rest] = string:split(Packet, " ", all),
+    ?assertEqual("<150>1", SeverityAndVsn),
+    ?assertEqual("time_stamp", TS),
+    ?assertEqual("couchdb", AppId),
+    ?assertEqual("msg_id", MsgId),
+    ?assert(is_pid(catch list_to_pid(Pid))),
+    ?assertEqual("[report123@12345 foo=1] stuff\n", string:join(Rest, " ")),
+    ?assert(meck:called(gen_udp, close, 1)),
+    ?assert(meck:validate(gen_udp)).
+
+
+
 facility_test() ->
     Names = [
         "kern",


[couchdb] 02/02: Add measures logging

Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

chewbranca pushed a commit to branch add-report-logging
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 9ce6bdc81a5958484e5dcd53e1ed9373d557faf0
Author: Russell Branca <ch...@apache.org>
AuthorDate: Tue Jan 17 09:41:14 2023 -0800

    Add measures logging
---
 src/couch_log/priv/stats_descriptions.cfg |  4 ++++
 src/couch_log/src/couch_log.erl           | 11 ++++++-----
 src/mango/src/mango_cursor_view.erl       |  1 +
 src/mango/src/mango_execution_stats.erl   | 11 +++++++++++
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/src/couch_log/priv/stats_descriptions.cfg b/src/couch_log/priv/stats_descriptions.cfg
index 31e41614b..febb5e057 100644
--- a/src/couch_log/priv/stats_descriptions.cfg
+++ b/src/couch_log/priv/stats_descriptions.cfg
@@ -46,3 +46,7 @@
     {type, counter},
     {desc, <<"number of logged warning messages">>}
 ]}.
+{[couch_log, level, report], [
+    {type, counter},
+    {desc, <<"number of logged report messages">>}
+]}.
diff --git a/src/couch_log/src/couch_log.erl b/src/couch_log/src/couch_log.erl
index d519082ce..3d198994c 100644
--- a/src/couch_log/src/couch_log.erl
+++ b/src/couch_log/src/couch_log.erl
@@ -21,7 +21,7 @@
     critical/2,
     alert/2,
     emergency/2,
-    report/2,
+    report/4,
     set_level/1
 ]).
 
@@ -49,11 +49,12 @@ alert(Fmt, Args) -> log(alert, Fmt, Args).
 -spec emergency(string(), list()) -> ok.
 emergency(Fmt, Args) -> log(emergency, Fmt, Args).
 
--spec report(string(), map()) -> ok.
-report(ReportId, Meta) when is_map(Meta) ->
+-spec report(string(), string(), list(), map()) -> ok.
+report(ReportId, Fmt, Args, Meta) when is_map(Meta) ->
     couch_stats:increment_counter([couch_log, level, report]),
-    Entry = couch_log_formatter:format(report, self(), ReportId, "", [], Meta),
-    ok = couch_log_server:report(Entry).
+    Entry = couch_log_formatter:format(report, self(), ReportId, Fmt, Args, Meta),
+    %%ok = couch_log_server:report(Entry).
+    ok = couch_log_server:log(Entry).
 
 -spec set_level(atom() | string() | integer()) -> true.
 set_level(Level) ->
diff --git a/src/mango/src/mango_cursor_view.erl b/src/mango/src/mango_cursor_view.erl
index 9202ce071..9bca19cee 100644
--- a/src/mango/src/mango_cursor_view.erl
+++ b/src/mango/src/mango_cursor_view.erl
@@ -159,6 +159,7 @@ execute(#cursor{db = Db, index = Idx, execution_stats = Stats} = Cursor0, UserFu
                     Arg = {add_key, bookmark, NewBookmark},
                     {_Go, FinalUserAcc} = UserFun(Arg, LastCursor#cursor.user_acc),
                     Stats0 = LastCursor#cursor.execution_stats,
+                    mango_execution_stats:log_stats(Stats0),
                     FinalUserAcc0 = mango_execution_stats:maybe_add_stats(
                         Opts, UserFun, Stats0, FinalUserAcc
                     ),
diff --git a/src/mango/src/mango_execution_stats.erl b/src/mango/src/mango_execution_stats.erl
index 0db3edf5f..2752f5a71 100644
--- a/src/mango/src/mango_execution_stats.erl
+++ b/src/mango/src/mango_execution_stats.erl
@@ -21,6 +21,7 @@
     incr_results_returned/1,
     log_start/1,
     log_end/1,
+    log_stats/1,
     maybe_add_stats/4
 ]).
 
@@ -74,6 +75,9 @@ log_end(Stats) ->
 maybe_add_stats(Opts, UserFun, Stats0, UserAcc) ->
     Stats1 = log_end(Stats0),
     couch_stats:update_histogram([mango, query_time], Stats1#execution_stats.executionTimeMs),
+    %% TODO: validate rows/reads assignments
+    chttpd_stats:incr_rows(Stats1#execution_stats.totalDocsExamined),
+    chttpd_stats:incr_reads(Stats1#execution_stats.totalQuorumDocsExamined),
 
     case couch_util:get_value(execution_stats, Opts) of
         true ->
@@ -84,3 +88,10 @@ maybe_add_stats(Opts, UserFun, Stats0, UserAcc) ->
         _ ->
             UserAcc
     end.
+
+log_stats(Stats) ->
+    {JStats0} = to_json(Stats),
+    Nonce = list_to_binary(couch_log_util:get_msg_id()),
+    JStats = {[{<<"nonce">>, Nonce} | JStats0]},
+	%% TODO: switch to report
+    couch_log:report("FIXME-reportid", "[ASDFMARKER] GOT MANGO EXEC STATS: ~s", [binary_to_list(jiffy:encode(JStats))], #{}).