From 93f1c95894e61d638c6cae082defb20b4fd50fd2 Mon Sep 17 00:00:00 2001 From: Alexander Verbitsky Date: Tue, 17 Nov 2015 12:07:06 +0300 Subject: [PATCH] Function for printing stacktrace in human readable form --- README.md | 13 +++++++++++ src/lager.erl | 45 ++++++++++++++++++++++++++++++++++++- test/pr_stacktrace_test.erl | 26 +++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/pr_stacktrace_test.erl 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, [])).