Initial attempt at a trace API, and a status() command
diff --git a/include/lager.hrl b/include/lager.hrl
index 1c42a93..bb42f7b 100644
--- a/include/lager.hrl
+++ b/include/lager.hrl
@@ -15,7 +15,7 @@
%% under the License.
-define(LEVELS,
- [debug, info, notice, warning, error, critical, alert, emergency]).
+ [debug, info, notice, warning, error, critical, alert, emergency, none]).
-define(DEBUG, 7).
-define(INFO, 6).
diff --git a/src/lager.erl b/src/lager.erl
index 49d2bea..a0d1355 100644
--- a/src/lager.erl
+++ b/src/lager.erl
@@ -23,6 +23,8 @@
%% API
-export([start/0,
log/8, log_dest/9, log/3, log/4,
+ trace_file/2, trace_file/3, trace_console/1, trace_console/2,
+ status/0,
get_loglevel/1, set_loglevel/2, set_loglevel/3, get_loglevels/0,
minimum_loglevel/1, posix_error/1,
safe_format/3, safe_format_chop/3]).
@@ -88,6 +90,63 @@
safe_format_chop(Format, Args, 4096)],
safe_notify({log, lager_util:level_to_num(Level), Timestamp, Msg}).
+trace_file(File, Filter) ->
+ trace_file(File, Filter, debug).
+
+trace_file(File, Filter, Level) ->
+ Trace0 = {Filter, Level, {lager_file_backend, File}},
+ case lager_util:validate_trace(Trace0) of
+ {ok, Trace} ->
+ Handlers = gen_event:which_handlers(lager_event),
+ %% check if this file backend is already installed
+ case lists:member({lager_file_backend, File}, Handlers) of
+ false ->
+ %% install the handler
+ supervisor:start_child(lager_handler_watcher_sup,
+ [lager_event, {lager_file_backend, File}, {File, none}]);
+ _ ->
+ ok
+ end,
+ %% install the trace.
+ {MinLevel, Traces} = lager_mochiglobal:get(loglevel),
+ lager_mochiglobal:put(loglevel, {MinLevel, [Trace|Traces]});
+ Error ->
+ Error
+ end.
+
+trace_console(Filter) ->
+ trace_file(Filter, debug).
+
+trace_console(Filter, Level) ->
+ Trace0 = {Filter, Level, lager_console_backend},
+ case lager_util:validate_trace(Trace0) of
+ {ok, Trace} ->
+ {Level, Traces} = lager_mochiglobal:get(loglevel),
+ lager_mochiglobal:put(loglevel, {Level, [Trace|Traces]});
+ Error ->
+ Error
+ end.
+
+status() ->
+ Handlers = gen_event:which_handlers(lager_event),
+ Status = ["Lager status:\n",
+ [begin
+ Level = get_loglevel(Handler),
+ case Handler of
+ {lager_file_backend, File} ->
+ io_lib:format("File ~s at level ~p\n", [File, Level]);
+ lager_console_backend ->
+ io_lib:format("Console at level ~p\n", [Level]);
+ _ ->
+ []
+ end
+ end || Handler <- Handlers],
+ "Active Traces:\n",
+ [begin
+ io_lib:format("Tracing messages matching ~p at level ~p to ~p\n", [Filter, lager_util:num_to_level(Level), Destination])
+ end || {Filter, Level, Destination} <- element(2, lager_mochiglobal:get(loglevel))]],
+ io:put_chars(Status).
+
%% @doc Set the loglevel for a particular backend.
set_loglevel(Handler, Level) when is_atom(Level) ->
Reply = gen_event:call(lager_event, Handler, {set_loglevel, Level}, infinity),
diff --git a/src/lager_util.erl b/src/lager_util.erl
index 5339636..c32df8c 100644
--- a/src/lager_util.erl
+++ b/src/lager_util.erl
@@ -21,7 +21,7 @@
-export([levels/0, level_to_num/1, num_to_level/1, open_logfile/2,
ensure_logfile/4, rotate_logfile/2, format_time/0, format_time/1,
localtime_ms/0, maybe_utc/1, parse_rotation_date_spec/1,
- calculate_next_rotation/1, check_traces/4]).
+ calculate_next_rotation/1, validate_trace/1, check_traces/4]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
@@ -37,7 +37,8 @@
level_to_num(error) -> 3;
level_to_num(critical) -> 2;
level_to_num(alert) -> 1;
-level_to_num(emergency) -> 0.
+level_to_num(emergency) -> 0;
+level_to_num(none) -> -1.
num_to_level(7) -> debug;
num_to_level(6) -> info;
@@ -46,7 +47,8 @@
num_to_level(3) -> error;
num_to_level(2) -> critical;
num_to_level(1) -> alert;
-num_to_level(0) -> emergency.
+num_to_level(0) -> emergency;
+num_to_level(-1) -> none.
open_logfile(Name, Buffer) ->
case filelib:ensure_dir(Name) of
@@ -269,6 +271,31 @@
NewNow = calendar:gregorian_seconds_to_datetime(Seconds),
calculate_next_rotation(T, NewNow).
+validate_trace({Filter, Level, {Destination, ID}}) when is_list(Filter), is_atom(Level), is_atom(Destination) ->
+ case validate_trace({Filter, Level, Destination}) of
+ {ok, {F, L, D}} ->
+ {ok, {F, L, {D, ID}}};
+ Error ->
+ Error
+ end;
+validate_trace({Filter, Level, Destination}) when is_list(Filter), is_atom(Level), is_atom(Destination) ->
+ try level_to_num(Level) of
+ L ->
+ case lists:all(fun({Key, _Value}) when is_atom(Key) -> true; (_) ->
+ false end, Filter) of
+ true ->
+ {ok, {Filter, L, Destination}};
+ _ ->
+ {error, invalid_filter}
+ end
+ catch
+ _:_ ->
+ {error, invalid_level}
+ end;
+validate_trace(_) ->
+ {error, invalid_trace}.
+
+
check_traces(_, _, [], Acc) ->
lists:flatten(Acc);
check_traces(Attrs, Level, [{_, FilterLevel, _}|Flows], Acc) when Level > FilterLevel ->