diff --git a/TODO.md b/TODO.md index d4f5684..83a6e44 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,6 @@ TODO ===== - - Allow to enable / disabled logfiles. + - gatherer.erl:29 Error on ++. - Limit number of emails that can be sent in a period of time. - - Store the logfiles in a configuration file. - gen_fsm for gatherer? diff --git a/apps/log_monitor/src/config.erl b/apps/log_monitor/src/config.erl index b17c4e4..52770c7 100644 --- a/apps/log_monitor/src/config.erl +++ b/apps/log_monitor/src/config.erl @@ -3,8 +3,8 @@ -include_lib("mnesia_tables.hrl"). --export([create_group/2, monitor_log/3, unmonitor_log/1]). --export([logfiles/0]). +-export([status/0]). +-export([reload/0]). -export([start_link/0, init/1, terminate/2]). -export([handle_info/2, handle_cast/2, handle_call/3]). -export([code_change/3]). @@ -17,6 +17,7 @@ start_link() -> init([]) -> register(config, self()), Statuses = ets:new(log_statuses, []), + %% TODO: reload? mnesia:activity( transaction, fun() -> @@ -49,50 +50,22 @@ handle_info(_Msg, State) -> handle_cast(_Msg, State) -> {noreply, State}. -handle_call({create_group, Name, EmailReceivers}, _From, State) -> - mnesia:activity( - transaction, - fun() -> - case mnesia:read(log_monitor_group, Name) of - [] -> - mnesia:write(#log_monitor_group{name = Name, email_receivers = EmailReceivers}), - {reply, ok, State}; - _ -> {reply, {error, "Duplicate"}, State} - end - end); -handle_call({monitor, Group, File, ErrorRegex}, _From, State) -> - mnesia:activity( - transaction, - fun() -> - case mnesia:read(log_monitor_group, Group) of - [{log_monitor_group, Group, _}] -> - case mnesia:read(log_monitor_file, File) of - [] -> - mnesia:write(#log_monitor_file{ - file = File, - error_regex = ErrorRegex, - group = Group, - status = enabled}), - logfiles_sup:add_child([File, ErrorRegex]), - {reply, ok, State}; - _ -> {reply, {error, "Duplicate"}, State} - end; - [] -> {reply, {error, "Unknown group"}, State} - end - end); -handle_call({unmonitor, File}, _From, State = #state{statuses = Statuses}) -> - mnesia:activity( - transaction, - fun() -> - case mnesia:read(log_monitor_file, File) of - [{log_monitor_file, File, _, _, _}] -> - mnesia:delete({log_monitor_file, File}), - ets:delete(Statuses, File), - logfiles_sup:remove_child(File), - {reply, ok, State}; - _ -> {reply, {error, "File not found"}, State} - end - end); +handle_call({reload}, _From, State = #state{statuses = Statuses}) -> + {ok, File} = application:get_env(log_monitor, logfiles_config), + {ok, Terms} = file:consult(File), + MonitoredLogs = proplists:get_value(monitored_logs, Terms), + Groups = [#log_monitor_group{name = Name, email_receivers = EmailReceivers} || {{group, Name}, {email_receivers, EmailReceivers}, _} <- MonitoredLogs], + Logfiles = utils:flatten([[#log_monitor_file{file = F, error_regex = ErrorRegex, group = Name} || {{file, F}, {error_regex, ErrorRegex}} <- Logfiles] + || {{group, Name}, {email_receivers, _}, {logfiles, Logfiles}} <- MonitoredLogs]), + GroupsPartitions = compare_groups(Groups), + manage_deleted_groups(proplists:get_value(deleted, GroupsPartitions)), + manage_existing_groups(proplists:get_value(existing, GroupsPartitions)), + manage_new_groups(proplists:get_value(new, GroupsPartitions)), + LogfilesPartitions = compare_logfiles(Logfiles), + manage_deleted_logfiles(proplists:get_value(deleted, LogfilesPartitions), Statuses), + manage_existing_logfiles(proplists:get_value(existing, LogfilesPartitions)), + manage_new_logfiles(proplists:get_value(new, LogfilesPartitions), Statuses), + {reply, ok, State}; handle_call({get_statuses}, _From, State = #state{statuses = Statuses}) -> {reply, ets:tab2list(Statuses), State}. @@ -102,14 +75,115 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -create_group(Name, EmailReceivers) -> - gen_server:call(config, {create_group, Name, EmailReceivers}). - -monitor_log(Group, File, ErrorRegex) -> - gen_server:call(config, {monitor, Group, File, ErrorRegex}). - -unmonitor_log(File) -> - gen_server:call(config, {unmonitor, File}). - -logfiles() -> +status() -> gen_server:call(config, {get_statuses}). + +reload() -> + gen_server:call(config, {reload}). + +compare_groups(Groups) -> + Old = list_groups(), + Deleted = lists:filter(fun(Name) -> not lists:any(fun(#log_monitor_group{name = N, email_receivers = _}) -> N == Name end, Groups) end, Old), + Existing = lists:filter(fun(#log_monitor_group{name = Name, email_receivers = _}) -> lists:member(Name, Old) end, Groups), + New = lists:filter(fun(#log_monitor_group{name = Name, email_receivers = _}) -> not lists:member(Name, Old) end, Groups), + [ + {deleted, Deleted}, + {existing, Existing}, + {new, New} + ]. + +manage_deleted_groups(Groups) -> + mnesia:activity( + transaction, + fun() -> + lists:foldl(fun(Name, _Acc) -> + mnesia:delete({log_monitor_group, Name}) + end, [], Groups) + end). + +manage_existing_groups(Groups) -> + mnesia:activity( + transaction, + fun() -> + lists:foldl(fun(Group, _Acc) -> + mnesia:write(Group) + end, [], Groups) + end). + +manage_new_groups(Groups) -> + mnesia:activity( + transaction, + fun() -> + lists:foldl(fun(Group, _Acc) -> + mnesia:write(Group) + end, [], Groups) + end). + +list_groups() -> + mnesia:activity( + transaction, + fun() -> + mnesia:foldl( + fun(#log_monitor_group{name = Name, email_receivers = _}, Acc) -> + [Name | Acc] + end, [], log_monitor_group) + end). + +compare_logfiles(Logfiles) -> + Old= list_logfiles(), + Deleted = lists:filter(fun(File) -> not lists:any(fun(#log_monitor_file{file = F, error_regex = _, group = _}) -> F == File end, Logfiles) end, Old), + Existing = lists:filter(fun(#log_monitor_file{file = File, error_regex = _, group = _}) -> lists:member(File, Old) end, Logfiles), + New = lists:filter(fun(#log_monitor_file{file = File, error_regex = _, group = _}) -> not lists:member(File, Old) end, Logfiles), + [ + {deleted, Deleted}, + {existing, Existing}, + {new, New} + ]. + +manage_deleted_logfiles(Logfiles, Statuses) -> + mnesia:activity( + transaction, + fun() -> + lists:foldl(fun(File, _Acc) -> + logfiles_sup:remove_child(File), + mnesia:delete({log_monitor_file, File}), + ets:delete(Statuses, File) + end, [], Logfiles) + end). + +manage_existing_logfiles(Logfiles) -> + mnesia:activity( + transaction, + fun() -> + lists:foldl(fun(Logfile = #log_monitor_file{file = File, error_regex = ErrorRegex, group = _}, _Acc) -> + mnesia:write(Logfile), + case mnesia:read(log_monitor_file, File) of + [#log_monitor_file{file = File, error_regex = ErrorRegex, group = _}] -> ok; + _ -> + %% The error regex has changed + logfiles_sup:remove_child(File), + logfiles_sup:add_child([File, ErrorRegex]) + end + end, [], Logfiles) + end). + +manage_new_logfiles(Logfiles, Statuses) -> + mnesia:activity( + transaction, + fun() -> + lists:foldl(fun(Logfile = #log_monitor_file{file = File, error_regex = ErrorRegex, group = _}, _Acc) -> + mnesia:write(Logfile), + ets:insert(Statuses, {File, inactive}), + logfiles_sup:add_child([File, ErrorRegex]) + end, [], Logfiles) + end). + +list_logfiles() -> + mnesia:activity( + transaction, + fun() -> + mnesia:foldl( + fun(#log_monitor_file{file = File, error_regex = _, group = _}, Acc) -> + [File | Acc] + end, [], log_monitor_file) + end). diff --git a/apps/log_monitor/src/log_monitor.app.src b/apps/log_monitor/src/log_monitor.app.src index 6f0f674..4b5dbf4 100644 --- a/apps/log_monitor/src/log_monitor.app.src +++ b/apps/log_monitor/src/log_monitor.app.src @@ -7,7 +7,9 @@ [kernel, stdlib, gen_smtp, - mnesia + mnesia, + %% Observer + observer, wx, runtime_tools ]}, {env,[]}, {modules, []}, diff --git a/apps/log_monitor/src/mailer.erl b/apps/log_monitor/src/mailer.erl index fb1a41a..adbad46 100644 --- a/apps/log_monitor/src/mailer.erl +++ b/apps/log_monitor/src/mailer.erl @@ -90,7 +90,7 @@ send_email(File, Text) -> Connection ) catch - _Throw -> + error:_ -> ok end. @@ -107,10 +107,11 @@ receivers(File) -> transaction, fun() -> case mnesia:read(log_monitor_file, File) of - [{log_monitor_file, File, _, _, Group}] -> + [{log_monitor_file, File, _, Group}] -> case mnesia:read(log_monitor_group, Group) of [{log_monitor_group, Group, EmailReceivers}] -> - EmailReceivers + EmailReceivers; + _ -> throw(file_group_not_found) end; _ -> throw(file_not_found) end diff --git a/apps/log_monitor/src/mnesia_tables.hrl b/apps/log_monitor/src/mnesia_tables.hrl index 9b907ee..51ecced 100644 --- a/apps/log_monitor/src/mnesia_tables.hrl +++ b/apps/log_monitor/src/mnesia_tables.hrl @@ -3,7 +3,7 @@ %% Mnesia tables -record(log_monitor_group, {name, email_receivers=[]}). --record(log_monitor_file, {file, error_regex, status, group}). +-record(log_monitor_file, {file, error_regex, group}). -record(log_monitor_error, {id, file, text}). -endif. diff --git a/apps/log_monitor/src/utils.erl b/apps/log_monitor/src/utils.erl new file mode 100644 index 0000000..a9474a0 --- /dev/null +++ b/apps/log_monitor/src/utils.erl @@ -0,0 +1,10 @@ +-module(utils). + +-export([flatten/1]). + +flatten(X) -> flatten(X,[]). + +flatten([],Acc) -> Acc; +flatten([[]|T],Acc) -> flatten(T, Acc); +flatten([[_|_]=H|T],Acc) -> flatten(T, flatten(H,Acc)); +flatten([H|T],Acc) -> flatten(T,Acc++[H]) . diff --git a/config/log_monitor.config b/config/log_monitor.config index ec7653b..5b1779c 100644 --- a/config/log_monitor.config +++ b/config/log_monitor.config @@ -16,7 +16,8 @@ {password, ""}] } ] - } + }, + {logfiles_config, "/home/fsalvini/gitRepos/log_monitor/config/logfiles.config"} ] }, %% Directory where the application data is stored. diff --git a/config/logfiles.config b/config/logfiles.config new file mode 100644 index 0000000..e8b962c --- /dev/null +++ b/config/logfiles.config @@ -0,0 +1,12 @@ +{monitored_logs, [ + { + {group, "Test"}, + {email_receivers, ["user@example.com"]}, + {logfiles, [ + { + {file, "/tmp/lines.log"}, + {error_regex, "ERROR"} + } + ]} + } +]}. diff --git a/config/rebar.lock b/config/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/config/rebar.lock @@ -0,0 +1 @@ +[].