1 -module(beam_stats_msg_graphite).
3 -include("include/beam_stats.hrl").
4 -include("include/beam_stats_ets_table.hrl").
5 -include("include/beam_stats_msg_graphite.hrl").
6 -include("include/beam_stats_process.hrl").
7 -include("include/beam_stats_process_ancestry.hrl").
8 -include("include/beam_stats_processes.hrl").
28 %% ============================================================================
30 %% ============================================================================
32 -spec of_beam_stats(beam_stats:t()) ->
34 of_beam_stats(#beam_stats{node_id=NodeID}=BeamStats) ->
35 NodeIDBin = node_id_to_bin(NodeID),
36 of_beam_stats(BeamStats, NodeIDBin).
38 -spec of_beam_stats(beam_stats:t(), binary()) ->
40 of_beam_stats(#beam_stats
41 { timestamp = Timestamp
44 % TODO: Handle the rest of data points
45 , io_bytes_in = IOBytesIn
46 , io_bytes_out = IOBytesOut
47 , context_switches = ContextSwitches
48 , reductions = Reductions
49 , run_queue = RunQueue
51 , processes = Processes
57 [ cons([N, <<"io">> , <<"bytes_in">> ], IOBytesIn , Ts)
58 , cons([N, <<"io">> , <<"bytes_out">>], IOBytesOut , Ts)
59 , cons([N, <<"context_switches">> ], ContextSwitches, Ts)
60 , cons([N, <<"reductions">> ], Reductions , Ts)
61 , cons([N, <<"run_queue">> ], RunQueue , Ts)
62 | of_memory(Memory, NodeID, Ts)
64 ++ of_ets(ETS, NodeID, Ts)
65 ++ of_processes(Processes, NodeID, Ts).
73 , timestamp = Timestamp
76 PathBin = path_to_bin(Path),
77 ValueBin = integer_to_binary(Value),
78 TimestampInt = timestamp_to_integer(Timestamp),
79 TimestampBin = integer_to_binary(TimestampInt),
80 <<PathBin/binary, " ", ValueBin/binary, " ", TimestampBin/binary>>.
82 -spec add_path_prefix(t(), binary()) ->
84 add_path_prefix(?T{path=Path}=T, <<Prefix/binary>>) ->
85 T?T{path = [Prefix | Path]}.
87 -spec path_to_bin([binary()]) ->
90 bin_join(Path, <<".">>).
92 -spec node_id_to_bin(node()) ->
94 node_id_to_bin(NodeID) ->
95 NodeIDBin = atom_to_binary(NodeID, utf8),
96 re:replace(NodeIDBin, "[\@\.]", "_", [global, {return, binary}]).
98 %% ============================================================================
100 %% ============================================================================
102 -spec bin_join([binary()], binary()) ->
104 bin_join([] , <<_/binary>> ) -> <<>>;
105 bin_join([<<B/binary>> | []] , <<_/binary>> ) -> B;
106 bin_join([<<B/binary>> | [_|_]=Bins], <<Sep/binary>>) ->
107 BinsBin = bin_join(Bins, Sep),
108 <<B/binary, Sep/binary, BinsBin/binary>>.
110 -spec timestamp_to_integer(erlang:timestamp()) ->
112 timestamp_to_integer({Megaseconds, Seconds, _}) ->
113 Megaseconds * 1000000 + Seconds.
115 -spec of_memory([{atom(), non_neg_integer()}], binary(), erlang:timestamp()) ->
117 of_memory(Memory, <<NodeID/binary>>, Timestamp) ->
119 fun ({Key, Value}) ->
120 KeyBin = atom_to_binary(Key, latin1),
121 cons([NodeID, <<"memory">>, KeyBin], Value, Timestamp)
123 lists:map(ComponentToMessage, Memory).
125 -spec of_ets(beam_stats_ets:t(), binary(), erlang:timestamp()) ->
127 of_ets(PerTableStats, <<NodeID/binary>>, Timestamp) ->
128 OfEtsTable = fun (Table) -> of_ets_table(Table, NodeID, Timestamp) end,
129 NestedMsgs = lists:map(OfEtsTable, PerTableStats),
130 lists:append(NestedMsgs).
132 -spec of_ets_table(beam_stats_ets_table:t(), binary(), erlang:timestamp()) ->
134 of_ets_table(#beam_stats_ets_table
143 IDBin = beam_stats_ets_table:id_to_bin(ID),
144 NameBin = atom_to_binary(Name, latin1),
145 NameAndID = [NameBin, IDBin],
146 [ cons([NodeID, <<"ets_table">>, <<"size">> | NameAndID], Size , Timestamp)
147 , cons([NodeID, <<"ets_table">>, <<"memory">> | NameAndID], Memory, Timestamp)
150 -spec of_processes(beam_stats_processes:t(), binary(), erlang:timestamp()) ->
153 #beam_stats_processes
154 { individual_stats = Processes
155 , count_all = CountAll
156 , count_exiting = CountExiting
157 , count_garbage_collecting = CountGarbageCollecting
158 , count_registered = CountRegistered
159 , count_runnable = CountRunnable
160 , count_running = CountRunning
161 , count_suspended = CountSuspended
162 , count_waiting = CountWaiting
167 OfProcess = fun (P) -> of_process(P, NodeID, Timestamp) end,
168 PerProcessMsgsNested = lists:map(OfProcess, Processes),
169 PerProcessMsgsFlattened = lists:append(PerProcessMsgsNested),
172 [ cons([N, <<"processes_count_all">> ], CountAll , Ts)
173 , cons([N, <<"processes_count_exiting">> ], CountExiting , Ts)
174 , cons([N, <<"processes_count_garbage_collecting">>], CountGarbageCollecting, Ts)
175 , cons([N, <<"processes_count_registered">> ], CountRegistered , Ts)
176 , cons([N, <<"processes_count_runnable">> ], CountRunnable , Ts)
177 , cons([N, <<"processes_count_running">> ], CountRunning , Ts)
178 , cons([N, <<"processes_count_suspended">> ], CountSuspended , Ts)
179 , cons([N, <<"processes_count_waiting">> ], CountWaiting , Ts)
180 | PerProcessMsgsFlattened
183 -spec of_process(beam_stats_process:t(), binary(), erlang:timestamp()) ->
189 , total_heap_size = TotalHeapSize
190 , stack_size = StackSize
191 , message_queue_len = MsgQueueLen
196 Origin = beam_stats_process:get_best_known_origin(Process),
197 OriginBin = proc_origin_to_bin(Origin),
198 PidBin = pid_to_bin(Pid),
199 OriginAndPid = [OriginBin, PidBin],
202 [ cons([N, <<"process_memory">> | OriginAndPid], Memory , Ts)
203 , cons([N, <<"process_total_heap_size">> | OriginAndPid], TotalHeapSize , Ts)
204 , cons([N, <<"process_stack_size">> | OriginAndPid], StackSize , Ts)
205 , cons([N, <<"process_message_queue_len">> | OriginAndPid], MsgQueueLen , Ts)
208 -spec proc_origin_to_bin(beam_stats_process:best_known_origin()) ->
210 proc_origin_to_bin({registered_name, Name}) ->
211 atom_to_binary(Name, utf8);
212 proc_origin_to_bin({ancestry, Ancestry}) ->
213 #beam_stats_process_ancestry
214 { raw_initial_call = InitCallRaw
215 , otp_initial_call = InitCallOTPOpt
216 , otp_ancestors = AncestorsOpt
219 InitCallOTPBinOpt = hope_option:map(InitCallOTPOpt , fun mfa_to_bin/1),
220 InitCallOTPBin = hope_option:get(InitCallOTPBinOpt, Blank),
221 AncestorsBinOpt = hope_option:map(AncestorsOpt , fun ancestors_to_bin/1),
222 AncestorsBin = hope_option:get(AncestorsBinOpt , Blank),
223 InitCallRawBin = mfa_to_bin(InitCallRaw),
224 << InitCallRawBin/binary
226 , InitCallOTPBin/binary
228 , AncestorsBin/binary
231 ancestors_to_bin([]) ->
233 ancestors_to_bin([A | Ancestors]) ->
234 ABin = ancestor_to_bin(A),
235 case ancestors_to_bin(Ancestors)
238 ; <<AncestorsBin/binary>> ->
239 <<ABin/binary, "-", AncestorsBin/binary>>
242 ancestor_to_bin(A) when is_atom(A) ->
243 atom_to_binary(A, utf8);
244 ancestor_to_bin(A) when is_pid(A) ->
248 PidList = erlang:pid_to_list(Pid),
249 PidBin = re:replace(PidList, "[\.]", "_", [global, {return, binary}]),
250 re:replace(PidBin , "[><]", "" , [global, {return, binary}]).
252 -spec mfa_to_bin(mfa()) ->
254 mfa_to_bin({Module, Function, Arity}) ->
255 ModuleBin = atom_to_binary(Module , utf8),
256 FunctionBin = atom_to_binary(Function, utf8),
257 ArityBin = erlang:integer_to_binary(Arity),
258 <<ModuleBin/binary, "-", FunctionBin/binary, "-", ArityBin/binary>>.
260 -spec cons([binary()], integer(), erlang:timestamp()) ->
262 cons(Path, Value, Timestamp) ->
266 , timestamp = Timestamp