-module(utParseStack).
-include("utParseStack.hrl").

-compile([export_all, nowarn_export_all]).

%%  堆栈回溯（stacktrace）是{Module，Function，Arity，Location}元组的列表。
%%  Arity: 根据异常，Arity字段可以是该函数调用的参数列表，而不是arity整数。
%%  Location: 是一个二元组的列表（可能为空），可以指示该函数在源代码中的位置。
%%  第一个元素是描述第二个元素中信息类型的原子。可能发生以下情况：
%% [{file, 一个字符串, 代表函数源文件的文件名}, {line, 元组的第二个元素是发生异常或调用函数的源文件中的行号, 整数> 0} :

%% term序列化, term转为string
term_to_string(Bin) when is_binary(Bin) ->
   binary_to_list(Bin);
term_to_string(Term) ->
   case catch io_lib:format("~w", [Term]) of
      {'EXIT', _} -> lists:flatten(io_lib:format("~p", [Term]));
      GoodString -> lists:flatten(GoodString)
   end.

%% term反序列化, string转换为term
string_to_term(String) ->
   case erl_scan:string(String ++ ".") of
      {ok, Tokens, _} ->
         case erl_parse:parse_term(Tokens) of
            {ok, Term} -> Term;
            _Err -> String
         end;
      _Error ->
         undefined
   end.

parseStack(Stacktrace, Class, Reason) ->
   CR = io_lib:format("~n  Class:~s~n  Reason:~p~n  Stacktrace:", [Class, Reason]),
   [CR | parseStack(Stacktrace)].

parseStack(Stacktrace) ->
   [begin
       case Location of
          [] ->
             [<<"\n     ">>, atom_to_list(Mod), <<":">>, atom_to_list(Func), <<"(">>, term_to_string(Arity), <<")">>];
          [{file, File}, {line, Line}] ->
             [<<"\n     ">>, atom_to_list(Mod), <<":">>, atom_to_list(Func), <<"/">>, integer_to_binary(Arity), <<" (">>, File, <<":">>, integer_to_binary(Line), <<")">>];
          _ ->
             [<<"\n     ">>, atom_to_list(Mod), <<":">>, atom_to_list(Func), <<" (">>, term_to_string(Arity), <<")">>, term_to_string(Location)]
       end
    end || {Mod, Func, Arity, Location} <- Stacktrace
   ].

printStack(Tag) ->
   ?PRINT_STACK(Tag).

%% 下面是测试代码
make_throw() ->
   throw({test, exception}).

bad_arity() ->
   lists:concat([], []).

bad_arg(ErrArgs) ->
   integer_to_list(ErrArgs).

test11(Index) ->
   application:set_env(lager, reverse_pretty_stacktrace, false),
   try
      case Index of
         1 ->
            make_throw();
         2 ->
            bad_arity();
         3 ->
            bad_arg(Index + 0.0)
      end
   catch
      ?EXCEPTION(Class, Reason, Stacktrace) ->
         Stacktrace = ?GET_STACK(Stacktrace),
         io:format(parseStack(Stacktrace)),
         io:format(parseStack(Stacktrace, Class, Reason))
      %% lagere使用示例
      %% lager:error(parse_stack(Stacktrace)),
      %% lager:error(parse_stack(Stacktrace, Class, Reason))
   end.