-module(beam_stats_consumer_statsd).
-include("include/beam_stats.hrl").
+-include("beam_stats_logging.hrl").
-behaviour(beam_stats_consumer).
, terminate/1
]).
-%% Tests (to be run by CT):
--export(
- [ ct_test__beam_stats_to_bins/1
- , ct_test__memory_component_to_statsd_msg/1
- , ct_test__statsd_msg_add_name_prefix/1
- , ct_test__statsd_msg_to_bin/1
- , ct_test__node_id_to_bin/1
- ]).
-
-type option() ::
- {consumption_interval , erlang:time()}
+ {consumption_interval , non_neg_integer()}
| {dst_host , inet:ip_address() | inet:hostname()}
| {dst_port , inet:port_number()}
| {src_port , inet:port_number()}
%% ============================================================================
-spec init([option()]) ->
- {erlang:time(), state()}.
+ {non_neg_integer(), state()}.
init(Options) ->
ConsumptionInterval = hope_kv_list:get(Options, consumption_interval, 60000),
DstHost = hope_kv_list:get(Options, dst_host, ?DEFAULT_DST_HOST),
-spec try_to_send(state(), binary()) ->
state().
try_to_send(#state{sock=none}=State, _) ->
- io:format("error: socket closed~n"),
+ ?log_error("Sending failed. No socket in state."),
% TODO: Maybe schedule retry?
State;
try_to_send(
of ok ->
State
; {error, _}=Error ->
- io:format("error: gen_udp:send/4 failed: ~p~n", [Error]),
+ ?log_error(
+ "gen_udp:send(~p, ~p, ~p, ~p) -> ~p",
+ [Sock, DstHost, DstPort, Error]
+ ),
% TODO: Do something with unsent messages?
ok = gen_udp:close(Sock),
State#state{sock=none}
of {ok, Sock} ->
State#state{sock = {some, Sock}}
; {error, _}=Error ->
- io:format("error: gen_udp:open/1 failed: ~p~n", [Error]),
+ ?log_error("gen_udp:open(~p) -> ~p", [SrcPort, Error]),
State#state{sock = none}
end.
beam_stats_to_bins(#beam_stats
{ node_id = NodeID
, memory = Memory
+ , io_bytes_in = IOBytesIn
+ , io_bytes_out = IOBytesOut
+ , context_switches = ContextSwitches
+ , reductions = Reductions
+ , run_queue = RunQueue
}
) ->
NodeIDBin = node_id_to_bin(NodeID),
- Msgs1 = memory_to_msgs(Memory),
+ Msgs1 =
+ [ io_bytes_in_to_msg(IOBytesIn)
+ , io_bytes_out_to_msg(IOBytesOut)
+ , context_switches_to_msg(ContextSwitches)
+ , reductions_to_msg(Reductions)
+ , run_queue_to_msg(RunQueue)
+ | memory_to_msgs(Memory)
+ ],
Msgs2 = [statsd_msg_add_name_prefix(M, NodeIDBin) || M <- Msgs1],
[statsd_msg_to_bin(M) || M <- Msgs2].
+-spec run_queue_to_msg(non_neg_integer()) ->
+ statsd_msg().
+run_queue_to_msg(RunQueue) ->
+ #statsd_msg
+ { name = <<"run_queue">>
+ , value = RunQueue
+ , type = gauge
+ }.
+
+-spec reductions_to_msg(non_neg_integer()) ->
+ statsd_msg().
+reductions_to_msg(Reductions) ->
+ #statsd_msg
+ { name = <<"reductions">>
+ , value = Reductions
+ , type = gauge
+ }.
+
+-spec context_switches_to_msg(non_neg_integer()) ->
+ statsd_msg().
+context_switches_to_msg(ContextSwitches) ->
+ #statsd_msg
+ { name = <<"context_switches">>
+ , value = ContextSwitches
+ , type = gauge
+ }.
+
+-spec io_bytes_in_to_msg(non_neg_integer()) ->
+ statsd_msg().
+io_bytes_in_to_msg(IOBytesIn) ->
+ #statsd_msg
+ { name = <<"io.bytes_in">>
+ , value = IOBytesIn
+ , type = gauge
+ }.
+
+-spec io_bytes_out_to_msg(non_neg_integer()) ->
+ statsd_msg().
+io_bytes_out_to_msg(IOBytesOut) ->
+ #statsd_msg
+ { name = <<"io.bytes_out">>
+ , value = IOBytesOut
+ , type = gauge
+ }.
+
-spec memory_to_msgs([{atom(), non_neg_integer()}]) ->
[statsd_msg()].
memory_to_msgs(Memory) ->
-spec memory_component_to_statsd_msg({atom(), non_neg_integer()}) ->
statsd_msg().
memory_component_to_statsd_msg({MemType, MemSize}) when MemSize >= 0 ->
+ MemTypeBin = atom_to_binary(MemType, latin1),
#statsd_msg
- { name = atom_to_binary(MemType, latin1)
+ { name = <<"memory.", MemTypeBin/binary>>
, value = MemSize
, type = gauge
}.
node_id_to_bin(NodeID) ->
NodeIDBin = atom_to_binary(NodeID, utf8),
re:replace(NodeIDBin, "[\@\.]", "_", [global, {return, binary}]).
-
-%% ============================================================================
-%% Tests
-%% ============================================================================
-
-ct_test__beam_stats_to_bins(_Cfg) ->
- BEAMStats = #beam_stats{node_id = 'node@host.local', memory = [{foo,1}]},
- [<<?PATH_PREFIX, ".node_host_local.foo:1|g\n">>] =
- beam_stats_to_bins(BEAMStats).
-
-ct_test__memory_component_to_statsd_msg(_Cfg) ->
- #statsd_msg{name = <<"foo">>, value = 1, type = gauge} =
- memory_component_to_statsd_msg({foo, 1}).
-
-ct_test__statsd_msg_add_name_prefix(_Cfg) ->
- Msg1 = #statsd_msg{name = <<"foo">>, value = 1, type = gauge},
- Msg2 = statsd_msg_add_name_prefix(Msg1, <<"bar">>),
- <<?PATH_PREFIX,".bar.foo">> = Msg2#statsd_msg.name.
-
-ct_test__statsd_msg_to_bin(_Cfg) ->
- Msg = #statsd_msg{name = <<"foo">>, value = 1, type = gauge},
- <<"foo:1|g\n">> = statsd_msg_to_bin(Msg).
-
-ct_test__node_id_to_bin(_Cfg) ->
- <<"foo_bar_local">> = node_id_to_bin('foo@bar.local').