%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
%%
%% 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.
%%
%% %CopyrightEnd%
%%
-module(snmp_test_global_sys_monitor).
-export([start/0, stop/0,
reset_events/0,
events/0,
log/1]).
-export([init/1]).
-define(NAME, ?MODULE).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start() ->
Parent = self(),
proc_lib:start(?MODULE, init, [Parent]).
stop() ->
cast(stop).
%% This does not reset the global counter but the "collector"
%% See events for more info.
reset_events() ->
cast(reset_events).
events() ->
call(events).
log(Event) ->
cast({node(), Event}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init(Parent) ->
process_flag(priority, high),
case global:register_name(?NAME, self()) of
yes ->
info_msg("Starting", []),
proc_lib:init_ack(Parent, {ok, self()}),
loop(#{parent => Parent, ev_cnt => 0, evs => []});
no ->
warning_msg("Already started", []),
proc_lib:init_ack(Parent, {error, already_started}),
exit(normal)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
loop(State) ->
receive
{?MODULE, stop} ->
warning_msg("Stopping with ~w events counted",
[maps:get(ev_cnt, State)]),
exit(normal);
{?MODULE, reset_events} ->
loop(State#{evs => []});
{?MODULE, Ref, From, events} ->
Evs = maps:get(evs, State),
From ! {?MODULE, Ref, lists:reverse(Evs)},
loop(State);
{?MODULE, {Node, Event}} ->
State2 = process_event(State, Node, Event),
loop(State2);
{nodedown = Event, Node} ->
State2 = process_event(State, Node, Event),
loop(State2);
_ ->
loop(State)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
process_event(State, Node, {Pid, TS, Tag, Info}) ->
process_system_event(State, Node, Pid, TS, Tag, Info);
process_event(State, Node, {TS, starting}) ->
FTS = snmp_misc:format_timestamp(TS),
info_msg("System Monitor on node ~p starting at ~s", [Node, FTS]),
if
(Node =/= node()) ->
erlang:monitor_node(Node, true);
true ->
ok
end,
State;
process_event(State, Node, {TS, already_started}) ->
FTS = snmp_misc:format_timestamp(TS),
info_msg("System Monitor on node ~p already started", [Node, FTS]),
State;
process_event(State, Node, nodedown) ->
info_msg("Node ~p down", [Node]),
State;
process_event(State, Node, Event) ->
warning_msg("Received unknown event from node ~p:"
"~n ~p", [Node, Event]),
State.
%% System Monitor events
%% We only *count* system events
process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
Node, Pid, TS, long_gc = Ev, Info) ->
print_system_event("Long GC", Node, Pid, TS, Info),
State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
Node, Pid, TS, long_schedule = Ev, Info) ->
print_system_event("Long Schedule", Node, Pid, TS, Info),
State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
Node, Pid, TS, large_heap = Ev, Info) ->
print_system_event("Large Heap", Node, Pid, TS, Info),
State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
Node, Pid, TS, busy_port = Ev, Info) ->
print_system_event("Busy port", Node, Pid, TS, Info),
State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
Node, Pid, TS, busy_dist_port = Ev, Info) ->
print_system_event("Busy dist port", Node, Pid, TS, Info),
State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
%% And everything else
process_system_event(State, Node, Pid, TS, Tag, Info) ->
Pre = f("Unknown Event '~p'", [Tag]),
print_system_event(Pre, Node, Pid, TS, Info),
State.
print_system_event(Pre, Node, Pid, TS, Info) ->
FTS = snmp_misc:format_timestamp(TS),
warning_msg("~s from ~p (~p) at ~s:"
"~n ~p", [Pre, Node, Pid, FTS, Info]).
f(F, A) ->
lists:flatten(io_lib:format(F, A)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cast(Msg) ->
try global:send(?NAME, {?MODULE, Msg}) of
Pid when is_pid(Pid) ->
ok
catch
C:E:_ ->
{error, {catched, C, E}}
end.
call(Req) ->
call(Req, infinity).
call(Req, Timeout) ->
Ref = make_ref(),
try global:send(?NAME, {?MODULE, Ref, self(), Req}) of
Pid when is_pid(Pid) ->
receive
{?MODULE, Ref, Rep} ->
Rep
after Timeout ->
{error, timeout}
end
catch
C:E:_ ->
{error, {catched, C, E}}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
info_msg(F, A) ->
error_logger:info_msg(format_msg(F, A), []).
warning_msg(F, A) ->
error_logger:warning_msg(format_msg(F, A), []).
format_msg(F, A) ->
f("~n" ++
"****** SNMP TEST GLOBAL SYSTEM MONITOR ******~n~n" ++
F ++
"~n~n",
A).