Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: fenek/lager
...
head fork: fenek/lager
Checking mergeability… Don't worry, you can still create the pull request.
  • 10 commits
  • 18 files changed
  • 0 commit comments
  • 2 contributors
Commits on Feb 12, 2012
@nialscorva nialscorva Changed the messages sent to the backends to include metadata and sep…
…arated formatting from the backend. Added documentation, fixed tests, and removed some unused code.
0139d98
Commits on Feb 27, 2012
@Vagabond Vagabond Merge branch 'master' of https://github.com/TensorWrench/lager into T…
…ensorWrench-master
d6d9c8f
@Vagabond Vagabond Change the default formatter to be backwards compatible with old beha…
…viour

Had to add a pseudo-ternary operator to the formatter to support this.
Also allowed lager:log to log metadata.
d590c49
Commits on Mar 12, 2012
@Vagabond Vagabond Update the console backend to use new default formatter for verbose mode 52593c9
Commits on Jun 25, 2012
@Vagabond Vagabond Merge branch 'TensorWrench-master' into integration
Conflicts:
	src/lager_app.erl
	test/lager_test_backend.erl
3badcb8
@Vagabond Vagabond Whitespace cleanups ec9756d
@Vagabond Vagabond Licencing headers 7854968
@Vagabond Vagabond Fix dialyzer warnings ca1d595
@Vagabond Vagabond Stricter dialyzer options 94c3f5f
Commits on Jun 28, 2012
@Vagabond Vagabond Merge branch 'master' into integration
Conflicts:
	test/crash.erl
	test/lager_test_backend.erl
