Supervisors and gen_servers

This commit is contained in:
Fabio Salvini 2017-06-11 18:47:34 +02:00
parent f0403bfa26
commit ae72db2769
7 changed files with 127 additions and 98 deletions

View File

@ -1,18 +1,25 @@
-module(coordinator). -module(coordinator).
-behaviour(supervisor).
-compile(export_all). -compile(export_all).
start() -> start_link() ->
_MailerPid = mailer:start(), supervisor:start_link(?MODULE, []).
{ok, Files} = application:get_env(log_monitor, logfiles),
Monitors = [monitor:start(File) || File <- Files],
loop(Monitors).
loop(Monitors) -> init([]) ->
receive SupFlags = #{strategy => one_for_one},
{'EXIT', FromPid, Reason} -> ChildSpecs = [#{
io:format("Monitor process terminated: ~s~n", [Reason]), id => mailer,
RemainingMonitors = [M || M <- Monitors, M =/= FromPid], start => {mailer, start_link, []},
loop(RemainingMonitors) restart => permanent,
after 5000 -> shutdown => 5000
loop(Monitors) }] ++ monitors_child_specs(),
end. {ok, {SupFlags, ChildSpecs}}.
monitors_child_specs() ->
{ok, Files} = application:get_env(log_monitor, logfiles),
[#{
id => list_to_atom(File),
start => {monitor, start_link, [File]},
restart => temporary,
shutdown => 5000
} || File <- Files].

View File

@ -0,0 +1,49 @@
-module(gatherer).
-behaviour(gen_server).
-compile(export_all).
start_link(File) ->
gen_server:start_link(?MODULE, [File], []).
init([File]) ->
{ok, [off, File]}.
handle_info({log_line, Text}, [off, File]) ->
case isError(Text) of
true ->
{ok, Timer} = timer:send_after(1000, {timeout}),
{noreply, [on, File, Text, Timer]};
false -> {noreply, [off, File]}
end;
handle_info({log_line, Text}, [on, File, Error, Timer]) ->
case isError(Text) of
true ->
timer:clean(Timer),
{ok, NewTimer} = timer:send_after(1000, {timeout}),
{noreply, [on, File, Error ++ "\n" ++ Text, NewTimer]};
false ->
{noreply, [on, File, Error ++ "\n" ++ Text, Timer]}
end;
handle_info({timeout}, [on, File, Error, _Timer]) ->
mailer ! {error, File, Error},
{noreply, [off, File]}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_call(_Request, _From, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
shutdown.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
isError(Text) ->
case re:run(Text, "ERROR") of %% TODO
{match, _} ->
true;
nomatch ->
false
end.

View File

@ -29,8 +29,8 @@ start_link() ->
%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} %% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
init([]) -> init([]) ->
{ok, { {one_for_all, 0, 1}, [{console, {ok, { {one_for_all, 0, 1}, [{console,
{coordinator, start, []}, {coordinator, start_link, []},
permanent, 5000, worker, [monitor]}]} }. permanent, 5000, supervisor, [coordinator]}]} }.
%%==================================================================== %%====================================================================
%% Internal functions %% Internal functions

View File

