diff --git a/README.md b/README.md index 645d3aa..1eb442b 100644 --- a/README.md +++ b/README.md @@ -392,6 +392,19 @@ Lager 2.0 changed the backend API, there are various 3rd party backends for lager available, but they may not have been updated to the new API. As they are updated, links to them can be re-added here. +Exception Pretty Printing +---------------------- + +```erlang +try + foo() +catch + Class:Error -> + Stacktrace = lists:reverse(erlang:get_stacktrace()), + lager:error("~nStacktrace:~s~n~s:~p", [lager:pr_stacktrace(Stacktrace), Class, Error]) +end. +``` + Record Pretty Printing ---------------------- Lager's parse transform will keep track of any record definitions it encounters diff --git a/src/lager.erl b/src/lager.erl index 90386c3..f1c07d2 100644 --- a/src/lager.erl +++ b/src/lager.erl @@ -33,7 +33,7 @@ get_loglevel/1, get_loglevel/2, set_loglevel/2, set_loglevel/3, set_loglevel/4, get_loglevels/1, update_loglevel_config/1, posix_error/1, set_loghwm/2, set_loghwm/3, set_loghwm/4, safe_format/3, safe_format_chop/3, unsafe_format/2, dispatch_log/5, dispatch_log/7, dispatch_log/9, - do_log/9, do_log/10, do_log_unsafe/10, pr/2, pr/3]). + do_log/9, do_log/10, do_log_unsafe/10, pr/2, pr/3, pr_stacktrace/1]). -type log_level() :: debug | info | notice | warning | error | critical | alert | emergency. -type log_level_number() :: 0..7. @@ -580,3 +580,46 @@ is_record_known(Record, Module) -> end end. +%% @doc Print stacktrace in human readable form +pr_stacktrace([]) -> + <<"">>; +pr_stacktrace([Entry|Stacktrace]) -> + Indent = <<" ">>, + {Mod, Func, ArityOrArgs, Location} = Entry, + + BinMod = atom_to_binary(Mod, utf8), + BinFunc = atom_to_binary(Func, utf8), + + {Arity, Args} = case ArityOrArgs of + A when is_integer(A) -> {A, []}; + _ -> + {length(ArityOrArgs), ArityOrArgs} + end, + BinArity = integer_to_binary(Arity), + FunSpec = <<"fun ", BinMod/binary, ":", BinFunc/binary, "/", BinArity/binary>>, + + BinArgs = case Args of + [] -> <<"">>; + _ -> + BArgs = binary:replace( + list_to_binary(io_lib:format("~p", [Args])), + <<"\n">>, <<"">>, [global]), + <<"\n", Indent/binary, Indent/binary, "args ", BArgs/binary>> + end, + + BinLocation = case Location of + [{file, File}, {line, Line}] -> + BLine = integer_to_binary(Line), + BFile = list_to_binary(File), + << + "\n", Indent/binary, Indent/binary, "file \"", BFile/binary, "\"", + "\n", Indent/binary, Indent/binary, "line ", BLine/binary>>; + [] -> <<"">> + end, + + Output = << + "\n", Indent/binary, FunSpec/binary, + BinArgs/binary, + BinLocation/binary>>, + Rest = pr_stacktrace(Stacktrace), + <>. diff --git a/test/pr_stacktrace_test.erl b/test/pr_stacktrace_test.erl new file mode 100644 index 0000000..dffa8bb --- /dev/null +++ b/test/pr_stacktrace_test.erl @@ -0,0 +1,26 @@ +-module(pr_stacktrace_test). + +-compile([{parse_transform, lager_transform}]). + +-include_lib("eunit/include/eunit.hrl"). + +foo() -> + throw(test). + +pr_stacktrace_test() -> + Result = try + foo() + catch + _Class:_Error -> + Stacktrace = lists:reverse(erlang:get_stacktrace()), + lager:pr_stacktrace(Stacktrace) + end, +ExpectedPart = <<" + fun pr_stacktrace_test:pr_stacktrace_test/0 + file \"test/pr_stacktrace_test.erl\" + line 12 + fun pr_stacktrace_test:foo/0 + file \"test/pr_stacktrace_test.erl\" + line 8">>, + + ?assertNotEqual(nomatch, binary:match(Result, ExpectedPart, [])).