Use Mnesia to store monitored log files

This commit is contained in:
Fabio Salvini 2017-06-15 23:40:27 +02:00
parent af8fad987d
commit 9b13aa6cbc
3 changed files with 102 additions and 33 deletions

View File

@ -1,31 +1,43 @@
-module(config). -module(config).
-behaviour(gen_server). -behaviour(gen_server).
-export([monitor_log/2, unmonitor_log/1]). -export([create_group/2, monitor_log/3, unmonitor_log/1]).
-export([logfiles/0]). -export([logfiles/0]).
-export([start_link/0, init/1, terminate/2]). -export([start_link/0, init/1, terminate/2]).
-export([handle_info/2, handle_cast/2, handle_call/3]). -export([handle_info/2, handle_cast/2, handle_call/3]).
-export([code_change/3]). -export([code_change/3]).
-record(state, {statuses}).
%% Mnesia tables
-record(log_monitor_group, {name, email_receivers=[]}).
-record(log_monitor_file, {file, error_regex, status, group}).
start_link() -> start_link() ->
gen_server:start_link(?MODULE, [], []). gen_server:start_link(?MODULE, [], []).
init([]) -> init([]) ->
register(config, self()), register(config, self()),
{ok, Storage} = application:get_env(log_monitor, storage),
{ok, Logfiles} = dets:open_file(Storage, [{auto_save, 1000}]),
Statuses = ets:new(log_statuses, []), Statuses = ets:new(log_statuses, []),
lists:foreach(fun({File, ErrorRegex}) -> start_mnesia(),
ets:insert(Statuses, {File, disabled}), mnesia:activity(
logfiles_sup:add_child([File, ErrorRegex]) transaction,
end, dets:foldl(fun(X, L) -> [X|L] end, [], Logfiles)), fun() ->
{ok, [Logfiles, Statuses]}. mnesia:foldl(
fun(#log_monitor_file{file = File, error_regex = ErrorRegex}, _Acc) ->
ets:insert(Statuses, {File, inactive}),
logfiles_sup:add_child([File, ErrorRegex]),
ok
end, [], log_monitor_file)
end),
{ok, #state{statuses = Statuses}}.
handle_info({watcher_init, File}, State = [_Logfiles, Statuses]) -> handle_info({watcher_init, File}, State = #state{statuses = Statuses}) ->
ets:insert(Statuses, {File, enabled}), ets:insert(Statuses, {File, active}),
{noreply, State}; {noreply, State};
handle_info({watcher_terminate, File}, State = [_Logfiles, Statuses]) -> handle_info({watcher_terminate, File}, State = #state{statuses = Statuses}) ->
ets:insert(Statuses, {File, disabled}), %% TODO: do not update it if not present
ets:insert(Statuses, {File, inactive}),
{noreply, State}; {noreply, State};
handle_info(_Msg, State) -> handle_info(_Msg, State) ->
{noreply, State}. {noreply, State}.
@ -33,21 +45,51 @@ handle_info(_Msg, State) ->
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State}.
handle_call({monitor, File, ErrorRegex}, _From, State = [Logfiles, Statuses]) -> handle_call({create_group, Name, EmailReceivers}, _From, State) ->
case dets:lookup(Logfiles, File) of mnesia:activity(
transaction,
fun() ->
case mnesia:read(log_monitor_group, Name) of
[] -> [] ->
dets:insert(Logfiles, {File, ErrorRegex}), mnesia:write(#log_monitor_group{name = Name, email_receivers = EmailReceivers}),
ets:insert(Statuses, {File, disabled}), {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]), logfiles_sup:add_child([File, ErrorRegex]),
{reply, ok, State}; {reply, ok, State};
_ -> {reply, duplicate, State} _ -> {reply, {error, "Duplicate"}, State}
end; end;
handle_call({unmonitor, File}, _From, State = [Logfiles, Statuses]) -> [] -> {reply, {error, "Unknown group"}, State}
logfiles_sup:remove_child(File), 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), ets:delete(Statuses, File),
dets:delete(Logfiles, File), logfiles_sup:remove_child(File),
{reply, ok, State}; {reply, ok, State};
handle_call({get_statuses}, _From, State = [_Logfiles, Statuses]) -> _ -> {reply, {error, "File not found"}, State}
end
end);
handle_call({get_statuses}, _From, State = #state{statuses = Statuses}) ->
{reply, ets:tab2list(Statuses), State}. {reply, ets:tab2list(Statuses), State}.
terminate(_Reason, _State) -> terminate(_Reason, _State) ->
@ -56,11 +98,33 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
monitor_log(File, ErrorRegex) -> create_group(Name, EmailReceivers) ->
gen_server:call(config, {monitor, File, ErrorRegex}). gen_server:call(config, {create_group, Name, EmailReceivers}).
monitor_log(Group, File, ErrorRegex) ->
gen_server:call(config, {monitor, Group, File, ErrorRegex}).
unmonitor_log(File) -> unmonitor_log(File) ->
gen_server:call(config, {unmonitor, File}). gen_server:call(config, {unmonitor, File}).
logfiles() -> logfiles() ->
gen_server:call(config, {get_statuses}). gen_server:call(config, {get_statuses}).
start_mnesia() ->
Nodes = [node()],
%% Stop Mnesia if is running, cannot create schema otherwise.
mnesia:stop(),
mnesia:create_schema(Nodes),
mnesia:start(),
mnesia:create_table(log_monitor_group,
[
{attributes, record_info(fields, log_monitor_group)},
{disc_copies, Nodes}
]),
mnesia:create_table(log_monitor_file,
[
{attributes, record_info(fields, log_monitor_file)},
{index, [#log_monitor_file.group]},
{disc_copies, Nodes}
]),
ok = mnesia:wait_for_tables([log_monitor_group, log_monitor_file], 30000).

View File

@ -6,7 +6,8 @@
{applications, {applications,
[kernel, [kernel,
stdlib, stdlib,
gen_smtp gen_smtp,
mnesia
]}, ]},
{env,[]}, {env,[]},
{modules, []}, {modules, []},

View File

@ -1,9 +1,6 @@
[ [
{ log_monitor, { log_monitor,
[ [
%% File where the list of monitored log files is stored.
%% WARNING: If you leave it inside /tmp, every time the node is restarted the log files will be lost.
{storage, "/tmp/logfiles"},
{email_config, {email_config,
[ [
{sender, "log@monitor.com"}, {sender, "log@monitor.com"},
@ -16,5 +13,12 @@
] ]
} }
] ]
},
%% Directory where the application data is stored.
%% WARNING: If you leave it inside /tmp, every time the node is restarted the
%% application will reset.
{ mnesia , [
{dir, "/tmp/mnesia"}
]
} }
]. ].