Supervisors and gen_servers
This commit is contained in:
parent
f0403bfa26
commit
ae72db2769
|
@ -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].
|
||||||
|
|
49
apps/log_monitor/src/gatherer.erl
Normal file
49
apps/log_monitor/src/gatherer.erl
Normal 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.
|
|
@ -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
|
||||||
|
|
|
@ -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
|
case send_email(File, Text) of
|
||||||
{error, File, Text} ->
|
{error, Reason, Message} ->
|
||||||
case send_email(File, Text) of
|
io:format("Error sending email: ~s ~p~n", [Reason, Message]);
|
||||||
{error, Reason, Message} ->
|
_ -> io:format("Mail sent~n", [])
|
||||||
io:format("Error sending email: ~s ~p~n", [Reason, Message]);
|
end,
|
||||||
_ -> io:format("Mail sent~n", [])
|
{noreply, []}.
|
||||||
end,
|
|
||||||
loop()
|
handle_cast(_Msg, State) ->
|
||||||
end.
|
{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),
|
||||||
|
|
|
@ -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}.
|
|
||||||
|
|
|
@ -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]).
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user