File lager-3.9.2-git.patch of Package lager
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5fd84e9..3b45b0b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,17 +29,14 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
- otp_vsn: [23.2]
+ otp_vsn: [23.2, 24]
os: [windows-latest]
+ rebar3_vsn: ['3.16']
steps:
- uses: actions/checkout@v2
- - uses: gleam-lang/setup-erlang@v1.1.2
+ - uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp_vsn}}
- id: install_erlang
- - run: wget https://s3.amazonaws.com/rebar3/rebar3 -OutFile rebar3
- shell: powershell
- - run: |
- & "${{steps.install_erlang.outputs.erlpath}}\bin\escript.exe" rebar3 dialyzer
- - run: |
- & "${{steps.install_erlang.outputs.erlpath}}\bin\escript.exe" rebar3 eunit
+ rebar3-version: ${{matrix.rebar3_vsn}}
+ - run: rebar3 dialyzer
+ - run: rebar3 eunit
diff --git a/README.md b/README.md
index a66302d..3a1655a 100644
--- a/README.md
+++ b/README.md
@@ -218,7 +218,7 @@ will be applied on that sink.
Custom Formatting
-----------------
-All loggers have a default formatting that can be overriden. A formatter is any module that
+All loggers have a default formatting that can be overridden. A formatter is any module that
exports `format(#lager_log_message{},Config#any())`. It is specified as part of the configuration
for the backend:
@@ -653,7 +653,7 @@ record a module compiled with the lager parse transform knows about by using the
lager:info("My state is ~p", [lager:pr(State, ?MODULE)])
```
-Often, `?MODULE` is sufficent, but you can obviously substitute that for a literal module name.
+Often, `?MODULE` is sufficient, but you can obviously substitute that for a literal module name.
`lager:pr` also works from the shell.
Colored terminal output
@@ -786,6 +786,15 @@ a black hole; nothing is passed through. A filter of `{null, true}` means
*everything* passes through. No other values for the null filter are valid and
will be rejected.
+### Silencing filters
+A special log level, `silence` can be used together with a filter in order
+to suppress specific log output. This can be useful if a backend has been
+configured for a particular log level, but a particular set of log messages
+clutters the log. If these come from a dependency, they might be difficult
+to remove entirely, and it might not be desirable to do so in general.
+In such situations, a trace filter with log level `silence` can turn them
+off selectively, while letting other messages through as before.
+
### Multiple sink support
If using multiple sinks, there are limitations on tracing that you
@@ -1254,7 +1263,7 @@ Example Usage:
* Bugfix: flush_threshold not working (#449)
* Feature: Add `node` as a formatting option (#447)
* Documentation: Update Elixir section with information about parse_transform (#446)
- * Bugfix: Correct default console configuation to use "[{level,info}]" instead (#445)
+ * Bugfix: Correct default console configuration to use "[{level,info}]" instead (#445)
* Feature: Pretty print lists of records at top level and field values with lager:pr (#442)
* Bugfix: Ignore return value of lager:dispatch_log in lager.hrl (#441)
@@ -1330,7 +1339,7 @@ Example Usage:
3.3.0 - 16 February 2017
- * Docs: Fix documentation to make 'it' unambiguous when discussing asychronous
+ * Docs: Fix documentation to make 'it' unambiguous when discussing asynchronous
operation. (#387)
* Test: Fix test flappiness due to insufficient sanitation between test runs (#384, #385)
* Feature: Allow metadata only logging. (#380)
diff --git a/rebar.config b/rebar.config
index c3a639b..f1250b4 100644
--- a/rebar.config
+++ b/rebar.config
@@ -35,7 +35,7 @@
warn_unused_import
% do NOT include warnings_as_errors, as rebar includes these options
% when compiling for eunit, and at least one test module has code that
- % is deliberatly broken and will generate an un-maskable warning
+ % is deliberately broken and will generate an un-maskable warning
]}.
{erl_first_files, ["src/lager_util.erl"]}.
diff --git a/src/error_logger_lager_h.erl b/src/error_logger_lager_h.erl
index b6ef5a9..96d2fcf 100644
--- a/src/error_logger_lager_h.erl
+++ b/src/error_logger_lager_h.erl
@@ -420,7 +420,7 @@ format_offender(Off) ->
[Name, MFA, get_value(pid, Off)])
end.
-%% backwards compatability shim
+%% backwards compatibility shim
format_reason(Reason) ->
element(2, format_reason_md(Reason)).
@@ -551,7 +551,7 @@ format_reason_md(Reason) ->
{Str, _} = lager_trunc_io:print(Reason, 500),
{[], Str}.
-%% backwards compatability shim
+%% backwards compatibility shim
format_mfa(MFA) ->
element(2, format_mfa_md(MFA)).
@@ -575,7 +575,7 @@ format_mfa_md([{M, F, A}| _]) ->
format_mfa_md([{M, F, A, Props}| _]) when is_list(Props) ->
%% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
%% TODO we might not always want to print the first MFA we see here, often it is more helpful
- %% to print a lower one, but it is hard to programatically decide.
+ %% to print a lower one, but it is hard to programmatically decide.
format_mfa_md({M, F, A, Props});
format_mfa_md(Other) ->
{[], io_lib:format("~w", [Other])}.
diff --git a/src/lager.erl b/src/lager.erl
index 5562042..d834f3c 100644
--- a/src/lager.erl
+++ b/src/lager.erl
@@ -53,7 +53,7 @@
count :: infinity | pos_integer(),
format_string :: string(),
timeout :: infinity | pos_integer(),
- started = os:timestamp() :: erlang:timestamp() %% use os:timestamp for compatability
+ started = os:timestamp() :: erlang:timestamp() %% use os:timestamp for compatibility
}).
%% API
diff --git a/src/lager_app.erl b/src/lager_app.erl
index 11c3ec3..6172be0 100644
--- a/src/lager_app.erl
+++ b/src/lager_app.erl
@@ -190,7 +190,8 @@ maybe_remove_logger_handler() ->
try
ok = logger:remove_handler(default)
catch
- error:undef -> ok;
+ error:undef -> ok; %% No remove_handler(), OTP < 21.1
+ error:{badmatch, {error, {not_found, default}}} -> ok; %% No default handler, already removed
Err:Reason ->
error_logger:error_msg("calling logger:remove_handler(default) failed: ~p ~p",
[Err, Reason])
diff --git a/src/lager_common_test_backend.erl b/src/lager_common_test_backend.erl
index 0e03d6f..cf344a1 100644
--- a/src/lager_common_test_backend.erl
+++ b/src/lager_common_test_backend.erl
@@ -13,7 +13,7 @@
bounce/0,
bounce/1]).
-%% holds the log messages for retreival on terminate
+%% holds the log messages for retrieval on terminate
-record(state, {level :: {mask, integer()},
formatter :: atom(),
format_config :: any(),
diff --git a/src/lager_default_formatter.erl b/src/lager_default_formatter.erl
index 8ed2647..94048dd 100644
--- a/src/lager_default_formatter.erl
+++ b/src/lager_default_formatter.erl
@@ -381,7 +381,7 @@ basic_test_() ->
[metadata]
)))
},
- {"Metadata can be printed in its enterity with custom seperators",
+ {"Metadata can be printed in its enterity with custom separators",
?_assertEqual(iolist_to_binary(["bar->2, baz->3, foo->1"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
diff --git a/src/lager_file_backend.erl b/src/lager_file_backend.erl
index 6b1ab17..ed4ea68 100644
--- a/src/lager_file_backend.erl
+++ b/src/lager_file_backend.erl
@@ -20,7 +20,7 @@
%% @doc File backend for lager, with multiple file support.
%% Multiple files are supported, each with the path and the loglevel being
-%% configurable. The configuration paramter for this backend is a list of
+%% configurable. The configuration parameter for this backend is a list of
%% key-value 2-tuples. See the init() function for the available options.
%% This backend supports external and internal log
%% rotation and will re-open handles to files if the inode changes. It will
@@ -111,7 +111,7 @@ init([{FileName, LogLevel}, {Formatter,FormatterConfig}]) when is_list(FileName)
init(LogFileConfig) when is_list(LogFileConfig) ->
case validate_logfile_proplist(LogFileConfig) of
false ->
- %% falied to validate config
+ %% failed to validate config
{error, {fatal, bad_config}};
Config ->
%% probabably a better way to do this, but whatever
@@ -197,9 +197,9 @@ handle_info({rotate, File}, #state{name=File, count=Count, date=Date, rotator=Ro
schedule_rotation(File, Date),
{ok, State1};
handle_info({shaper_expired, Name}, #state{shaper=Shaper, name=Name, formatter=Formatter, formatter_config=FormatConfig} = State) ->
- _ = case Shaper#lager_shaper.dropped of
+ State1 = case Shaper#lager_shaper.dropped of
0 ->
- ok;
+ State;
Dropped ->
Report = io_lib:format(
"lager_file_backend dropped ~p messages in the last second that exceeded the limit of ~p messages/sec",
@@ -208,7 +208,7 @@ handle_info({shaper_expired, Name}, #state{shaper=Shaper, name=Name, formatter=F
write(State, lager_msg:timestamp(ReportMsg),
lager_msg:severity_as_int(ReportMsg), Formatter:format(ReportMsg, FormatConfig))
end,
- {ok, State#state{shaper=Shaper#lager_shaper{dropped=0, mps=0, lasttime=os:timestamp()}}};
+ {ok, State1#state{shaper=Shaper#lager_shaper{dropped=0, mps=0, lasttime=os:timestamp()}}};
handle_info(_Info, State) ->
{ok, State}.
@@ -248,8 +248,8 @@ write(#state{name=Name, fd=FD,
%% need to check for rotation
Buffer = {State0#state.sync_size, State0#state.sync_interval},
case Rotator:ensure_logfile(Name, FD, Inode, Ctime, Buffer) of
- {ok, {_FD, _Inode, _Ctime, Size}} when RotSize > 0, Size > RotSize ->
- State1 = close_file(State0),
+ {ok, {NewFD, NewInode, NewCtime, Size}} when RotSize > 0, Size > RotSize ->
+ State1 = close_file(State0#state{fd=NewFD, inode=NewInode, ctime=NewCtime}),
case Rotator:rotate_logfile(Name, Count) of
ok ->
%% go around the loop again, we'll do another rotation check and hit the next clause of ensure_logfile
diff --git a/src/lager_format.erl b/src/lager_format.erl
index 3233554..338abc2 100644
--- a/src/lager_format.erl
+++ b/src/lager_format.erl
@@ -110,7 +110,7 @@ pad_char(Fmt, Args) -> {$\s,Fmt,Args}.
%% collect_cc([FormatChar], [Argument]) ->
%% {Control,[ControlArg],[FormatChar],[Arg]}.
-%% Here we collect the argments for each control character.
+%% Here we collect the arguments for each control character.
%% Be explicit to cause failure early.
collect_cc([$w|Fmt], [A|Args]) -> {$w,[A],Fmt,Args};
diff --git a/src/lager_rotator_default.erl b/src/lager_rotator_default.erl
index 738585f..c5fa8ad 100644
--- a/src/lager_rotator_default.erl
+++ b/src/lager_rotator_default.erl
@@ -56,7 +56,7 @@ reopen_logfile(Name, FD0, Buffer) ->
_ = file:close(FD0),
_ = file:close(FD0),
case open_logfile(Name, Buffer) of
- {ok, {_FD1, _Inode, _Size, _Ctime}=FileInfo} ->
+ {ok, {_FD1, _Inode, _Ctime, _Size}=FileInfo} ->
%% inode changed, file was probably moved and
%% recreated
{ok, FileInfo};
diff --git a/src/lager_transform.erl b/src/lager_transform.erl
index bd03512..9330455 100644
--- a/src/lager_transform.erl
+++ b/src/lager_transform.erl
@@ -25,7 +25,11 @@
-include("lager.hrl").
--export([parse_transform/2]).
+-export([parse_transform/2, parse_transform_info/0]).
+
+%% @private
+parse_transform_info() ->
+ #{error_location => line}.
%% @private
parse_transform(AST, Options) ->
@@ -136,28 +140,41 @@ add_function_transforms(Line, DefaultAttrs, [{Atom, on_log, {Module, Function}}|
add_function_transforms(Line, {cons, Line, NewFunction, DefaultAttrs}, Remainder).
+build_dynamic_attrs(Line) ->
+ {cons, Line, {tuple, Line, [
+ {atom, Line, pid},
+ {call, Line, {atom, Line, pid_to_list}, [
+ {call, Line, {atom, Line ,self}, []}]}]},
+ {cons, Line, {tuple, Line, [
+ {atom, Line, node},
+ {call, Line, {atom, Line, node}, []}]},
+ %% get the metadata with lager:md(), this will always return a list so we can use it as the tail here
+ {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}.
+
+build_mf_attrs(Line, Attrs0) ->
+ {cons, Line, {tuple, Line, [
+ {atom, Line, module}, {atom, Line, get(module)}]},
+ {cons, Line, {tuple, Line, [
+ {atom, Line, function}, {atom, Line, get(function)}]},
+ Attrs0}}.
+
+build_loc_attrs(Line, Attrs0) when is_integer(Line) ->
+ {cons, Line, {tuple, Line, [
+ {atom, Line, line}, {integer, Line, Line}]},
+ Attrs0};
+build_loc_attrs(Line = {LineNum, Col}, Attrs0) when is_integer(LineNum), is_integer(Col) ->
+ {cons, Line, {tuple, Line, [
+ {atom, Line, line}, {integer, Line, LineNum}]},
+ {cons, Line, {tuple, Line, [
+ {atom, Line, col}, {integer, Line, Col}]},
+ Attrs0}}.
+
do_transform(Line, SinkName, Severity, Arguments0) ->
do_transform(Line, SinkName, Severity, Arguments0, safe).
do_transform(Line, SinkName, Severity, Arguments0, Safety) ->
SeverityAsInt=lager_util:level_to_num(Severity),
- DefaultAttrs0 = {cons, Line, {tuple, Line, [
- {atom, Line, module}, {atom, Line, get(module)}]},
- {cons, Line, {tuple, Line, [
- {atom, Line, function}, {atom, Line, get(function)}]},
- {cons, Line, {tuple, Line, [
- {atom, Line, line},
- {integer, Line, Line}]},
- {cons, Line, {tuple, Line, [
- {atom, Line, pid},
- {call, Line, {atom, Line, pid_to_list}, [
- {call, Line, {atom, Line ,self}, []}]}]},
- {cons, Line, {tuple, Line, [
- {atom, Line, node},
- {call, Line, {atom, Line, node}, []}]},
- %% get the metadata with lager:md(), this will always return a list so we can use it as the tail here
- {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}}}},
- %{nil, Line}}}}}}},
+ DefaultAttrs0 = build_mf_attrs(Line, build_loc_attrs(Line, build_dynamic_attrs(Line))),
Functions = get(functions),
DefaultAttrs1 = add_function_transforms(Line, DefaultAttrs0, Functions),
DefaultAttrs = case erlang:get(application) of
@@ -182,7 +199,7 @@ do_transform(Line, SinkName, Severity, Arguments0, Safety) ->
unsafe ->
do_log_unsafe
end,
- %% Wrap the call to lager:dispatch_log/6 in case that will avoid doing any work if this message is not elegible for logging
+ %% Wrap the call to lager:dispatch_log/6 in case that will avoid doing any work if this message is not eligible for logging
%% See lager.erl (lines 89-100) for lager:dispatch_log/6
%% case {whereis(Sink), whereis(?DEFAULT_SINK), lager_config:get({Sink, loglevel}, {?LOG_NONE, []})} of
{'case',Line,
diff --git a/src/lager_util.erl b/src/lager_util.erl
index a9b4645..a0e8478 100644
--- a/src/lager_util.erl
+++ b/src/lager_util.erl
@@ -77,6 +77,8 @@ num_to_level(?EMERGENCY) -> emergency;
num_to_level(?LOG_NONE) -> none.
-spec config_to_mask(atom()|string()) -> {'mask', integer()}.
+config_to_mask(silence) ->
+ silence;
config_to_mask(Conf) ->
Levels = config_to_levels(Conf),
{mask, lists:foldl(fun(Level, Acc) ->
@@ -419,6 +421,8 @@ trace_acc([{Key, '<', Val}|T], Acc) ->
check_traces(_, _, [], Acc) ->
lists:flatten(Acc);
+check_traces(Attrs, Level, [{_, silence, _} = Flow|Flows], Acc) ->
+ check_traces(Attrs, Level, Flows, [check_trace(Attrs, Flow)|Acc]);
check_traces(Attrs, Level, [{_, {mask, FilterLevel}, _}|Flows], Acc) when (Level band FilterLevel) == 0 ->
check_traces(Attrs, Level, Flows, Acc);
check_traces(Attrs, Level, [{Filter, _, _}|Flows], Acc) when length(Attrs) < length(Filter) ->
@@ -429,13 +433,16 @@ check_traces(Attrs, Level, [Flow|Flows], Acc) ->
check_trace(Attrs, {Filter, _Level, Dest}) when is_list(Filter) ->
check_trace(Attrs, {trace_all(Filter), _Level, Dest});
-check_trace(Attrs, {Filter, _Level, Dest}) when is_tuple(Filter) ->
+check_trace(Attrs, {Filter, Level, Dest} = F) when is_tuple(Filter) ->
Made = gre:make(Attrs, [list]),
glc:handle(?DEFAULT_TRACER, Made),
Match = glc_lib:matches(Filter, Made),
case Match of
true ->
- Dest;
+ case Level of
+ silence -> {silence, Dest};
+ _ -> Dest
+ end;
false ->
[]
end.
@@ -445,13 +452,17 @@ is_loggable(Msg, {mask, Mask}, MyName) ->
%% using syslog style comparison flags
%S = lager_msg:severity_as_int(Msg),
%?debugFmt("comparing masks ~.2B and ~.2B -> ~p~n", [S, Mask, S band Mask]),
- (lager_msg:severity_as_int(Msg) band Mask) /= 0 orelse
- lists:member(MyName, lager_msg:destinations(Msg));
+ (not lists:member({silence, MyName}, lager_msg:destinations(Msg)))
+ andalso
+ ((lager_msg:severity_as_int(Msg) band Mask) /= 0 orelse
+ lists:member(MyName, lager_msg:destinations(Msg)));
is_loggable(Msg, SeverityThreshold, MyName) when is_atom(SeverityThreshold) ->
is_loggable(Msg, level_to_num(SeverityThreshold), MyName);
is_loggable(Msg, SeverityThreshold, MyName) when is_integer(SeverityThreshold) ->
- lager_msg:severity_as_int(Msg) =< SeverityThreshold orelse
- lists:member(MyName, lager_msg:destinations(Msg)).
+ (not lists:member({silence, MyName}, lager_msg:destinations(Msg)))
+ andalso
+ (lager_msg:severity_as_int(Msg) =< SeverityThreshold orelse
+ lists:member(MyName, lager_msg:destinations(Msg))).
i2l(I) when I < 10 -> [$0, $0+I];
i2l(I) -> integer_to_list(I).
diff --git a/test/lager_test_backend.erl b/test/lager_test_backend.erl
index 78bee6c..1c7d763 100644
--- a/test/lager_test_backend.erl
+++ b/test/lager_test_backend.erl
@@ -437,9 +437,9 @@ lager_test_() ->
?assertEqual(0, count()),
lager:trace(?MODULE, [{module, ?MODULE}], debug),
?assertMatch({?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, _}, lager_config:get(loglevel)),
- %% elegible for tracing
+ %% eligible for tracing
ok = lager:info("hello world"),
- %% NOT elegible for tracing
+ %% NOT eligible for tracing
ok = lager:log(info, [{pid, self()}], "hello world"),
?assertEqual(1, count()),
ok
@@ -943,7 +943,7 @@ test_body(Expected, Actual) ->
"Trailing data \"~s\" following \"~s\"",
[Rest, Expected]);
{match, [{0, _}]} ->
- % the whole sting is " line NNN"
+ % the whole string is " line NNN"
ok;
{match, [{Off, _}]} ->
?debugFmt(
diff --git a/test/lager_trace_test.erl b/test/lager_trace_test.erl
index 48d722d..098d574 100644
--- a/test/lager_trace_test.erl
+++ b/test/lager_trace_test.erl
@@ -13,6 +13,7 @@
-define(SECOND_LOG_ENTRY_TIMEOUT, 1000). % 1 second
-define(FNAME, "test/test1.log").
+-define(FNAME2, "test/test2.log").
trace_test_() ->
{timeout,
@@ -48,7 +49,8 @@ trace_test_() ->
application:unset_env(lager, async_threshold),
error_logger:tty(true)
end,
- [{"Trace combined with log_root",
+ [
+ {"Trace combined with log_root",
fun() ->
lager:info([{tag, mytag}], "Test message"),
@@ -75,6 +77,77 @@ trace_test_() ->
end}
]}}.
+trace_silence_test_() ->
+ {timeout,
+ 10,
+ {foreach,
+ fun() ->
+ file:write_file(?FNAME2, ""),
+ error_logger:tty(false),
+ application:load(lager),
+ application:set_env(lager, log_root, "test"),
+ application:set_env(lager, handlers,
+ [{lager_file_backend,
+ [{file, "test2.log"},
+ {level, info},
+ {formatter, lager_default_formatter},
+ {formatter_config, [message, "\n"]}
+ ]}]),
+ application:set_env(lager, traces,
+ [{{lager_file_backend, "test2.log"},
+ [{tag, silencetag}], silence}]),
+ application:set_env(lager, error_logger_redirect, false),
+ application:set_env(lager, async_threshold, undefined),
+ lager:start()
+ end,
+ fun(_) ->
+ file:delete(?FNAME2),
+ application:stop(lager),
+ application:stop(goldrush),
+ application:unset_env(lager, log_root),
+ application:unset_env(lager, handlers),
+ application:unset_env(lager, traces),
+ application:unset_env(lager, error_logger_redirect),
+ application:unset_env(lager, async_threshold),
+ error_logger:tty(true)
+ end,
+ [
+ {"Trace filter silences output",
+ fun() ->
+ %% One message is supposed to get through, the other not
+ lager:info([{tag, mytag}], "Test message"),
+ lager:info([{tag, silencetag}], "This shouldn't get logged"),
+
+ % Wait until we have the expected log entry in the log file.
+ case wait_until(fun() ->
+ count_lines(?FNAME2) >= 1
+ end, ?FIRST_LOG_ENTRY_TIMEOUT) of
+ ok ->
+ ok;
+ {error, timeout} ->
+ throw({file_empty, file:read_file(?FNAME)})
+ end,
+
+ % Let's wait a little to see that we don't get a duplicate log
+ % entry.
+ case wait_until(fun() ->
+ count_lines(?FNAME2) >= 2
+ end, ?SECOND_LOG_ENTRY_TIMEOUT) of
+ ok ->
+ throw({too_many_entries, file:read_file(?FNAME)});
+ {error, timeout} ->
+ ok
+ end,
+ {ok, Bin} = file:read_file(?FNAME2),
+ case Bin of
+ <<"Test message\n">> ->
+ ok;
+ _ ->
+ throw({unexpected_output_in_log, Bin})
+ end
+ end}
+ ]}}.
+
% Wait until Fun() returns true.
wait_until(Fun, Timeout) ->
wait_until(Fun, Timeout, {8, 13}).
diff --git a/test/sync_error_logger.erl b/test/sync_error_logger.erl
index 078000f..bd1b609 100644
--- a/test/sync_error_logger.erl
+++ b/test/sync_error_logger.erl
@@ -21,7 +21,7 @@
%% The error_logger API, but synchronous!
%% This is helpful for tests, otherwise you need lots of nasty timer:sleep.
%% Additionally, the warning map can be set on a per-process level, for
-%% convienience, via the process dictionary value `warning_map'.
+%% convenience, via the process dictionary value `warning_map'.
-export([
info_msg/1, info_msg/2,