6fa3e9f
View
1  .gitignore
@@ -4,3 +4,4 @@ ebin
doc
*.swp
erl_crash.dump
+.project
View
2  Makefile
@@ -39,7 +39,7 @@ dialyzer: compile
@echo Use "'make build_plt'" to build PLT prior to using this target.
@echo
@sleep 1
- dialyzer -Wno_return -Wunmatched_returns --plt $(COMBO_PLT) ebin | \
+ dialyzer -Wunmatched_returns --plt $(COMBO_PLT) ebin | \
fgrep -v -f ./dialyzer.ignore-warnings
cleanplt:
View
39 README.org
@@ -66,6 +66,45 @@
The available configuration options for each backend are listed in their
module's documentation.
+* Custom Formatting
+ All loggers have a default formatting that can be overriden. A formatter is any module that
+ exports format(#lager_log_message{},Config#any()). It is specified as part of the configuration
+ for the backend:
+
+#+BEGIN_EXAMPLE
+ {lager, [
+ {handlers, [
+ {lager_console_backend, [info, {lager_default_formatter, [time," [",severity,"] ", message, "\n"]}},
+ {lager_file_backend, [
+ [{"error.log", error, 10485760, "$D0", 5,{lager_default_formatter,[date, " ", time," [",severity,"] ",pid, " ", message, "\n"]}],
+ {"console.log", info, 10485760, "$D0", 5}
+ ]}
+ ]}
+ ]}.
+#+END_EXAMPLE
+
+ Included is lager_default_formatter. This provides a generic, default formatting for log messages using a "semi-iolist"
+ as configuration. Any iolist allowed elements in the configuration are printed verbatim. Atoms in the configuration
+ are treated as metadata properties and extracted from the log message.
+ The metadata properties date,time, message, and severity will always exist.
+ The properties pid, file, line, module, and function will always exist if the parser transform is used.
+
+#+BEGIN_EXAMPLE
+ ["Foo"] -> "Foo", regardless of message content.
+ [message] -> The content of the logged message, alone.
+ [{pid,"Unknown Pid"}] -> "<?.?.?>" if pid is in the metadata, "Unknown Pid" if not.
+ [date, " ", time," [",severity,"] ",pid, " ", message, "\n"] -> default formatting if none is provided
+#+END_EXAMPLE
+
+ Optionally, a tuple of {atom(),semi-iolist()}
+ can be used. The atom will look up the property, but if not found it will use the semi-iolist() instead. These fallbacks
+ can be nested or refer to other properties.
+
+#+BEGIN_EXAMPLE
+ [{pid,"Unknown Pid"}] -> "<?.?.?>" if pid is in the metadata, "Unknown Pid" if not.
+ [{server,[$(,{pid,"Unknown Server"},$)]}}] -> user provided server metadata, otherwise "(<?.?.?>)", otherwise "(Unknown Server)"
+#+END_EXAMPLE
+
* Error logger integration
Lager is also supplied with a error_logger handler module that translates
traditional erlang error messages into a friendlier format and sends them into
View
19 include/lager.hrl
@@ -14,6 +14,15 @@
%% specific language governing permissions and limitations
%% under the License.
+-record(lager_log_message,{
+ destinations,
+ metadata,
+ severity_as_int,
+ timestamp,
+ message
+ }).
+
+
-define(LEVELS,
[debug, info, notice, warning, error, critical, alert, emergency, none]).
@@ -55,9 +64,12 @@
lager_util:level_to_num(Level) =< element(1, lager_mochiglobal:get(loglevel, {?LOG_NONE, []}))).
-define(NOTIFY(Level, Pid, Format, Args),
- gen_event:notify(lager_event, {log, lager_util:level_to_num(Level),
- lager_util:format_time(), [io_lib:format("[~p] ", [Level]),
- io_lib:format("~p ", [Pid]), io_lib:format(Format, Args)]})).
+ gen_event:notify(lager_event,#lager_log_message{destinations=[],
+ message=io_lib:format(Format,Args),
+ metadata=[{pid,Pid},{line,?LINE},{file,?FILE},{module,?MODULE}],
+ timestamp=lager_util:format_time(),
+ severity_as_int=lager_util:level_to_num(Level)
+ })).
%% FOR INTERNAL USE ONLY
%% internal non-blocking logging call
@@ -93,3 +105,4 @@
end
end)).
-endif.
+
View
6 src/error_logger_lager_h.erl
@@ -102,11 +102,11 @@ handle_event(Event, State) ->
"Supervisor ~w had child ~s exit with reason ~s in context ~w",
[element(2, Name), Offender, format_reason(Reason), Ctx]);
_ ->
- ?LOG(error, Pid, ["SUPERVISOR REPORT ", print_silly_list(D)])
+ ?LOG(error, Pid, "SUPERVISOR REPORT " ++ print_silly_list(D))
end;
{error_report, _GL, {Pid, crash_report, [Self, Neighbours]}} ->
?CRASH_LOG(Event),
- ?LOG(error, Pid, ["CRASH REPORT ", format_crash_report(Self, Neighbours)]);
+ ?LOG(error, Pid, "CRASH REPORT " ++ format_crash_report(Self, Neighbours));
{warning_msg, _GL, {Pid, Fmt, Args}} ->
?LOG(warning, Pid, lager:safe_format(Fmt, Args, 4096));
{warning_report, _GL, {Pid, std_warning, Report}} ->
@@ -136,7 +136,7 @@ handle_event(Event, State) ->
?LOG(debug, P, "Supervisor ~w started ~s at pid ~w",
[element(2, Name), MFA, Pid]);
_ ->
- ?LOG(info, P, ["PROGRESS REPORT ", print_silly_list(D)])
+ ?LOG(info, P, "PROGRESS REPORT " ++ print_silly_list(D))
end;
_ ->
?LOG(warning, self(), "Unexpected error_logger event ~w", [Event])
View
111 src/lager.erl
@@ -19,15 +19,16 @@
-module(lager).
-include("lager.hrl").
+-include_lib("eunit/include/eunit.hrl").
%% API
-export([start/0,
- log/8, log_dest/9, log/3, log/4,
+ log/3, log/4,
trace_file/2, trace_file/3, trace_console/1, trace_console/2,
clear_all_traces/0, stop_trace/1, 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,dispatch_log/8]).
+ safe_format/3, safe_format_chop/3,dispatch_log/4]).
-type log_level() :: debug | info | notice | warning | error | critical | alert | emergency.
-type log_level_number() :: 0..7.
@@ -52,69 +53,52 @@ start_ok(App, {error, Reason}) ->
erlang:error({app_start_failed, App, Reason}).
--spec dispatch_log(log_level(), atom(), atom(), pos_integer(), pid(), list(), string(), list()) ->
- ok | {error, lager_not_running}.
+-spec dispatch_log(log_level(), list(), string(), list() | none) -> ok | {error, lager_not_running}.
+dispatch_log(Severity, Metadata, Format, Args) when is_atom(Severity)->
+ case whereis(lager_event) of
+ undefined ->
+ %% lager isn't running
+ {error, lager_not_running};
+ Pid ->
-dispatch_log(Severity, Module, Function, Line, Pid, Traces, Format, Args) ->
- {LevelThreshold,TraceFilters} = lager_mochiglobal:get(loglevel,{?LOG_NONE,[]}),
- Result=
- case LevelThreshold >= lager_util:level_to_num(Severity) of
- true -> lager:log(Severity,Module,Function,Line,Pid,
- lager_util:maybe_utc(lager_util:localtime_ms()),
- Format,Args);
- _ -> ok
- end,
- case TraceFilters of
- [] -> Result;
- Match when is_list(Match) ->
- lager:log_dest(Severity,Module,Function,Line,Pid,
- lager_util:maybe_utc(lager_util:localtime_ms()),
- lager_util:check_traces(Traces,
- lager_util:level_to_num(Severity),
- TraceFilters,
- []),
- Format,Args);
- _ -> ok
+ {LevelThreshold,TraceFilters} = lager_mochiglobal:get(loglevel,{?LOG_NONE,[]}),
+ SeverityAsInt=lager_util:level_to_num(Severity),
+ Destinations = case TraceFilters of
+ [] -> [];
+ _ ->
+ lager_util:check_traces(Metadata,SeverityAsInt,TraceFilters,[])
+ end,
+ case (LevelThreshold >= SeverityAsInt orelse Destinations =/= []) of
+ true ->
+ Timestamp = lager_util:format_time(),
+ Msg=case Args of
+ A when is_list(A) ->safe_format_chop(Format,Args,4096);
+ _ -> Format
+ end,
+ gen_event:sync_notify(Pid, #lager_log_message{destinations=Destinations,
+ metadata=Metadata,
+ severity_as_int=SeverityAsInt,
+ timestamp=Timestamp,
+ message=Msg});
+ _ ->
+ ok
+ end
end.
-%% @private
--spec log(log_level(), atom(), atom(), pos_integer(), pid(), tuple(), string(), list()) ->
- ok | {error, lager_not_running}.
-log(Level, Module, Function, Line, Pid, Time, Format, Args) ->
- Timestamp = lager_util:format_time(Time),
- Msg = [["[", atom_to_list(Level), "] "],
- io_lib:format("~p@~p:~p:~p ", [Pid, Module, Function, Line]),
- safe_format_chop(Format, Args, 4096)],
- safe_notify({log, lager_util:level_to_num(Level), Timestamp, Msg}).
-
-%% @private
--spec log_dest(log_level(), atom(), atom(), pos_integer(), pid(), tuple(), list(), string(), list()) ->
- ok | {error, lager_not_running}.
-log_dest(_Level, _Module, _Function, _Line, _Pid, _Time, [], _Format, _Args) ->
- ok;
-log_dest(Level, Module, Function, Line, Pid, Time, Dest, Format, Args) ->
- Timestamp = lager_util:format_time(Time),
- Msg = [["[", atom_to_list(Level), "] "],
- io_lib:format("~p@~p:~p:~p ", [Pid, Module, Function, Line]),
- safe_format_chop(Format, Args, 4096)],
- safe_notify({log, Dest, lager_util:level_to_num(Level), Timestamp, Msg}).
-
-
%% @doc Manually log a message into lager without using the parse transform.
--spec log(log_level(), pid(), list()) -> ok | {error, lager_not_running}.
-log(Level, Pid, Message) ->
- Timestamp = lager_util:format_time(),
- Msg = [["[", atom_to_list(Level), "] "], io_lib:format("~p ", [Pid]),
- safe_format_chop("~s", [Message], 4096)],
- safe_notify({log, lager_util:level_to_num(Level), Timestamp, Msg}).
+-spec log(log_level(), pid() | [tuple(),...], list()) -> ok | {error, lager_not_running}.
+log(Level, Pid, Message) when is_pid(Pid) ->
+ dispatch_log(Level, [{pid,Pid}], Message, []);
+log(Level, Metadata, Message) when is_list(Metadata) ->
+ dispatch_log(Level, Metadata, Message, []).
%% @doc Manually log a message into lager without using the parse transform.
--spec log(log_level(), pid(), string(), list()) -> ok | {error, lager_not_running}.
-log(Level, Pid, Format, Args) ->
- Timestamp = lager_util:format_time(),
- Msg = [["[", atom_to_list(Level), "] "], io_lib:format("~p ", [Pid]),
- safe_format_chop(Format, Args, 4096)],
- safe_notify({log, lager_util:level_to_num(Level), Timestamp, Msg}).
+-spec log(log_level(), pid() | [tuple(),...], string(), list()) -> ok | {error, lager_not_running}.
+log(Level, Pid, Format, Args) when is_pid(Pid) ->
+ dispatch_log(Level, [{pid,Pid}], Format, Args);
+log(Level, Metadata, Format, Args) when is_list(Metadata) ->
+ dispatch_log(Level, Metadata, Format, Args).
+
trace_file(File, Filter) ->
trace_file(File, Filter, debug).
@@ -270,15 +254,6 @@ minimum_loglevel([]) ->
minimum_loglevel(Levels) ->
erlang:hd(lists:reverse(lists:sort(Levels))).
-safe_notify(Event) ->
- case whereis(lager_event) of
- undefined ->
- %% lager isn't running
- {error, lager_not_running};
- Pid ->
- gen_event:sync_notify(Pid, Event)
- end.
-
%% @doc Print the format string `Fmt' with `Args' safely with a size
%% limit of `Limit'. If the format string is invalid, or not enough
%% arguments are supplied 'FORMAT ERROR' is printed with the offending
View
42 src/lager_app.erl
@@ -22,7 +22,9 @@
-behaviour(application).
-include("lager.hrl").
-
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+-endif.
-export([start/0,
start/2,
stop/1]).
@@ -77,7 +79,43 @@ stop(Handlers) ->
expand_handlers([]) ->
[];
expand_handlers([{lager_file_backend, Configs}|T]) ->
- [{{lager_file_backend, element(1, Config)}, Config} || Config <- Configs] ++
+ [ to_config(Config) || Config <- Configs] ++
expand_handlers(T);
expand_handlers([H|T]) ->
[H | expand_handlers(T)].
+
+to_config({Name,Severity}) ->
+ {{lager_file_backend, Name}, {Name, Severity}};
+to_config({Name,_Severity,_Size,_Rotation,_Count}=Config) ->
+ {{lager_file_backend, Name}, Config};
+to_config({Name,Severity,Size,Rotation,Count,Format}) ->
+ {{lager_file_backend, Name}, {Name,Severity,Size,Rotation,Count},Format}.
+
+
+
+-ifdef(TEST).
+application_config_mangling_test_() ->
+ [{"Explode the file backend handlers",
+ ?_assertMatch(
+ [{lager_console_backend, info},
+ {{lager_file_backend,"error.log"},{"error.log",error,10485760, "$D0",5}},
+ {{lager_file_backend,"console.log"},{"console.log",info,10485760, "$D0",5}}
+ ],
+ expand_handlers([{lager_console_backend, info},
+ {lager_file_backend, [
+ {"error.log", error, 10485760, "$D0", 5},
+ {"console.log", info, 10485760, "$D0", 5}
+ ]}]
+ ))},
+ {"Explode with formatter info",
+ ?_assertMatch(
+ [{{lager_file_backend,"test.log"}, {"test.log", debug, 10485760, "$D0", 5},{lager_default_formatter,["[",severity,"] ", message, "\n"]}},
+ {{lager_file_backend,"test2.log"}, {"test2.log",debug, 10485760, "$D0", 5},{lager_default_formatter,["2>[",severity,"] ", message, "\n"]}}],
+ expand_handlers([{lager_file_backend, [
+ {"test.log", debug, 10485760, "$D0", 5,{lager_default_formatter,["[",severity,"] ", message, "\n"]}},
+ {"test2.log",debug, 10485760, "$D0", 5,{lager_default_formatter,["2>[",severity,"] ",message, "\n"]}}
+ ]
+ }])
+ )
+ }].
+-endif.
View
56 src/lager_backend_utils.erl
@@ -0,0 +1,56 @@
+%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%%
+%% This file is provided to you under the Apache License,
+%% Version 2.0 (the "License"); you may not use this file
+%% except in compliance with the License. You may obtain
+%% a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+
+%% @doc Module containing useful functions for writing lager backends.
+
+-module(lager_backend_utils).
+
+%%
+%% Include files
+%%
+-include_lib("lager/include/lager.hrl").
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+-endif.
+
+%%
+%% Exported Functions
+%%
+-export([is_loggable/3]).
+
+%%
+%% API Functions
+%%
+
+
+-spec is_loggable(#lager_log_message{},integer(),term()) -> boolean().
+is_loggable(#lager_log_message{severity_as_int=Severity,destinations=Destinations},SeverityThreshold,MyName) ->
+ %?debugFmt("is_loggable: Severity=~p, Threshold=~p, Destinations=~p, MyName=~p", [Severity,SeverityThreshold,Destinations,MyName]),
+ Severity =< SeverityThreshold orelse lists:member(MyName, Destinations).
+
+
+-ifdef(TEST).
+
+is_loggable_test_() ->
+ [
+ {"Loggable by severity only", ?_assert(is_loggable(#lager_log_message{severity_as_int=1,destinations=[]},2,me))},
+ {"Not loggable by severity only", ?_assertNot(is_loggable(#lager_log_message{severity_as_int=2,destinations=[]},1,me))},
+ {"Loggable by severity with destination", ?_assert(is_loggable(#lager_log_message{severity_as_int=1,destinations=[you]},2,me))},
+ {"Not loggable by severity with destination", ?_assertNot(is_loggable(#lager_log_message{severity_as_int=2,destinations=[you]},1,me))},
+ {"Loggable by destination overriding severity", ?_assert(is_loggable(#lager_log_message{severity_as_int=2,destinations=[me]},1,me))}
+ ].
+
+-endif.
View
51 src/lager_console_backend.erl
@@ -24,7 +24,7 @@
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
code_change/3]).
--record(state, {level, verbose}).
+-record(state, {level, formatter,format_config}).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
@@ -33,18 +33,21 @@
-include("lager.hrl").
+-define(TERSE_FORMAT,[time, " [", severity,"] ", message, "\n"]).
+
%% @private
init(Level) when is_atom(Level) ->
+ init([Level,{lager_default_formatter,?TERSE_FORMAT}]);
+init([Level, true]) -> % for backwards compatibility
+ init([Level,{lager_default_formatter,[]}]);
+init([Level,false]) -> % for backwards compatibility
+ init([Level,{lager_default_formatter,?TERSE_FORMAT}]);
+init([Level,{Formatter,FormatterConfig}]) when is_atom(Level), is_atom(Formatter)->
case lists:member(Level, ?LEVELS) of
true ->
- {ok, #state{level=lager_util:level_to_num(Level), verbose=false}};
- _ ->
- {error, bad_log_level}
- end;
-init([Level, Verbose]) ->
- case lists:member(Level, ?LEVELS) of
- true ->
- {ok, #state{level=lager_util:level_to_num(Level), verbose=Verbose}};
+ {ok, #state{level=lager_util:level_to_num(Level),
+ formatter=Formatter,
+ format_config=FormatterConfig}};
_ ->
{error, bad_log_level}
end.
@@ -64,29 +67,15 @@ handle_call(_Request, State) ->
{ok, ok, State}.
%% @private
-handle_event({log, Dest, Level, {Date, Time}, [LevelStr, Location, Message]},
- #state{level=L, verbose=Verbose} = State) when Level > L ->
- case lists:member(lager_console_backend, Dest) of
+handle_event(#lager_log_message{}=Message,
+ #state{level=L,formatter=Formatter,format_config=FormatConfig} = State) ->
+ case lager_backend_utils:is_loggable(Message, L, ?MODULE) of
true ->
- case Verbose of
- true ->
- io:put_chars([Date, " ", Time, " ", LevelStr, Location, Message, "\n"]);
- _ ->
- io:put_chars([Time, " ", LevelStr, Message, "\n"])
- end,
+ io:put_chars(Formatter:format(Message,FormatConfig)),
{ok, State};
false ->
{ok, State}
end;
-handle_event({log, Level, {Date, Time}, [LevelStr, Location, Message]},
- #state{level=LogLevel, verbose=Verbose} = State) when Level =< LogLevel ->
- case Verbose of
- true ->
- io:put_chars([Date, " ", Time, " ", LevelStr, Location, Message, "\n"]);
- _ ->
- io:put_chars([Time, " ", LevelStr, Message, "\n"])
- end,
- {ok, State};
handle_event(_Event, State) ->
{ok, State}.
@@ -142,6 +131,7 @@ console_log_test_() ->
Pid = spawn(F(self())),
gen_event:add_handler(lager_event, lager_console_backend, info),
erlang:group_leader(Pid, whereis(lager_event)),
+ lager_mochiglobal:put(loglevel, {?INFO, []}),
lager:log(info, self(), "Test message"),
receive
{io_request, From, ReplyAs, {put_chars, unicode, Msg}} ->
@@ -158,12 +148,13 @@ console_log_test_() ->
Pid = spawn(F(self())),
erlang:group_leader(Pid, whereis(lager_event)),
gen_event:add_handler(lager_event, lager_console_backend, [info, true]),
- lager:log(info, self(), "Test message"),
- lager:log(info, self(), "Test message"),
+ lager_mochiglobal:put(loglevel, {?INFO, []}),
+ lager:info("Test message"),
+ lager:info("Test message"),
PidStr = pid_to_list(self()),
receive
{io_request, _, _, {put_chars, unicode, Msg}} ->
- ?assertMatch([_, _, "[info]", PidStr, "Test message\n"], re:split(Msg, " ", [{return, list}, {parts, 5}]))
+ ?assertMatch([_, _, "[info]", PidStr, _,"Test message\n"], re:split(Msg, "[ @]", [{return, list}, {parts, 6}]))
after
500 ->
?assert(false)
View
8 src/lager_crash_log.erl
@@ -53,7 +53,7 @@
date,
count,
flap=false
- }).
+}).
%% @private
start_link(Filename, MaxBytes, Size, Date, Count) ->
@@ -273,7 +273,7 @@ filesystem_test_() ->
file:write_file_info("crash_test.log", FInfo#file_info{mode = 0}),
{ok, _} = ?MODULE:start_link("crash_test.log", 65535, 0, undefined, 0),
?assertEqual(1, lager_test_backend:count()),
- {_Level, _Time, [_, _, Message]} = lager_test_backend:pop(),
+ {_Level, _Time, Message,_Metadata} = lager_test_backend:pop(),
?assertEqual("Failed to open crash log file crash_test.log with error: permission denied", lists:flatten(Message))
end
},
@@ -292,7 +292,7 @@ filesystem_test_() ->
_ = gen_event:which_handlers(error_logger),
?assertEqual(3, lager_test_backend:count()),
lager_test_backend:pop(),
- {_Level, _Time, [_, _, Message]} = lager_test_backend:pop(),
+ {_Level, _Time, Message,_Metadata} = lager_test_backend:pop(),
?assertEqual("Failed to reopen crash log crash_test.log with error: permission denied", lists:flatten(Message))
end
},
@@ -303,7 +303,7 @@ filesystem_test_() ->
file:write_file_info("crash_test.log", FInfo#file_info{mode = 0}),
{ok, _} = ?MODULE:start_link("crash_test.log", 65535, 0, undefined, 0),
?assertEqual(1, lager_test_backend:count()),
- {_Level, _Time, [_, _, Message]} = lager_test_backend:pop(),
+ {_Level, _Time, Message,_Metadata} = lager_test_backend:pop(),
?assertEqual("Failed to open crash log file crash_test.log with error: permission denied", lists:flatten(Message)),
file:write_file_info("crash_test.log", FInfo#file_info{mode = OldPerms}),
sync_error_logger:error_msg("Test message~n"),
View
150 src/lager_default_formatter.erl
@@ -0,0 +1,150 @@
+%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%%
+%% This file is provided to you under the Apache License,
+%% Version 2.0 (the "License"); you may not use this file
+%% except in compliance with the License. You may obtain
+%% a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+
+-module(lager_default_formatter).
+
+%%
+%% Include files
+%%
+-include_lib("lager/include/lager.hrl").
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+-endif.
+
+%%
+%% Exported Functions
+%%
+-export([format/2]).
+
+%%
+%% API Functions
+%%
+
+%% @doc Provides a generic, default formatting for log messages using a semi-iolist as configuration. Any iolist allowed
+%% elements in the configuration are printed verbatim. Atoms in the configuration are treated as metadata properties
+%% and extracted from the log message. Optionally, a tuple of {atom(),semi-iolist()} can be used. The atom will look
+%% up the property, but if not found it will use the semi-iolist() instead. These fallbacks can be similarly nested
+%% or refer to other properties, if desired.
+%%
+%% The metadata properties date,time, message, and severity will always exist.
+%% The properties pid, file, line, module, and function will always exist if the parser transform is used.
+%%
+%% Example:
+%% ["Foo"] -> "Foo", regardless of message content.
+%% [message] -> The content of the logged message, alone.
+%% [{pid,"Unknown Pid"}] -> "<?.?.?>" if pid is in the metadata, "Unknown Pid" if not.
+%% [date, " ", time," [",severity,"] ",pid, " ", message, "\n"] -> default formatting if none is provided
+%% @end
+-spec format(#lager_log_message{},list()) -> any().
+format(#lager_log_message{}=Msg,[]) ->
+ format(Msg,
+ [date, " ", time, " [", severity, "] ",
+ {pid, ""},
+ {module, [
+ {pid, ["@"], ""},
+ module,
+ {function, [":", function], ""},
+ {line, [":",line], ""}], ""},
+ " ", message, "\n"]);
+format(Message,Config) ->
+ [ output(V,Message) || V <- Config ].
+
+
+-spec output(term(),#lager_log_message{}) -> iolist().
+output(message,#lager_log_message{message=M}) -> M;
+output(date,#lager_log_message{timestamp={D,_T}}) -> D;
+output(time,#lager_log_message{timestamp={_D,T}}) -> T;
+output(severity,#lager_log_message{severity_as_int=S}) ->
+ atom_to_list(lager_util:num_to_level(S));
+output(Prop,#lager_log_message{metadata=Metadata}) when is_atom(Prop) ->
+ make_printable(get_metadata(Prop,Metadata,<<"Undefined">>));
+output({Prop,Default},#lager_log_message{metadata=Metadata}=L) when is_atom(Prop) ->
+ make_printable(get_metadata(Prop,Metadata,output(Default,L)));
+output({Prop, Present, Absent}, #lager_log_message{metadata=Metadata}=L) when is_atom(Prop) ->
+ %% sort of like a poor man's ternary operator
+ case get_metadata(Prop, Metadata) of
+ undefined ->
+ [ output(V, L) || V <- Absent];
+ _ ->
+ [ output(V, L) || V <- Present]
+ end;
+output(Other,_) -> make_printable(Other).
+
+-spec make_printable(any()) -> iolist().
+make_printable(A) when is_atom(A) -> atom_to_list(A);
+make_printable(P) when is_pid(P) -> pid_to_list(P);
+make_printable(L) when is_list(L) orelse is_binary(L) -> L;
+make_printable(Other) -> io_lib:format("~p",[Other]).
+
+get_metadata(Key, Metadata) ->
+ get_metadata(Key, Metadata, undefined).
+
+get_metadata(Key, Metadata, Default) ->
+ case lists:keyfind(Key, 1, Metadata) of
+ false ->
+ Default;
+ {Key, Value} ->
+ Value
+ end.
+
+-ifdef(TEST).
+basic_test_() ->
+ [{"Default formatting test",
+ ?_assertEqual(iolist_to_binary([<<"Day Time [error] ">>, pid_to_list(self()), <<" Message\n">>]),
+ iolist_to_binary(format(#lager_log_message{timestamp={"Day","Time"},
+ message="Message",
+ severity_as_int=lager_util:level_to_num(error),
+ metadata=[{pid,self()}]},
+ [])))
+ },
+ {"Basic Formatting",
+ ?_assertEqual(<<"Simplist Format">>,
+ iolist_to_binary(format(#lager_log_message{timestamp={"Day","Time"},
+ message="Message",
+ severity_as_int=lager_util:level_to_num(error),
+ metadata=[{pid,self()}]},
+ ["Simplist Format"])))
+ },
+ {"Default equivalent formatting test",
+ ?_assertEqual(iolist_to_binary([<<"Day Time [error] ">>, pid_to_list(self()), <<" Message\n">>]),
+ iolist_to_binary(format(#lager_log_message{timestamp={"Day","Time"},
+ message="Message",
+ severity_as_int=lager_util:level_to_num(error),
+ metadata=[{pid,self()}]},
+ [date, " ", time," [",severity,"] ",pid, " ", message, "\n"]
+ )))
+ },
+ {"Non existant metadata can default to string",
+ ?_assertEqual(iolist_to_binary([<<"Day Time [error] Fallback Message\n">>]),
+ iolist_to_binary(format(#lager_log_message{timestamp={"Day","Time"},
+ message="Message",
+ severity_as_int=lager_util:level_to_num(error),
+ metadata=[]},
+ [date, " ", time," [",severity,"] ",{does_not_exist,"Fallback"}, " ", message, "\n"]
+ )))
+ },
+ {"Non existant metadata can default to other metadata",
+ ?_assertEqual(iolist_to_binary([<<"Day Time [error] Fallback Message\n">>]),
+ iolist_to_binary(format(#lager_log_message{timestamp={"Day","Time"},
+ message="Message",
+ severity_as_int=lager_util:level_to_num(error),
+ metadata=[{pid,"Fallback"}]},
+ [date, " ", time," [",severity,"] ",{does_not_exist,pid}, " ", message, "\n"]
+ )))
+ }
+ ].
+
+-endif.
View
63 src/lager_file_backend.erl
@@ -49,29 +49,37 @@
flap=false :: boolean(),
size = 0 :: integer(),
date,
- count = 10
+ count = 10,
+ formatter,
+ formatter_config
}).
%% @private
-spec init([{string(), lager:log_level()},...]) -> {ok, #state{}}.
-init(LogFile) ->
+init([LogFile,{Formatter}]) ->
+ init([LogFile,{Formatter,[]}]);
+init([LogFile,{Formatter,FormatterConfig}]) ->
case validate_logfile(LogFile) of
{Name, Level, Size, Date, Count} ->
schedule_rotation(Name, Date),
State = case lager_util:open_logfile(Name, true) of
{ok, {FD, Inode, _}} ->
#state{name=Name, level=lager_util:level_to_num(Level),
- fd=FD, inode=Inode, size=Size, date=Date, count=Count};
+ fd=FD, inode=Inode, size=Size, date=Date, count=Count, formatter=Formatter, formatter_config=FormatterConfig};
{error, Reason} ->
?INT_LOG(error, "Failed to open log file ~s with error ~s",
[Name, file:format_error(Reason)]),
#state{name=Name, level=lager_util:level_to_num(Level),
- flap=true, size=Size, date=Date, count=Count}
+ flap=true, size=Size, date=Date, count=Count, formatter=Formatter, formatter_config=FormatterConfig}
end,
+
{ok, State};
false ->
ignore
- end.
+ end;
+init(LogFile) ->
+ init([LogFile,{lager_default_formatter,[]}]).
+
%% @private
handle_call({set_loglevel, Level}, #state{name=Ident} = State) ->
@@ -83,17 +91,14 @@ handle_call(_Request, State) ->
{ok, ok, State}.
%% @private
-handle_event({log, Dest, Level, {Date, Time}, Message},
- #state{name=Name, level=L} = State) when Level > L ->
- case lists:member({lager_file_backend, Name}, Dest) of
+handle_event(Message,
+ #state{name=Name, level=L,formatter=Formatter,formatter_config=FormatConfig} = State) ->
+ case lager_backend_utils:is_loggable(Message,L,{lager_file_backend, Name}) of
true ->
- {ok, write(State, Level, [Date, " ", Time, " ", Message, "\n"])};
+ {ok,write(State, Message#lager_log_message.severity_as_int, Formatter:format(Message,FormatConfig)) };
false ->
{ok, State}
end;
-handle_event({log, Level, {Date, Time}, Message}, #state{level=L} = State) when Level =< L->
- NewState = write(State, Level, [Date, " ", Time, " ", Message, "\n"]),
- {ok, NewState};
handle_event(_Event, State) ->
{ok, State}.
@@ -261,7 +266,7 @@ filesystem_test_() ->
file:write_file_info("test.log", FInfo#file_info{mode = 0}),
gen_event:add_handler(lager_event, lager_file_backend, {"test.log", info}),
?assertEqual(1, lager_test_backend:count()),
- {_Level, _Time, [_, _, Message]} = lager_test_backend:pop(),
+ {_Level, _Time,Message,_Metadata} = lager_test_backend:pop(),
?assertEqual("Failed to open log file test.log with error permission denied", lists:flatten(Message))
end
},
@@ -279,7 +284,7 @@ filesystem_test_() ->
?assertEqual(3, lager_test_backend:count()),
lager_test_backend:pop(),
lager_test_backend:pop(),
- {_Level, _Time, [_, _, Message]} = lager_test_backend:pop(),
+ {_Level, _Time, Message,_Metadata} = lager_test_backend:pop(),
?assertEqual("Failed to reopen log file test.log with error permission denied", lists:flatten(Message))
end
},
@@ -290,7 +295,7 @@ filesystem_test_() ->
file:write_file_info("test.log", FInfo#file_info{mode = 0}),
gen_event:add_handler(lager_event, lager_file_backend, {"test.log", info}),
?assertEqual(1, lager_test_backend:count()),
- {_Level, _Time, [_, _, Message]} = lager_test_backend:pop(),
+ {_Level, _Time, Message,_Metadata} = lager_test_backend:pop(),
?assertEqual("Failed to open log file test.log with error permission denied", lists:flatten(Message)),
file:write_file_info("test.log", FInfo#file_info{mode = OldPerms}),
lager:log(error, self(), "Test message"),
@@ -352,6 +357,7 @@ filesystem_test_() ->
?MODULE}], ?DEBUG,
{lager_file_backend, "test.log"}}]}),
lager:error("Test message"),
+ timer:sleep(1000),
{ok, Bin} = file:read_file("test.log"),
?assertMatch([_, _, "[error]", _, "Test message\n"], re:split(Bin, " ", [{return, list}, {parts, 5}]))
end
@@ -389,6 +395,33 @@ filesystem_test_() ->
]
}.
+formatting_test_() ->
+ {foreach,
+ fun() ->
+ file:write_file("test.log", ""),
+ file:write_file("test2.log", ""),
+ error_logger:tty(false),
+ application:load(lager),
+ application:set_env(lager, handlers, [{lager_test_backend, info}]),
+ application:set_env(lager, error_logger_redirect, false),
+ application:start(lager)
+ end,
+ fun(_) ->
+ file:delete("test.log"),
+ file:delete("test2.log"),
+ application:stop(lager),
+ error_logger:tty(true)
+ end,
+ [{"Should have two log files, the second prefixed with 2>",
+ fun() ->
+ gen_event:add_handler(lager_event, lager_file_backend,[{"test.log", debug},{lager_default_formatter,["[",severity,"] ", message, "\n"]}]),
+ gen_event:add_handler(lager_event, lager_file_backend,[{"test2.log", debug},{lager_default_formatter,["2> [",severity,"] ", message, "\n"]}]),
+ lager:log(error, self(), "Test message"),
+ ?assertMatch({ok, <<"[error] Test message\n">>},file:read_file("test.log")),
+ ?assertMatch({ok, <<"2> [error] Test message\n">>},file:read_file("test2.log"))
+ end
+ }
+ ]}.
-endif.
View
105 src/lager_format.erl
@@ -88,7 +88,7 @@ field_value(Fmt, Args) ->
field_value([C|Fmt], Args, F) when is_integer(C), C >= $0, C =< $9 ->
field_value(Fmt, Args, 10*F + (C - $0));
-field_value(Fmt, Args, F) -> %Default case
+field_value(Fmt, Args, F) -> %Default case
{F,Fmt,Args}.
pad_char([$.,$*|Fmt], [Pad|Args]) -> {Pad,Fmt,Args};
@@ -96,7 +96,7 @@ pad_char([$.,Pad|Fmt], Args) -> {Pad,Fmt,Args};
pad_char(Fmt, Args) -> {$\s,Fmt,Args}.
%% collect_cc([FormatChar], [Argument]) ->
-%% {Control,[ControlArg],[FormatChar],[Arg]}.
+%% {Control,[ControlArg],[FormatChar],[Arg]}.
%% Here we collect the argments for each control character.
%% Be explicit to cause failure early.
@@ -151,8 +151,7 @@ build2([C|Cs], Count, MaxLen) ->
build2([], _, _) -> [].
%% control(FormatChar, [Argument], FieldWidth, Adjust, Precision, PadChar,
-%% Indentation) ->
-%% [Char]
+%% Indentation) -> [Char]
%% This is the main dispatch function for the various formatting commands.
%% Field widths and precisions have already been calculated.
@@ -277,15 +276,15 @@ term(T, F, Adj, P0, Pad) ->
L = lists:flatlength(T),
P = case P0 of none -> erlang:min(L, F); _ -> P0 end,
if
- L > P ->
- adjust(chars($*, P), chars(Pad, F-P), Adj);
- F >= P ->
- adjust(T, chars(Pad, F-L), Adj)
+ L > P ->
+ adjust(chars($*, P), chars(Pad, F-P), Adj);
+ F >= P ->
+ adjust(T, chars(Pad, F-L), Adj)
end.
%% fwrite_e(Float, Field, Adjust, Precision, PadChar)
-fwrite_e(Fl, none, Adj, none, Pad) -> %Default values
+fwrite_e(Fl, none, Adj, none, Pad) -> %Default values
fwrite_e(Fl, none, Adj, 6, Pad);
fwrite_e(Fl, none, _Adj, P, _Pad) when P >= 2 ->
float_e(Fl, float_data(Fl), P);
@@ -294,12 +293,12 @@ fwrite_e(Fl, F, Adj, none, Pad) ->
fwrite_e(Fl, F, Adj, P, Pad) when P >= 2 ->
term(float_e(Fl, float_data(Fl), P), F, Adj, F, Pad).
-float_e(Fl, Fd, P) when Fl < 0.0 -> %Negative numbers
+float_e(Fl, Fd, P) when Fl < 0.0 -> %Negative numbers
[$-|float_e(-Fl, Fd, P)];
float_e(_Fl, {Ds,E}, P) ->
case float_man(Ds, 1, P-1) of
- {[$0|Fs],true} -> [[$1|Fs]|float_exp(E)];
- {Fs,false} -> [Fs|float_exp(E-1)]
+ {[$0|Fs],true} -> [[$1|Fs]|float_exp(E)];
+ {Fs,false} -> [Fs|float_exp(E-1)]
end.
%% float_man([Digit], Icount, Dcount) -> {[Chars],CarryFlag}.
@@ -312,22 +311,22 @@ float_man(Ds, 0, Dc) ->
{[$.|Cs],C};
float_man([D|Ds], I, Dc) ->
case float_man(Ds, I-1, Dc) of
- {Cs,true} when D =:= $9 -> {[$0|Cs],true};
- {Cs,true} -> {[D+1|Cs],false};
- {Cs,false} -> {[D|Cs],false}
+ {Cs,true} when D =:= $9 -> {[$0|Cs],true};
+ {Cs,true} -> {[D+1|Cs],false};
+ {Cs,false} -> {[D|Cs],false}
end;
-float_man([], I, Dc) -> %Pad with 0's
+float_man([], I, Dc) -> %Pad with 0's
{string:chars($0, I, [$.|string:chars($0, Dc)]),false}.
float_man([D|_], 0) when D >= $5 -> {[],true};
float_man([_|_], 0) -> {[],false};
float_man([D|Ds], Dc) ->
case float_man(Ds, Dc-1) of
- {Cs,true} when D =:= $9 -> {[$0|Cs],true};
- {Cs,true} -> {[D+1|Cs],false};
- {Cs,false} -> {[D|Cs],false}
+ {Cs,true} when D =:= $9 -> {[$0|Cs],true};
+ {Cs,true} -> {[D+1|Cs],false};
+ {Cs,false} -> {[D|Cs],false}
end;
-float_man([], Dc) -> {string:chars($0, Dc),false}. %Pad with 0's
+float_man([], Dc) -> {string:chars($0, Dc),false}. %Pad with 0's
%% float_exp(Exponent) -> [Char].
%% Generate the exponent of a floating point number. Always include sign.
@@ -339,7 +338,7 @@ float_exp(E) ->
%% fwrite_f(FloatData, Field, Adjust, Precision, PadChar)
-fwrite_f(Fl, none, Adj, none, Pad) -> %Default values
+fwrite_f(Fl, none, Adj, none, Pad) -> %Default values
fwrite_f(Fl, none, Adj, 6, Pad);
fwrite_f(Fl, none, _Adj, P, _Pad) when P >= 1 ->
float_f(Fl, float_data(Fl), P);
@@ -351,11 +350,11 @@ fwrite_f(Fl, F, Adj, P, Pad) when P >= 1 ->
float_f(Fl, Fd, P) when Fl < 0.0 ->
[$-|float_f(-Fl, Fd, P)];
float_f(Fl, {Ds,E}, P) when E =< 0 ->
- float_f(Fl, {string:chars($0, -E+1, Ds),1}, P); %Prepend enough 0's
+ float_f(Fl, {string:chars($0, -E+1, Ds),1}, P); %Prepend enough 0's
float_f(_Fl, {Ds,E}, P) ->
case float_man(Ds, E, P) of
- {Fs,true} -> "1" ++ Fs; %Handle carry
- {Fs,false} -> Fs
+ {Fs,true} -> "1" ++ Fs; %Handle carry
+ {Fs,false} -> Fs
end.
%% float_data([FloatChar]) -> {[Digit],Exponent}
@@ -380,20 +379,20 @@ fwrite_g(Fl, F, Adj, none, Pad) ->
fwrite_g(Fl, F, Adj, P, Pad) when P >= 1 ->
A = abs(Fl),
E = if A < 1.0e-1 -> -2;
- A < 1.0e0 -> -1;
- A < 1.0e1 -> 0;
- A < 1.0e2 -> 1;
- A < 1.0e3 -> 2;
- A < 1.0e4 -> 3;
- true -> fwrite_f
- end,
+ A < 1.0e0 -> -1;
+ A < 1.0e1 -> 0;
+ A < 1.0e2 -> 1;
+ A < 1.0e3 -> 2;
+ A < 1.0e4 -> 3;
+ true -> fwrite_f
+ end,
if P =< 1, E =:= -1;
- P-1 > E, E >= -1 ->
- fwrite_f(Fl, F, Adj, P-1-E, Pad);
- P =< 1 ->
- fwrite_e(Fl, F, Adj, 2, Pad);
- true ->
- fwrite_e(Fl, F, Adj, P, Pad)
+ P-1 > E, E >= -1 ->
+ fwrite_f(Fl, F, Adj, P-1-E, Pad);
+ P =< 1 ->
+ fwrite_e(Fl, F, Adj, 2, Pad);
+ true ->
+ fwrite_e(Fl, F, Adj, P, Pad)
end.
@@ -407,15 +406,15 @@ string(S, none, _Adj, P, Pad) ->
string(S, F, Adj, P, Pad) when F >= P ->
N = lists:flatlength(S),
if F > P ->
- if N > P ->
- adjust(flat_trunc(S, P), chars(Pad, F-P), Adj);
- N < P ->
- adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj);
- true -> % N == P
- adjust(S, chars(Pad, F-P), Adj)
- end;
+ if N > P ->
+ adjust(flat_trunc(S, P), chars(Pad, F-P), Adj);
+ N < P ->
+ adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj);
+ true -> % N == P
+ adjust(S, chars(Pad, F-P), Adj)
+ end;
true -> % F == P
- string_field(S, F, Adj, N, Pad)
+ string_field(S, F, Adj, N, Pad)
end.
string_field(S, F, _Adj, N, _Pad) when N > F ->
@@ -431,11 +430,11 @@ string_field(S, _, _, _, _) -> % N == F
unprefixed_integer(Int, F, Adj, Base, Pad, Lowercase)
when Base >= 2, Base =< 1+$Z-$A+10 ->
if Int < 0 ->
- S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
- term([$-|S], F, Adj, none, Pad);
+ S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
+ term([$-|S], F, Adj, none, Pad);
true ->
- S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
- term(S, F, Adj, none, Pad)
+ S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
+ term(S, F, Adj, none, Pad)
end.
%% prefixed_integer(Int, Field, Adjust, Base, PadChar, Prefix, Lowercase)
@@ -444,11 +443,11 @@ unprefixed_integer(Int, F, Adj, Base, Pad, Lowercase)
prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
when Base >= 2, Base =< 1+$Z-$A+10 ->
if Int < 0 ->
- S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
- term([$-,Prefix|S], F, Adj, none, Pad);
+ S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
+ term([$-,Prefix|S], F, Adj, none, Pad);
true ->
- S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
- term([Prefix|S], F, Adj, none, Pad)
+ S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
+ term([Prefix|S], F, Adj, none, Pad)
end.
%% char(Char, Field, Adjust, Precision, PadChar) -> [Char].
View
6 src/lager_handler_watcher.erl
@@ -117,7 +117,7 @@ reinstall_on_initial_failure_test_() ->
application:start(lager),
try
?assertEqual(1, lager_test_backend:count()),
- {_Level, _Time, [_, _, Message]} = lager_test_backend:pop(),
+ {_Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertMatch("Lager failed to install handler lager_crash_backend into lager_event, retrying later :"++_, lists:flatten(Message)),
?assertEqual(0, lager_test_backend:count()),
timer:sleep(6000),
@@ -148,9 +148,9 @@ reinstall_on_runtime_failure_test_() ->
?assert(lists:member(lager_crash_backend, gen_event:which_handlers(lager_event))),
timer:sleep(6000),
?assertEqual(2, lager_test_backend:count()),
- {_Level, _Time, [_, _, Message]} = lager_test_backend:pop(),
+ {_Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertEqual("Lager event handler lager_crash_backend exited with reason crash", lists:flatten(Message)),
- {_Level2, _Time2, [_, _, Message2]} = lager_test_backend:pop(),
+ {_Level2, _Time2, Message2, _Metadata} = lager_test_backend:pop(),
?assertMatch("Lager failed to install handler lager_crash_backend into lager_event, retrying later :"++_, lists:flatten(Message2)),
?assertEqual(false, lists:member(lager_crash_backend, gen_event:which_handlers(lager_event)))
after
View
29 src/lager_transform.erl
@@ -57,8 +57,8 @@ walk_body(Acc, []) ->
walk_body(Acc, [H|T]) ->
walk_body([transform_statement(H)|Acc], T).
-transform_statement({call, Line, {remote, Line1, {atom, Line2, lager},
- {atom, Line3, Severity}}, Arguments0} = Stmt) ->
+transform_statement({call, Line, {remote, _Line1, {atom, _Line2, lager},
+ {atom, _Line3, Severity}}, Arguments0} = Stmt) ->
case lists:member(Severity, ?LEVELS) of
true ->
DefaultAttrs = {cons, Line, {tuple, Line, [
@@ -75,7 +75,7 @@ transform_statement({call, Line, {remote, Line1, {atom, Line2, lager},
{nil, Line}}}}},
{Traces, Message, Arguments} = case Arguments0 of
[Format] ->
- {DefaultAttrs, Format, {nil, Line}};
+ {DefaultAttrs, Format, {atom, Line, none}};
[Arg1, Arg2] ->
%% some ambiguity here, figure out if these arguments are
%% [Format, Args] or [Attr, Format].
@@ -84,28 +84,21 @@ transform_statement({call, Line, {remote, Line1, {atom, Line2, lager},
case Arg1 of
{cons, _, {tuple, _, _}, _} ->
{concat_lists(Arg1, DefaultAttrs),
- Arg2, {nil,Line}};
+ Arg2, {atom, Line, none}};
_ ->
{DefaultAttrs, Arg1, Arg2}
end;
[Attrs, Format, Args] ->
{concat_lists(Attrs, DefaultAttrs), Format, Args}
end,
- {block, Line,
+ {call, Line, {remote, Line, {atom,Line,lager},{atom,Line,dispatch_log}},
[
- {call, Line, {remote, Line, {atom,Line1,lager},{atom,Line2,dispatch_log}},
- [
- {atom, Line3, Severity},
- {atom, Line3, get(module)},
- {atom, Line3, get(function)},
- {integer, Line3, Line},
- {call, Line3, {atom, Line3 ,self}, []},
- Traces,
- Message,
- Arguments
- ]
- }
- ]}; % block contents
+ {atom,Line,Severity},
+ Traces,
+ Message,
+ Arguments
+ ]
+ };
false ->
Stmt
end;
View
44 src/lager_trunc_io.erl
@@ -42,8 +42,8 @@
-endif.
-type option() :: {'depth', integer()}
- | {'lists_as_strings', boolean()}
- | {'force_strings', boolean()}.
+ | {'lists_as_strings', boolean()}
+ | {'force_strings', boolean()}.
-type options() :: [option()].
-record(print_options, {
@@ -92,9 +92,9 @@ fprint(T, Max, Options) ->
-spec safe(term(), pos_integer()) -> {string(), pos_integer()} | {string()}.
safe(What, Len) ->
case catch print(What, Len) of
- {L, Used} when is_list(L) -> {L, Used};
- _ -> {"unable to print" ++ io_lib:write(What, 99)}
- end.
+ {L, Used} when is_list(L) -> {L, Used};
+ _ -> {"unable to print" ++ io_lib:write(What, 99)}
+ end.
%% @doc Returns {List, Length}
-spec print(term(), pos_integer()) -> {iolist(), pos_integer()}.
@@ -371,29 +371,29 @@ test() ->
-spec test(atom(), atom()) -> ok.
test(Mod, Func) ->
Simple_items = [atom, 1234, 1234.0, {tuple}, [], [list], "string", self(),
- <<1,2,3>>, make_ref(), fun() -> ok end],
+ <<1,2,3>>, make_ref(), fun() -> ok end],
F = fun(A) ->
- Mod:Func(A, 100),
- Mod:Func(A, 2),
- Mod:Func(A, 20)
- end,
+ Mod:Func(A, 100),
+ Mod:Func(A, 2),
+ Mod:Func(A, 20)
+ end,
G = fun(A) ->
- case catch F(A) of
- {'EXIT', _} -> exit({failed, A});
- _ -> ok
- end
- end,
-
+ case catch F(A) of
+ {'EXIT', _} -> exit({failed, A});
+ _ -> ok
+ end
+ end,
+
lists:foreach(G, Simple_items),
-
+
Tuples = [ {1,2,3,a,b,c}, {"abc", def, 1234},
- {{{{a},b,c,{d},e}},f}],
-
+ {{{{a},b,c,{d},e}},f}],
+
Lists = [ [1,2,3,4,5,6,7], lists:seq(1,1000),
- [{a}, {a,b}, {a, [b,c]}, "def"], [a|b], [$a|$b] ],
-
-
+ [{a}, {a,b}, {a, [b,c]}, "def"], [a|b], [$a|$b] ],
+
+
lists:foreach(G, Tuples),
lists:foreach(G, Lists).
View
110 test/crash.erl
@@ -15,92 +15,92 @@
}).
start() ->
- gen_server:start({local, ?MODULE}, ?MODULE, [], []).
+ gen_server:start({local, ?MODULE}, ?MODULE, [], []).
init(_) ->
- {ok, {}}.
+ {ok, {}}.
handle_call(undef, _, State) ->
- {reply, ?MODULE:booger(), State};
+ {reply, ?MODULE:booger(), State};
handle_call(badfun, _, State) ->
- M = booger,
- {reply, M(), State};
+ M = booger,
+ {reply, M(), State};
handle_call(bad_return, _, _) ->
- bleh;
+ bleh;
handle_call(bad_return_string, _, _) ->
- {tuple, {tuple, "string"}};
+ {tuple, {tuple, "string"}};
handle_call(case_clause, _, State) ->
- case State of
- goober ->
- {reply, ok, State}
- end;
+ case State of
+ goober ->
+ {reply, ok, State}
+ end;
handle_call(case_clause_string, _, State) ->
- Foo = atom_to_list(?MODULE),
- case Foo of
- State ->
- {reply, ok, State}
- end;
+ Foo = atom_to_list(?MODULE),
+ case Foo of
+ State ->
+ {reply, ok, State}
+ end;
handle_call(if_clause, _, State) ->
- if State == 1 ->
- {reply, ok, State}
- end;
+ if State == 1 ->
+ {reply, ok, State}
+ end;
handle_call(try_clause, _, State) ->
- Res = try tuple_to_list(State) of
- [_A, _B] -> ok
- catch
- _:_ -> ok
- end,
- {reply, Res, State};
+ Res = try tuple_to_list(State) of
+ [_A, _B] -> ok
+ catch
+ _:_ -> ok
+ end,
+ {reply, Res, State};
handle_call(badmatch, _, State) ->
- {A, B, C} = State,
- {reply, [A, B, C], State};
+ {A, B, C} = State,
+ {reply, [A, B, C], State};
handle_call(badrecord, _, State) ->
- Host = State#state.host,
- {reply, Host, State};
+ Host = State#state.host,
+ {reply, Host, State};
handle_call(function_clause, _, State) ->
- {reply, function(State), State};
+ {reply, function(State), State};
handle_call(badarith, _, State) ->
- Res = 1 / length(tuple_to_list(State)),
- {reply, Res, State};
+ Res = 1 / length(tuple_to_list(State)),
+ {reply, Res, State};
handle_call(badarg1, _, State) ->
- Res = list_to_binary(["foo", bar]),
- {reply, Res, State};
+ Res = list_to_binary(["foo", bar]),
+ {reply, Res, State};
handle_call(badarg2, _, State) ->
- Res = erlang:iolist_to_binary(["foo", bar]),
- {reply, Res, State};
+ Res = erlang:iolist_to_binary(["foo", bar]),
+ {reply, Res, State};
handle_call(system_limit, _, State) ->
- Res = list_to_atom(lists:flatten(lists:duplicate(256, "a"))),
- {reply, Res, State};
+ Res = list_to_atom(lists:flatten(lists:duplicate(256, "a"))),
+ {reply, Res, State};
handle_call(process_limit, _, State) ->
- %% run with +P 300 to make this crash
- [erlang:spawn(fun() -> timer:sleep(5000) end) || _ <- lists:seq(0, 500)],
- {reply, ok, State};
+ %% run with +P 300 to make this crash
+ [erlang:spawn(fun() -> timer:sleep(5000) end) || _ <- lists:seq(0, 500)],
+ {reply, ok, State};
handle_call(port_limit, _, State) ->
- [erlang:open_port({spawn, "ls"}, []) || _ <- lists:seq(0, 1024)],
- {reply, ok, State};
+ [erlang:open_port({spawn, "ls"}, []) || _ <- lists:seq(0, 1024)],
+ {reply, ok, State};
handle_call(noproc, _, State) ->
- Res = gen_event:call(foo, bar, baz),
- {reply, Res, State};
+ Res = gen_event:call(foo, bar, baz),
+ {reply, Res, State};
handle_call(badarity, _, State) ->
- F = fun(A, B, C) -> A + B + C end,
- Res = F(State),
- {reply, Res, State};
+ F = fun(A, B, C) -> A + B + C end,
+ Res = F(State),
+ {reply, Res, State};
handle_call(throw, _, _State) ->
- throw(a_ball);
+ throw(a_ball);
handle_call(_Call, _From, State) ->
- {reply, ok, State}.
+ {reply, ok, State}.
handle_cast(_Cast, State) ->
- {noreply, State}.
+ {noreply, State}.
handle_info(_Info, State) ->
- {noreply, State}.
+ {noreply, State}.
terminate(_, _) ->
- ok.
+ ok.
code_change(_, State, _) ->
- {ok, State}.
+ {ok, State}.
function(X) when is_list(X) ->
- ok.
+ ok.
View
421 test/lager_test_backend.erl
@@ -54,13 +54,12 @@ handle_call({set_loglevel, Level}, State) ->
handle_call(_Request, State) ->
{ok, ok, State}.
-handle_event({log, [?MODULE], Level, Time, Message}, #state{buffer=Buffer} = State) ->
- {ok, State#state{buffer=Buffer ++ [{Level, Time, Message}]}};
-handle_event({log, Level, Time, Message}, #state{level=LogLevel,
- buffer=Buffer} = State) when Level =< LogLevel ->
- {ok, State#state{buffer=Buffer ++ [{Level, Time, Message}]}};
-handle_event({log, _Level, _Time, _Message}, #state{ignored=Ignored} = State) ->
- {ok, State#state{ignored=Ignored ++ [ignored]}};
+handle_event(#lager_log_message{severity_as_int=Level,timestamp=Time, message=Message, metadata=Metadata}=Msg,
+ #state{level=LogLevel,buffer=Buffer,ignored=Ignored} = State) ->
+ case lager_backend_utils:is_loggable(Msg, LogLevel, ?MODULE) of
+ true -> {ok, State#state{buffer=Buffer ++ [{Level, Time, Message, Metadata}]}};
+ _ -> {ok, State#state{ignored=Ignored ++ [ignored]}}
+ end;
handle_event(_Event, State) ->
{ok, State}.
@@ -88,10 +87,10 @@ flush() ->
gen_event:call(lager_event, ?MODULE, flush).
has_line_numbers() ->
- %% are we R15 or greater
- Rel = erlang:system_info(otp_release),
- {match, [Major]} = re:run(Rel, "^R(\\d+)[A|B](|0(\\d))$", [{capture, [1], list}]),
- list_to_integer(Major) >= 15.
+ %% are we R15 or greater
+ Rel = erlang:system_info(otp_release),
+ {match, [Major]} = re:run(Rel, "^R(\\d+)[A|B](|0(\\d))$", [{capture, [1], list}]),
+ list_to_integer(Major) >= 15.
not_running_test() ->
?assertEqual({error, lager_not_running}, lager:log(info, self(), "not running")).
@@ -111,11 +110,9 @@ lager_test_() ->
fun() ->
lager:warning("test message"),
?assertEqual(1, count()),
- {Level, _Time, Message} = pop(),
+ {Level, _Time, Message, _Metadata} = pop(),
?assertMatch(Level, lager_util:level_to_num(warning)),
- [LevelStr, _LocStr, MsgStr] = re:split(Message, " ", [{return, list}, {parts, 3}]),
- ?assertEqual("[warning]", LevelStr),
- ?assertEqual("test message", MsgStr),
+ ?assertEqual("test message", Message),
ok
end
},
@@ -123,11 +120,9 @@ lager_test_() ->
fun() ->
lager:warning("test message ~p", [self()]),
?assertEqual(1, count()),
- {Level, _Time, Message} = pop(),
+ {Level, _Time, Message,_Metadata} = pop(),
?assertMatch(Level, lager_util:level_to_num(warning)),
- [LevelStr, _LocStr, MsgStr] = re:split(Message, " ", [{return, list}, {parts, 3}]),
- ?assertEqual("[warning]", LevelStr),
- ?assertEqual(lists:flatten(io_lib:format("test message ~p", [self()])), MsgStr),
+ ?assertEqual(lists:flatten(io_lib:format("test message ~p", [self()])), lists:flatten(Message)),
ok
end
},
@@ -250,16 +245,28 @@ crash(Type) ->
test_body(Expected, Actual) ->
case has_line_numbers() of
true ->
- FileLine = string:substr(Actual, length(Expected)+1),
+ %FileLine = string:substr(Actual, length(Expected)+1),
Body = string:substr(Actual, 1, length(Expected)),
?assertEqual(Expected, Body),
- ?assertEqual(" line ", string:substr(FileLine, 1, 6));
+ %?assertEqual(" line ", string:substr(FileLine, 1, 6));
+ ok;
false ->
?assertEqual(Expected, Actual)
end.
error_logger_redirect_crash_test_() ->
+ TestBody=fun(Name,CrashReason,Expected) -> {Name,
+ fun() ->
+ Pid = whereis(crash),
+ crash(CrashReason),
+ {Level, _, Msg,Metadata} = pop(),
+ test_body(Expected, lists:flatten(Msg)),
+ ?assertEqual(Pid,proplists:get_value(pid,Metadata)),
+ ?assertEqual(lager_util:level_to_num(error),Level)
+ end
+ }
+ end,
{foreach,
fun() ->
error_logger:tty(false),
@@ -287,160 +294,23 @@ error_logger_redirect_crash_test_() ->
?assertEqual(0, count())
end
},
- {"bad return value",
- fun() ->
- Pid = whereis(crash),
- crash(bad_return),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad return value: bleh", [Pid])),
- ?assertEqual(Expected, lists:flatten(Msg))
- end
- },
- {"bad return value with string",
- fun() ->
- Pid = whereis(crash),
- crash(bad_return_string),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad return value: {tuple,{tuple,\"string\"}}", [Pid])),
- ?assertEqual(Expected, lists:flatten(Msg))
- end
- },
- {"bad return value uncaught throw",
- fun() ->
- Pid = whereis(crash),
- crash(throw),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad return value: a_ball", [Pid])),
- ?assertEqual(Expected, lists:flatten(Msg))
- end
- },
- {"case clause",
- fun() ->
- Pid = whereis(crash),
- crash(case_clause),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no case clause matching {} in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"case clause string",
- fun() ->
- Pid = whereis(crash),
- crash(case_clause_string),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no case clause matching \"crash\" in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"function clause",
- fun() ->
- Pid = whereis(crash),
- crash(function_clause),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no function clause matching crash:function({})", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"if clause",
- fun() ->
- Pid = whereis(crash),
- crash(if_clause),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no true branch found while evaluating if expression in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"try clause",
- fun() ->
- Pid = whereis(crash),
- crash(try_clause),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no try clause matching [] in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"undefined function",
- fun() ->
- Pid = whereis(crash),
- crash(undef),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: call to undefined function crash:booger/0 from crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"bad math",
- fun() ->
- Pid = whereis(crash),
- crash(badarith),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad arithmetic expression in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"bad match",
- fun() ->
- Pid = whereis(crash),
- crash(badmatch),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no match of right hand value {} in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"bad arity",
- fun() ->
- Pid = whereis(crash),
- crash(badarity),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: fun called with wrong arity of 1 instead of 3 in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"bad arg1",
- fun() ->
- Pid = whereis(crash),
- crash(badarg1),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad argument in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"bad arg2",
- fun() ->
- Pid = whereis(crash),
- crash(badarg2),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad argument in call to erlang:iolist_to_binary([\"foo\",bar]) in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"bad record",
- fun() ->
- Pid = whereis(crash),
- crash(badrecord),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad record state in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- },
- {"noproc",
- fun() ->
- Pid = whereis(crash),
- crash(noproc),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no such process or port in call to gen_event:call(foo, bar, baz)", [Pid])),
- ?assertEqual(Expected, lists:flatten(Msg))
- end
- },
- {"badfun",
- fun() ->
- Pid = whereis(crash),
- crash(badfun),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad function booger in crash:handle_call/3", [Pid])),
- test_body(Expected, lists:flatten(Msg))
- end
- }
-
+ TestBody("bad return value",bad_return,"gen_server crash terminated with reason: bad return value: bleh"),
+ TestBody("bad return value with string",bad_return_string,"gen_server crash terminated with reason: bad return value: {tuple,{tuple,\"string\"}}"),
+ TestBody("bad return value uncaught throw",throw,"gen_server crash terminated with reason: bad return value: a_ball"),
+ TestBody("case clause",case_clause,"gen_server crash terminated with reason: no case clause matching {} in crash:handle_call/3"),
+ TestBody("case clause string",case_clause_string,"gen_server crash terminated with reason: no case clause matching \"crash\" in crash:handle_call/3"),
+ TestBody("function clause",function_clause,"gen_server crash terminated with reason: no function clause matching crash:function({})"),
+ TestBody("if clause",if_clause,"gen_server crash terminated with reason: no true branch found while evaluating if expression in crash:handle_call/3"),
+ TestBody("try clause",try_clause,"gen_server crash terminated with reason: no try clause matching [] in crash:handle_call/3"),
+ TestBody("undefined function",undef,"gen_server crash terminated with reason: call to undefined function crash:booger/0 from crash:handle_call/3"),
+ TestBody("bad math",badarith,"gen_server crash terminated with reason: bad arithmetic expression in crash:handle_call/3"),
+ TestBody("bad match",badmatch,"gen_server crash terminated with reason: no match of right hand value {} in crash:handle_call/3"),