Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #74 from basho/adt-tensorwrench-integration-2

Changed the messages sent to the backends to include metadata and separated formatting from the backend
  • Loading branch information...
commit 9981ca0b4d7a57a421f7a6bba71e2087c0d614ed 2 parents d8ad5eb + e04830b
@Vagabond Vagabond authored
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
@@ -74,6 +74,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.
+ [{pid, ["My pid is ", pid], "Unknown Pid"}] -> if pid is in the metadata print "My pid is <?.?.?>", otherwise print "Unknown Pid"
+#+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
15 include/lager.hrl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -14,6 +14,9 @@
%% specific language governing permissions and limitations
%% under the License.
+
+-define(DEFAULT_TRUNCATION, 4096).
+
-define(LEVELS,
[debug, info, notice, warning, error, critical, alert, emergency, none]).
@@ -55,9 +58,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, {log, lager_msg:new(io_lib:format(Format, Args),
+ lager_util:format_time(),
+ Level,
+ [{pid,Pid},{line,?LINE},{file,?FILE},{module,?MODULE}],
+ [])}
+ )).
%% FOR INTERNAL USE ONLY
%% internal non-blocking logging call
@@ -93,3 +99,4 @@
end
end)).
-endif.
+
View
20 src/error_logger_lager_h.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -88,7 +88,7 @@ handle_event(Event, State) ->
[ID, Name, format_reason(Reason)]);
_ ->
?CRASH_LOG(Event),
- ?LOG(error, Pid, lager:safe_format(Fmt, Args, 4096))
+ ?LOG(error, Pid, lager:safe_format(Fmt, Args, ?DEFAULT_TRUNCATION))
end;
{error_report, _GL, {Pid, std_error, D}} ->
?CRASH_LOG(Event),
@@ -102,17 +102,17 @@ 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));
+ ?LOG(warning, Pid, lager:safe_format(Fmt, Args, ?DEFAULT_TRUNCATION));
{warning_report, _GL, {Pid, std_warning, Report}} ->
?LOG(warning, Pid, print_silly_list(Report));
{info_msg, _GL, {Pid, Fmt, Args}} ->
- ?LOG(info, Pid, lager:safe_format(Fmt, Args, 4096));
+ ?LOG(info, Pid, lager:safe_format(Fmt, Args, ?DEFAULT_TRUNCATION));
{info_report, _GL, {Pid, std_info, D}} when is_list(D) ->
Details = lists:sort(D),
case Details of
@@ -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])
@@ -292,17 +292,17 @@ format_args([H|T], FmtAcc, ArgsAcc) ->
print_silly_list(L) when is_list(L) ->
case lager_stdlib:string_p(L) of
true ->
- lager_trunc_io:format("~s", [L], 4096);
+ lager_trunc_io:format("~s", [L], ?DEFAULT_TRUNCATION);
_ ->
print_silly_list(L, [], [])
end;
print_silly_list(L) ->
- {Str, _} = lager_trunc_io:print(L, 4096),
+ {Str, _} = lager_trunc_io:print(L, ?DEFAULT_TRUNCATION),
Str.
print_silly_list([], Fmt, Acc) ->
lager_trunc_io:format(string:join(lists:reverse(Fmt), ", "),
- lists:reverse(Acc), 4096);
+ lists:reverse(Acc), ?DEFAULT_TRUNCATION);
print_silly_list([{K,V}|T], Fmt, Acc) ->
print_silly_list(T, ["~p: ~p" | Fmt], [V, K | Acc]);
print_silly_list([H|T], Fmt, Acc) ->
View
112 src/lager.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -19,15 +19,16 @@
-module(lager).
-include("lager.hrl").
+-include_lib("eunit/include/eunit.hrl").
%% API
-export([start/0,
- log/9, log_dest/10, 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/9]).
+ safe_format/3, safe_format_chop/3,dispatch_log/5]).
-type log_level() :: debug | info | notice | warning | error | critical | alert | emergency.
-type log_level_number() :: 0..7.
@@ -52,69 +53,47 @@ 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(), integer()) ->
- ok | {error, lager_not_running}.
-
-dispatch_log(Severity, Module, Function, Line, Pid, Traces, Format, Args, TruncSize) ->
- {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,TruncSize);
- _ -> 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,TruncSize);
- _ -> ok
+-spec dispatch_log(log_level(), list(), string(), list() | none, pos_integer()) -> ok | {error, lager_not_running}.
+dispatch_log(Severity, Metadata, Format, Args, Size) when is_atom(Severity)->
+ case whereis(lager_event) of
+ undefined ->
+ %% lager isn't running
+ {error, lager_not_running};
+ Pid ->
+ {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,Size);
+ _ -> Format
+ end,
+ gen_event:sync_notify(Pid, {log, lager_msg:new(Msg, Timestamp,
+ Severity, Metadata, Destinations)});
+ _ ->
+ ok
+ end
end.
-%% @private
--spec log(log_level(), atom(), atom(), pos_integer(), pid(), tuple(), string(), list(), integer()) ->
- ok | {error, lager_not_running}.
-log(Level, Module, Function, Line, Pid, Time, Format, Args, TruncSize) ->
- 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, TruncSize)],
- 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(), integer()) ->
- ok | {error, lager_not_running}.
-log_dest(_Level, _Module, _Function, _Line, _Pid, _Time, [], _Format, _Args, _TruncSize) ->
- ok;
-log_dest(Level, Module, Function, Line, Pid, Time, Dest, Format, Args, TruncSize) ->
- 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, TruncSize)],
- 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, [], ?DEFAULT_TRUNCATION);
+log(Level, Metadata, Message) when is_list(Metadata) ->
+ dispatch_log(Level, Metadata, Message, [], ?DEFAULT_TRUNCATION).
%% @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, ?DEFAULT_TRUNCATION);
+log(Level, Metadata, Format, Args) when is_list(Metadata) ->
+ dispatch_log(Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION).
trace_file(File, Filter) ->
trace_file(File, Filter, debug).
@@ -257,7 +236,7 @@ posix_error(Error) when is_atom(Error) ->
Message -> Message
end;
posix_error(Error) ->
- safe_format_chop("~p", [Error], 4096).
+ safe_format_chop("~p", [Error], ?DEFAULT_TRUNCATION).
%% @private
get_loglevels() ->
@@ -270,15 +249,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
44 src/lager_app.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -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
53 src/lager_console_backend.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -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, "\r\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,[{eol, "\r\n"}]}]);
+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({log, Message},
+ #state{level=L,formatter=Formatter,format_config=FormatConfig} = State) ->
+ case lager_util:is_loggable(Message, L, ?MODULE) of
true ->
- case Verbose of
- true ->
- io:put_chars(user, [Date, " ", Time, " ", LevelStr, Location, Message, "\r\n"]);
- _ ->
- io:put_chars(user, [Time, " ", LevelStr, Message, "\r\n"])
- end,
+ io:put_chars(user, 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(user, [Date, " ", Time, " ", LevelStr, Location, Message, "\r\n"]);
- _ ->
- io:put_chars(user, [Time, " ", LevelStr, Message, "\r\n"])
- end,
- {ok, State};
handle_event(_Event, State) ->
{ok, State}.
@@ -146,6 +135,7 @@ console_log_test_() ->
unregister(user),
register(user, Pid),
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}} ->
@@ -164,12 +154,13 @@ console_log_test_() ->
register(user, Pid),
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\r\n"], re:split(Msg, " ", [{return, list}, {parts, 5}]))
+ ?assertMatch([_, _, "[info]", PidStr, _,"Test message\r\n"], re:split(Msg, "[ @]", [{return, list}, {parts, 6}]))
after
500 ->
?assert(false)
View
10 src/lager_crash_log.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -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
169 src/lager_default_formatter.erl
@@ -0,0 +1,169 @@
+%% Copyright (c) 2011-2012 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. You can also use a {atom, semi-iolist(), semi-iolist()} formatter, which
+%% acts like a ternary operator's true/false branches.
+%%
+%% 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.
+%%
+%% `[{pid, ["My pid is ", pid], "Unknown Pid"}]' -> if pid is in the metada print "My pid is ?.?.?", otherwise print "Unknown Pid"
+%% @end
+-spec format(lager_msg:lager_msg(),list()) -> any().
+format(Msg,[]) ->
+ format(Msg, [{eol, "\n"}]);
+format(Msg,[{eol, EOL}]) ->
+ format(Msg,
+ [date, " ", time, " [", severity, "] ",
+ {pid, ""},
+ {module, [
+ {pid, ["@"], ""},
+ module,
+ {function, [":", function], ""},
+ {line, [":",line], ""}], ""},
+ " ", message, EOL]);
+format(Message,Config) ->
+ [ output(V,Message) || V <- Config ].
+
+
+-spec output(term(),lager_msg:lager_msg()) -> iolist().
+output(message,Msg) -> lager_msg:message(Msg);
+output(date,Msg) ->
+ {D, _T} = lager_msg:timestamp(Msg),
+ D;
+output(time,Msg) ->
+ {_D, T} = lager_msg:timestamp(Msg),
+ T;
+output(severity,Msg) ->
+ atom_to_list(lager_msg:severity(Msg));
+output(Prop,Msg) when is_atom(Prop) ->
+ Metadata = lager_msg:metadata(Msg),
+ make_printable(get_metadata(Prop,Metadata,<<"Undefined">>));
+output({Prop,Default},Msg) when is_atom(Prop) ->
+ Metadata = lager_msg:metadata(Msg),
+ make_printable(get_metadata(Prop,Metadata,output(Default,Msg)));
+output({Prop, Present, Absent}, Msg) when is_atom(Prop) ->
+ %% sort of like a poor man's ternary operator
+ Metadata = lager_msg:metadata(Msg),
+ case get_metadata(Prop, Metadata) of
+ undefined ->
+ [ output(V, Msg) || V <- Absent];
+ _ ->
+ [ output(V, Msg) || 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_msg:new("Message",
+ {"Day", "Time"},
+ error,
+ [{pid, self()}],
+ []),
+ [])))
+ },
+ {"Basic Formatting",
+ ?_assertEqual(<<"Simplist Format">>,
+ iolist_to_binary(format(lager_msg:new("Message",
+ {"Day", "Time"},
+ error,
+ [{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_msg:new("Message",
+ {"Day", "Time"},
+ error,
+ [{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_msg:new("Message",
+ {"Day", "Time"},
+ error,
+ [{pid, self()}],
+ []),
+ [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_msg:new("Message",
+ {"Day", "Time"},
+ error,
+ [{pid, "Fallback"}],
+ []),
+ [date, " ", time," [",severity,"] ",{does_not_exist,pid}, " ", message, "\n"]
+ )))
+ }
+ ].
+
+-endif.
View
65 src/lager_file_backend.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -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({log, Message},
+ #state{name=Name, level=L,formatter=Formatter,formatter_config=FormatConfig} = State) ->
+ case lager_util:is_loggable(Message,L,{lager_file_backend, Name}) of
true ->
- {ok, write(State, Level, [Date, " ", Time, " ", Message, "\n"])};
+ {ok,write(State, lager_msg:severity_as_int(Message), 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
107 src/lager_format.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -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.
@@ -275,15 +274,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);
@@ -292,12 +291,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}.
@@ -310,22 +309,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.
@@ -337,7 +336,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);
@@ -349,11 +348,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}
@@ -378,20 +377,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.
@@ -405,15 +404,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 ->
@@ -429,11 +428,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)
@@ -442,11 +441,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
8 src/lager_handler_watcher.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -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
2  src/lager_handler_watcher_sup.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
View
51 src/lager_msg.erl
@@ -0,0 +1,51 @@
+-module(lager_msg).
+
+-export([new/5]).
+-export([message/1]).
+-export([timestamp/1]).
+-export([severity/1]).
+-export([severity_as_int/1]).
+-export([metadata/1]).
+-export([destinations/1]).
+
+-record(lager_msg,{
+ destinations :: list(),
+ metadata :: [tuple()],
+ severity :: lager:log_level(),
+ timestamp :: {string(), string()},
+ message :: list()
+ }).
+
+-opaque lager_msg() :: #lager_msg{}.
+-export_type([lager_msg/0]).
+
+-spec new(list(), {string(), string()}, atom(), [tuple()], list()) -> lager_msg().
+new(Msg, Timestamp, Severity, Metadata, Destinations) ->
+ #lager_msg{message=Msg, timestamp=Timestamp, severity=Severity,
+ metadata=Metadata, destinations=Destinations}.
+
+-spec message(lager_msg()) -> list().
+message(Msg) ->
+ Msg#lager_msg.message.
+
+-spec timestamp(lager_msg()) -> {string(), string()}.
+timestamp(Msg) ->
+ Msg#lager_msg.timestamp.
+
+-spec severity(lager_msg()) -> lager:log_level().
+severity(Msg) ->
+ Msg#lager_msg.severity.
+
+-spec severity_as_int(lager_msg()) -> lager:log_level_number().
+severity_as_int(Msg) ->
+ lager_util:level_to_num(Msg#lager_msg.severity).
+
+-spec metadata(lager_msg()) -> [tuple()].
+metadata(Msg) ->
+ Msg#lager_msg.metadata.
+
+-spec destinations(lager_msg()) -> list().
+destinations(Msg) ->
+ Msg#lager_msg.destinations.
+
+
View
2  src/lager_sup.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
View
35 src/lager_transform.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -29,7 +29,7 @@
%% @private
parse_transform(AST, Options) ->
- TruncSize = proplists:get_value(lager_truncation_size, Options, 4096),
+ TruncSize = proplists:get_value(lager_truncation_size, Options, ?DEFAULT_TRUNCATION),
put(truncation_size, TruncSize),
walk_ast([], AST).
@@ -59,8 +59,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, [
@@ -77,7 +77,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].
@@ -86,29 +86,22 @@ 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,
- {integer, Line3, get(truncation_size)}
- ]
- }
- ]}; % block contents
+ {atom,Line,Severity},
+ Traces,
+ Message,
+ Arguments,
+ {integer, Line, get(truncation_size)}
+ ]
+ };
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()}.
@@ -443,29 +443,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
20 src/lager_util.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -21,12 +21,14 @@
-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, validate_trace/1, check_traces/4]).
+ calculate_next_rotation/1, validate_trace/1, check_traces/4, is_loggable/3]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
+-include("lager.hrl").
+
levels() ->
[debug, info, notice, warning, error, critical, alert, emergency].
@@ -327,6 +329,11 @@ check_trace_iter(Attrs, [{Key, Match}|T]) ->
false
end.
+-spec is_loggable(lager_msg:lager_msg(),integer(),term()) -> boolean().
+is_loggable(Msg ,SeverityThreshold,MyName) ->
+ lager_msg:severity_as_int(Msg) =< SeverityThreshold orelse
+ lists:member(MyName, lager_msg:destinations(Msg)).
+
-ifdef(TEST).
parse_test() ->
@@ -454,4 +461,13 @@ check_trace_test() ->
{[{module, '*'}], 0, bar}], [])),
ok.
+is_loggable_test_() ->
+ [
+ {"Loggable by severity only", ?_assert(is_loggable(lager_msg:new("",{"",""}, alert, [], []),2,me))},
+ {"Not loggable by severity only", ?_assertNot(is_loggable(lager_msg:new("",{"",""}, critical, [], []),1,me))},
+ {"Loggable by severity with destination", ?_assert(is_loggable(lager_msg:new("",{"",""}, alert, [], [you]),2,me))},
+ {"Not loggable by severity with destination", ?_assertNot(is_loggable(lager_msg:new("",{"",""}, critical, [], [you]),1,me))},
+ {"Loggable by destination overriding severity", ?_assert(is_loggable(lager_msg:new("",{"",""}, critical, [], [me]),1,me))}
+ ].
+
-endif.
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
2  test/lager_crash_backend.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
View
432 test/lager_test_backend.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2011-2012 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
@@ -54,13 +54,15 @@ 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({log, Msg},
+ #state{level=LogLevel,buffer=Buffer,ignored=Ignored} = State) ->
+ case lager_util:is_loggable(Msg, LogLevel, ?MODULE) of
+ true -> {ok, State#state{buffer=Buffer ++
+ [{lager_msg:severity_as_int(Msg),
+ lager_msg:timestamp(Msg),
+ lager_msg:message(Msg), lager_msg:metadata(Msg)}]}};
+ _ -> {ok, State#state{ignored=Ignored ++ [ignored]}}
+ end;
handle_event(_Event, State) ->
{ok, State}.
@@ -88,10 +90,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 +113,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 +123,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
},
@@ -253,13 +251,33 @@ test_body(Expected, Actual) ->
FileLine = string:substr(Actual, length(Expected)+1),
Body = string:substr(Actual, 1, length(Expected)),
?assertEqual(Expected, Body),
- ?assertEqual(" line ", string:substr(FileLine, 1, 6));
+ case string:substr(FileLine, 1, 6) of
+ [] ->
+ %% sometimes there's no line information...
+ ?assert(true);
+ " line " ->
+ ?assert(true);
+ Other ->
+ ?debugFmt("unexpected trailing data ~p", [Other]),
+ ?assert(false)
+ end;
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 +305,24 @@ 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 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"),
+ TestBody("bad arity",badarity,"gen_server crash terminated with reason: fun called with wrong arity of 1 instead of 3 in crash:handle_call/3"),
+ TestBody("bad arg1",badarg1,"gen_server crash terminated with reason: bad argument in crash:handle_call/3"),
+ TestBody("bad arg2",badarg2,"gen_server crash terminated with reason: bad argument in call to erlang:iolist_to_binary([\"foo\",bar]) in crash:handle_call/3"),
+ TestBody("bad record",badrecord,"gen_server crash terminated with reason: bad record state in crash:handle_call/3"),
+ TestBody("noproc",noproc,"gen_server crash terminated with reason: no such process or port in call to gen_event:call(foo, bar, baz)"),
+ TestBody("badfun",badfun,"gen_server crash terminated with reason: bad function booger in crash:handle_call/3")
]
}.
@@ -466,17 +348,22 @@ error_logger_redirect_test_() ->
fun() ->
sync_error_logger:error_report([{this, is}, a, {silly, format}]),
_ = gen_event:which_handlers(error_logger),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w this: is, a, silly: format", [self()])),
+ {Level, _, Msg,Metadata} = pop(),
+ ?assertEqual(lager_util:level_to_num(error),Level),
+ ?assertEqual(self(),proplists:get_value(pid,Metadata)),
+ Expected = "this: is, a, silly: format",
?assertEqual(Expected, lists:flatten(Msg))
+
end
},
{"string error reports are printed",
fun() ->
sync_error_logger:error_report("this is less silly"),
_ = gen_event:which_handlers(error_logger),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w this is less silly", [self()])),
+ {Level, _, Msg,Metadata} = pop(),
+ ?assertEqual(lager_util:level_to_num(error),Level),
+ ?assertEqual(self(),proplists:get_value(pid,Metadata)),
+ Expected = "this is less silly",
?assertEqual(Expected, lists:flatten(Msg))
end
},
@@ -484,8 +371,10 @@ error_logger_redirect_test_() ->
fun() ->
sync_error_logger:error_msg("doom, doom has come upon you all"),
_ = gen_event:which_handlers(error_logger),
- {_, _, Msg} = pop(),
- Expected = lists:flatten(io_lib:format("[error] ~w doom, doom has come upon you all", [self()])),
+ {Level, _, Msg,Metadata} = pop(),
+ ?assertEqual(lager_util:level_to_num(error),Level),
+ ?assertEqual(self(),proplists:get_value(pid,Metadata)),
+ Expected = "doom, doom has come upon you all",
?assertEqual(Expected, lists:flatten(Msg))
end
},
@@ -493,7 +382,7 @@ error_logger_redirect_test_() ->
fun() ->
sync_error_logger:error_msg("doom, doom has come upon you all ~p", [string:copies("doom", 10000)]),
_ = gen_event:which_handlers(error_logger),
- {_, _, Msg} = pop(),
+ {_, _, Msg,_Metadata} = pop(),
?assert(length(lists:flatten(Msg)) < 5100)
end
},