blob: 47b28300835b0f26a5802bc80ccb6d07c5430378 [file] [log] [blame]
% Licensed under the Apache License, Version 2.0 (the "License"); you may not
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
% License for the specific language governing permissions and limitations under
% the License.
-module(chttpd_stats).
-include_lib("kernel/include/logger.hrl").
-export([
init/1,
report/1,
incr_reads/0,
incr_reads/1,
incr_writes/0,
incr_writes/1,
incr_rows/0,
incr_rows/1,
update_interval/1
]).
-record(st, {
reads = 0,
writes = 0,
rows = 0,
reporter,
last_report_ts = 0,
interval,
request
}).
-define(KEY, chttpd_stats).
-define(INTERVAL_IN_SEC, 60).
init(Request) ->
Reporter = config:get("chttpd", "stats_reporter"),
Time = erlang:monotonic_time(second),
Interval = config:get_integer(
"chttpd",
"stats_reporting_interval",
?INTERVAL_IN_SEC
),
put(?KEY, #st{
reporter = Reporter,
last_report_ts = Time,
interval = Interval,
request = Request
}).
report(HttpResp) ->
try
case get(?KEY) of
#st{} = St ->
report(HttpResp, St);
_ ->
ok
end
catch
T:R:S ->
?LOG_ERROR(#{
what => stats_report_failure,
tag => T,
details => R,
stacktrace => S
}),
Fmt = "Failed to report chttpd request stats: ~p:~p ~p",
couch_log:error(Fmt, [T, R, S])
end.
report(_HttpResp, #st{reporter = undefined}) ->
ok;
report(HttpResp, #st{reporter = Reporter} = St) ->
Mod = list_to_existing_atom(Reporter),
#st{
reads = Reads,
writes = Writes,
rows = Rows,
request = HttpReq
} = St,
Mod:report(HttpReq, HttpResp, Reads, Writes, Rows).
incr_reads() ->
incr(#st.reads, 1).
incr_reads(N) when is_integer(N), N >= 0 ->
incr(#st.reads, N).
incr_writes() ->
incr(#st.writes, 1).
incr_writes(N) when is_integer(N), N >= 0 ->
incr(#st.writes, N).
incr_rows() ->
incr(#st.rows, 1).
incr_rows(N) when is_integer(N), N >= 0 ->
incr(#st.rows, N).
incr(Idx, Count) ->
case get(?KEY) of
#st{} = St ->
Total = element(Idx, St) + Count,
NewSt = setelement(Idx, St, Total),
put(?KEY, NewSt),
maybe_report_intermittent(St);
_ ->
ok
end.
maybe_report_intermittent(State) ->
#st{last_report_ts = LastTime, interval = Interval} = State,
CurrentTime = erlang:monotonic_time(second),
case CurrentTime - LastTime of
Change when Change >= Interval ->
% Since response is not available during the request, we set
% this undefined. Modules that call:
% Mod:report(HttpReq, HttpResp, Reads, Writes, Rows) should
% be aware of this. Mod:report should also return a boolean
% to indicate if reset should occur
case ?MODULE:report(undefined) of
true ->
reset_stats(State, CurrentTime);
_ ->
ok
end;
_ ->
ok
end.
update_interval(Interval) ->
case get(?KEY) of
#st{} = St ->
put(?KEY, St#st{interval = Interval});
_ ->
ok
end.
reset_stats(State, NewTime) ->
put(?KEY, State#st{
reads = 0,
writes = 0,
rows = 0,
last_report_ts = NewTime
}).