Permalink
Browse files

Adding more metrics, as per issue #1

- Adding garbage collection count per interval
- Adding words reclaimed in garbage collections per interval
- Adding reduction increment count per interval
- Adding IO data (bytes in and out) per interval
  • Loading branch information...
1 parent 365896d commit 0fa940d70b78d991c3431d415dd84c5dbe6b2fcf @ferd committed Nov 13, 2012
Showing with 70 additions and 9 deletions.
  1. +9 −0 README.markdown
  2. +1 −1 src/vmstats.app.src
  3. +43 −7 src/vmstats_server.erl
  4. +7 −1 test/statsderl.erl
  5. +10 −0 test/vmstats_server_tests.erl
View
@@ -30,3 +30,12 @@ Scheduler wall time statistics are now disabled by default to keep in line with
## I was basing myself on 'master' and stuff started breaking!
You are likely using vmstats with an Erlang release prior to R15B. Switch away from master and use the tag "0.1.0" to get back to the functionning version you knew.
+
+## CHANGELOG ##
+
+### 0.2.2 ###
+
+- Adding garbage collection count per interval
+- Adding words reclaimed in garbage collections per interval
+- Adding reduction increment count per interval
+- Adding IO data (bytes in and out) per interval
View
@@ -1,6 +1,6 @@
{application, vmstats, [
{description, "Tiny application to gather VM statistics for StatsD client"},
- {vsn, "0.2.1"},
+ {vsn, "0.2.2"},
{registered, [vmstats_sup, vmstats_server]},
{applications, [
kernel,
View
@@ -15,8 +15,9 @@
sched_time :: enabled | disabled | unavailable,
prev_sched :: [{integer(), integer(), integer()}],
timer_ref :: reference(),
- delay :: integer()}). % milliseconds
-
+ delay :: integer(), % milliseconds
+ prev_io :: {In::integer(), Out::integer()},
+ prev_gc :: {GCs::integer(), Words::integer(), 0}}).
%%% INTERFACE
%% the base key is passed from the supervisor. This function
%% should not be called manually.
@@ -27,23 +28,31 @@ start_link(BaseKey) ->
init(BaseKey) ->
{ok, Delay} = application:get_env(vmstats, delay),
Ref = erlang:start_timer(Delay, self(), ?TIMER_MSG),
+ {{input,In},{output,Out}} = erlang:statistics(io),
+ PrevGC = erlang:statistics(garbage_collection),
case {sched_time_available(), application:get_env(vmstats, sched_time)} of
{true, {ok,true}} ->
{ok, #state{key = [BaseKey,$.],
timer_ref = Ref,
delay = Delay,
sched_time = enabled,
- prev_sched = lists:sort(erlang:statistics(scheduler_wall_time))}};
+ prev_sched = lists:sort(erlang:statistics(scheduler_wall_time)),
+ prev_io = {In,Out},
+ prev_gc = PrevGC}};
{true, _} ->
{ok, #state{key = [BaseKey,$.],
timer_ref = Ref,
delay = Delay,
- sched_time = disabled}};
+ sched_time = disabled,
+ prev_io = {In,Out},
+ prev_gc = PrevGC}};
{false, _} ->
{ok, #state{key = [BaseKey,$.],
timer_ref = Ref,
delay = Delay,
- sched_time = unavailable}}
+ sched_time = unavailable,
+ prev_io = {In,Out},
+ prev_gc = PrevGC}}
end.
handle_call(_Msg, _From, State) ->
@@ -77,6 +86,20 @@ handle_info({timeout, R, ?TIMER_MSG}, S = #state{key=K, delay=D, timer_ref=R}) -
statsderl:gauge([K2,"binary"], proplists:get_value(binary, Mem), 1.00),
statsderl:gauge([K2,"ets"], proplists:get_value(ets, Mem), 1.00),
+ %% Incremental values
+ #state{prev_io={OldIn,OldOut}, prev_gc={OldGCs,OldWords,_}} = S,
+ {{input,In},{output,Out}} = erlang:statistics(io),
+ GC = {GCs, Words, _} = erlang:statistics(garbage_collection),
+
+ statsderl:increment([K,"io.bytes_in"], In-OldIn, 1.00),
+ statsderl:increment([K,"io.bytes_out"], Out-OldOut, 1.00),
+ statsderl:increment([K,"gc.count"], GCs-OldGCs, 1.00),
+ statsderl:increment([K,"gc.words_reclaimed"], Words-OldWords, 1.00),
+
+ %% Reductions across the VM, excluding current time slice, already incremental
+ {_, Reds} = erlang:statistics(reductions),
+ statsderl:increment([K,"reductions"], Reds, 1.00),
+
%% Scheduler wall time
#state{sched_time=Sched, prev_sched=PrevSched} = S,
case Sched of
@@ -89,12 +112,25 @@ handle_info({timeout, R, ?TIMER_MSG}, S = #state{key=K, delay=D, timer_ref=R}) -
end
|| {Sid, Active, Total} <- wall_time_diff(PrevSched, NewSched)],
{noreply, S#state{timer_ref=erlang:start_timer(D, self(), ?TIMER_MSG),
- prev_sched=NewSched}};
+ prev_sched=NewSched, prev_io={In,Out}, prev_gc=GC}};
_ -> % disabled or unavailable
- {noreply, S#state{timer_ref=erlang:start_timer(D, self(), ?TIMER_MSG)}}
+ {noreply, S#state{timer_ref=erlang:start_timer(D, self(), ?TIMER_MSG),
+ prev_io={In,Out}, prev_gc=GC}}
end;
handle_info(_Msg, {state, _Key, _TimerRef, _Delay}) ->
exit(forced_upgrade_restart);
+handle_info(_Msg, {state, _Key, SchedTime, _PrevSched, _TimerRef, _Delay}) ->
+ %% The older version may have had the scheduler time enabled by default.
+ %% We could check for settings and preserve it in memory, but then it would
+ %% be more confusing if the behaviour changes on the next restart.
+ %% Instead, we show a warning and restart as usual.
+ case {application:get_env(vmstats, sched_time), SchedTime} of
+ {undefined, active} -> % default was on
+ error_logger:warning_msg("vmstats no longer runs scheduler time by default. Restarting...");
+ _ ->
+ ok
+ end,
+ exit(forced_upgrade_restart);
handle_info(_Msg, State) ->
{noreply, State}.
View
@@ -1,9 +1,12 @@
-module(statsderl).
--export([start_link/0, gauge/3, called/0, stop/0]).
+-export([start_link/0, increment/3, gauge/3, called/0, stop/0]).
start_link() ->
spawn_link(fun() -> init() end).
+increment(Key, Value, SampleRate) ->
+ call({increment, Key, Value, SampleRate}).
+
gauge(Key, Value, SampleRate) ->
call({gauge, Key, Value, SampleRate}).
@@ -17,6 +20,9 @@ init() ->
loop(Stack) ->
receive
+ {From, {increment, K, D, F}} ->
+ reply(From, ok),
+ loop([{K,D,F}|Stack]);
{From, {gauge, K, D, F}} ->
reply(From, ok),
loop([{K,D,F}|Stack]);
@@ -15,6 +15,10 @@ timer_500ms_test() ->
%% First match works
?assertMatch(
[{"key.error_logger_queue_len", _, 1.00},
+ {"key.gc.count",_,1.00},
+ {"key.gc.words_reclaimed",_,1.00},
+ {"key.io.bytes_in",_,1.00},
+ {"key.io.bytes_out",_,1.00},
{"key.memory.atom_used", _, 1.00},
{"key.memory.binary", _, 1.00},
{"key.memory.ets", _, 1.00},
@@ -23,6 +27,7 @@ timer_500ms_test() ->
{"key.modules", _, 1.00},
{"key.proc_count", _, 1.00},
{"key.proc_limit", _, 1.00},
+ {"key.reductions", _, 1.00},
{"key.run_queue", _, 1.00}],
lists:sort([{lists:flatten(K), V, Freq} || {K, V, Freq} <- statsderl:called()])
),
@@ -31,6 +36,10 @@ timer_500ms_test() ->
%% Done, we know it loops!
?assertMatch(
[{"key.error_logger_queue_len", _, 1.00},
+ {"key.gc.count",_,1.00},
+ {"key.gc.words_reclaimed",_,1.00},
+ {"key.io.bytes_in",_,1.00},
+ {"key.io.bytes_out",_,1.00},
{"key.memory.atom_used", _, 1.00},
{"key.memory.binary", _, 1.00},
{"key.memory.ets", _, 1.00},
@@ -39,6 +48,7 @@ timer_500ms_test() ->
{"key.modules", _, 1.00},
{"key.proc_count", _, 1.00},
{"key.proc_limit", _, 1.00},
+ {"key.reductions", _, 1.00},
{"key.run_queue", _, 1.00}],
lists:sort([{lists:flatten(K), V, Freq} || {K, V, Freq} <- statsderl:called()])
),

0 comments on commit 0fa940d

Please sign in to comment.