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 @@ log(Level, Pid, Format, Args) -> 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(warning) -> 4; 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(4) -> warning; 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 @@ calculate_next_rotation([{date, Date}|T], {{Year, Month, Day}, _} = Now) -> 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 ->