@ -1,25 +1,33 @@
-module(mailer). -module(mailer).
-behaviour(gen_server).
-compile(export_all). -compile(export_all).
start() -> start_link() ->
io:format("Starting Mailer~n", []), gen_server:start_link(?MODULE, [], []).
Pid = spawn_link(?MODULE, init, []),
register(mailer, Pid),
Pid.
init() -> init([]) ->
loop(). register(mailer, self()),
{ok, []}.
loop() -> handle_info({error, File, Text}, []) ->
receive
{error, File, Text} ->
case send_email(File, Text) of case send_email(File, Text) of
{error, Reason, Message} -> {error, Reason, Message} ->
io:format("Error sending email: ~s ~p~n", [Reason, Message]); io:format("Error sending email: ~s ~p~n", [Reason, Message]);
_ -> io:format("Mail sent~n", []) _ -> io:format("Mail sent~n", [])
end, end,
loop() {noreply, []}.
end.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_call(_Request, _From, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
shutdown.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
send_email(File, Text) -> send_email(File, Text) ->
{ok, EmailConfig} = application:get_env(log_monitor, email_config), {ok, EmailConfig} = application:get_env(log_monitor, email_config),

View File

@ -1,59 +1,22 @@
-module(monitor). -module(monitor).
-behaviour(supervisor).
-compile(export_all). -compile(export_all).
start(File) -> start_link(File) ->
io:format("Starting monitor for file: ~s~n", [File]), supervisor:start_link(?MODULE, [File]).
Pid = spawn_link(?MODULE, init, [File]),
Pid.
init(File) -> init(File) ->
process_flag(trap_exit, true), SupFlags = #{strategy => one_for_one},
startWatcher(File), ChildSpecs = [#{
loop(File). id => gatherer,
start => {gatherer, start_link, [File]},
loop(File) -> restart => permanent,
receive shutdown => 5000
{log_line, Text} -> },
case isError(Text) of #{
true -> errorGathering(File, Text); id => watcher,
false -> loop(File) start => {watcher, start_link, [self(), File]},
end, restart => permanent,
io:format("Received line: ~s~n", [Text]), shutdown => 5000
loop(File); }],
{'EXIT', _FromPid, Reason} -> {ok, {SupFlags, ChildSpecs}}.
io:format("Watcher process terminated: ~s~n", [Reason]),
startWatcher(File),
loop(File)
end.
errorGathering(File, Error) ->
{ok, Timer} = timer:send_after(1000, {timeout}),
errorGathering(File, Error, Timer).
errorGathering(File, Error, Timer) ->
receive
{log_line, Text} ->
case isError(Text) of
true ->
timer:clean(Timer),
{ok, NewTimer} = timer:send_after(1000, {timeout}),
errorGathering(File, Error ++ "\n" ++ Text, NewTimer);
false ->
errorGathering(File, Error ++ "\n" ++ Text, Timer)
end;
{timeout} ->
mailer ! {error, File, Error},
loop(File)
end.
isError(Text) ->
case re:run(Text, "ERROR") of %% TODO
{match, _} ->
true;
nomatch ->
false
end.
startWatcher(File) ->
%% WatcherPid = spawn_link(watcher, init, [self(), File]),
WatcherPid = watcher:start_link(self(), File),
{ok, WatcherPid}.

View File

@ -2,20 +2,20 @@
-behaviour(gen_server). -behaviour(gen_server).
-compile(export_all). -compile(export_all).
start_link(MonitorPid, File) -> start_link(SupPid, File) ->
gen_server:start_link(?MODULE, [MonitorPid, File], []). gen_server:start_link(?MODULE, [SupPid, File], []).
init([MonitorPid, File]) -> init([SupPid, File]) ->
io:format("Init~n"),
Cmd = "/usr/bin/tail -n0 --follow=name " ++ File, Cmd = "/usr/bin/tail -n0 --follow=name " ++ File,
Port = open_port({spawn, Cmd}, [stderr_to_stdout, exit_status, binary]), Port = open_port({spawn, Cmd}, [stderr_to_stdout, exit_status, binary]),
{ok, [MonitorPid, Port]}. {ok, [SupPid, Port]}.
handle_info(Msg, [MonitorPid, Port]) -> handle_info(Msg, [SupPid, Port]) ->
case Msg of case Msg of
{Port, {data, Text}} -> {Port, {data, Text}} ->
MonitorPid ! {log_line, Text}, GathererPid = gatherer_pid(SupPid),
{noreply, [MonitorPid, Port]}; GathererPid ! {log_line, Text},
{noreply, [SupPid, Port]};
{Port, {exit_status, _Status}} -> {Port, {exit_status, _Status}} ->
io:format("Watcher terminated~n"), io:format("Watcher terminated~n"),
{stop, tail_exit, []} {stop, tail_exit, []}
@ -27,9 +27,11 @@ handle_cast(_Msg, State) ->
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
{noreply, State}. {noreply, State}.
terminate(Reason, _State) -> terminate(_Reason, _State) ->
io:format("Reason: ~s~n", [Reason]),
shutdown. shutdown.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
gatherer_pid(SupPid) ->
hd([Pid || {Id, Pid, _, _} <- supervisor:which_children(SupPid), Id == gatherer]).

View File

@ -1,7 +1,7 @@
[ [
{ log_monitor, { log_monitor,
[ [
{logfiles, ["/tmp/lines.log"]}, {logfiles, ["/tmp/lines.log", "/tmp/lines2.log"]},
{email_config, {email_config,
[ [
{sender, "me@example.com"}, {sender, "me@example.com"},