You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2021/04/18 02:27:58 UTC

[couchdb] branch erlang-23-for-main created (now 4cbd1de)

This is an automated email from the ASF dual-hosted git repository.

vatamane pushed a change to branch erlang-23-for-main
in repository https://gitbox.apache.org/repos/asf/couchdb.git.


      at 4cbd1de  feat: work around get_stacktrace deprecation/removal

This branch includes the following new commits:

     new 4cbd1de  feat: work around get_stacktrace deprecation/removal

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[couchdb] 01/01: feat: work around get_stacktrace deprecation/removal

Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

vatamane pushed a commit to branch erlang-23-for-main
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 4cbd1de51d8a62ff9001a6e32450a96844d0af6e
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sat Mar 13 18:00:12 2021 +0100

    feat: work around get_stacktrace deprecation/removal
    
    This patch introduces a macro and inserts it everywhere we catch errors
    and then generatre a stacktrace.
    
    So far the only thing that is a little bit ugly is that in two places,
    I had to add a header include dependency on couch_db.erl where those
    modules didn’t have any ties to couchdb/* before, alas. I’d be willing
    to duplicate the macros in those modules, if we don’t want the include
    dependency.
---
 Makefile                                           |  2 +-
 rebar.config.script                                |  4 +--
 src/chttpd/src/chttpd.erl                          | 40 ++++++++++------------
 src/chttpd/src/chttpd_handlers.erl                 |  3 +-
 src/chttpd/src/chttpd_stats.erl                    |  5 +--
 src/couch/include/couch_db.hrl                     | 27 +++++++++++++++
 src/couch/src/couch_proc_manager.erl               |  3 +-
 src/couch_js/src/couch_js_proc_manager.erl         |  3 +-
 src/couch_replicator/src/couch_replicator_job.erl  | 23 ++++++-------
 .../src/couch_replicator_parse.erl                 | 13 +++----
 src/couch_views/src/couch_views_indexer.erl        |  3 +-
 src/couch_views/src/couch_views_updater.erl        |  3 +-
 src/ctrace/src/ctrace.erl                          |  3 +-
 src/fabric/src/fabric2_db_expiration.erl           |  3 +-
 src/fabric/src/fabric2_index.erl                   |  6 ++--
 src/fabric/src/fabric2_util.erl                    | 16 +--------
 src/mango/src/mango_httpd.erl                      |  3 +-
 17 files changed, 79 insertions(+), 81 deletions(-)

diff --git a/Makefile b/Makefile
index 7914118..e3939fd 100644
--- a/Makefile
+++ b/Makefile
@@ -187,7 +187,7 @@ exunit: export ERL_AFLAGS = -config $(shell pwd)/rel/files/eunit.config
 exunit: export COUCHDB_QUERY_SERVER_JAVASCRIPT = $(shell pwd)/bin/couchjs $(shell pwd)/share/server/main.js
 exunit: export COUCHDB_TEST_ADMIN_PARTY_OVERRIDE=1
 exunit: couch elixir-init setup-eunit elixir-check-formatted elixir-credo
-	@mix test --cover --trace $(EXUNIT_OPTS)
+	@mix test --trace $(EXUNIT_OPTS)
 
 setup-eunit: export BUILDDIR = $(shell pwd)
 setup-eunit: export ERL_AFLAGS = -config $(shell pwd)/rel/files/eunit.config
diff --git a/rebar.config.script b/rebar.config.script
index 0f40cf0..3156b09 100644
--- a/rebar.config.script
+++ b/rebar.config.script
@@ -152,7 +152,7 @@ DepDescs = [
 {hyper,            "hyper",            {tag, "CouchDB-2.2.0-6"}},
 {ibrowse,          "ibrowse",          {tag, "CouchDB-4.0.1-2"}},
 {jaeger_passage,   "jaeger-passage",   {tag, "CouchDB-0.1.14-1"}},
-{jiffy,            "jiffy",            {tag, "CouchDB-1.0.4-1"}},
+{jiffy,            "jiffy",            {tag, "CouchDB-1.0.5-1"}},
 {local,            "local",            {tag, "0.2.1"}},
 {mochiweb,         "mochiweb",         {tag, "v2.20.0"}},
 {meck,             "meck",             {tag, "0.8.8"}},
@@ -191,7 +191,7 @@ ErlOpts = case os:getenv("ERL_OPTS") of
 end.
 
 AddConfig = [
-    {require_otp_vsn, "19|20|21|22"},
+    {require_otp_vsn, "20|21|22|23"},
     {deps_dir, "src"},
     {deps, lists:map(MakeDep, DepDescs ++ OptionalDeps)},
     {sub_dirs, SubDirs},
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index 8567ada..135c4ec 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -278,16 +278,15 @@ before_request(HttpReq) ->
         {ok, HttpReq1} = chttpd_plugin:before_request(HttpReq),
         chttpd_stats:init(HttpReq1),
         {ok, HttpReq1}
-    catch Tag:Error ->
-        {error, catch_error(HttpReq, Tag, Error)}
+    catch ?STACKTRACE(Tag, Error, Stack)
+        {error, catch_error(HttpReq, Tag, Error, Stack)}
     end.
 
 after_request(HttpReq, HttpResp0) ->
     {ok, HttpResp1} =
         try
             chttpd_plugin:after_request(HttpReq, HttpResp0)
-        catch _Tag:Error ->
-            Stack = erlang:get_stacktrace(),
+        catch ?STACKTRACE(_Tag, Error, Stack)
             send_error(HttpReq, {Error, nil, Stack}),
             {ok, HttpResp0#httpd_resp{status = aborted}}
         end,
@@ -320,8 +319,8 @@ process_request(#httpd{mochi_req = MochiReq} = HttpReq) ->
         Response ->
             {HttpReq, Response}
         end
-    catch Tag:Error ->
-        {HttpReq, catch_error(HttpReq, Tag, Error)}
+    catch ?STACKTRACE(Tag, Error, Stack)
+        {HttpReq, catch_error(HttpReq, Tag, Error, Stack)}
     end.
 
 handle_req_after_auth(HandlerKey, HttpReq) ->
@@ -333,17 +332,17 @@ handle_req_after_auth(HandlerKey, HttpReq) ->
         AuthorizedReq = chttpd_auth:authorize(possibly_hack(HttpReq),
             fun chttpd_auth_request:authorize_request/1),
         {AuthorizedReq, HandlerFun(AuthorizedReq)}
-    catch Tag:Error ->
-        {HttpReq, catch_error(HttpReq, Tag, Error)}
+    catch ?STACKTRACE(Tag, Error, Stack)
+        {HttpReq, catch_error(HttpReq, Tag, Error, Stack)}
     end.
 
-catch_error(_HttpReq, throw, {http_head_abort, Resp}) ->
+catch_error(_HttpReq, throw, {http_head_abort, Resp}, _Stack) ->
     {ok, Resp};
-catch_error(_HttpReq, throw, {http_abort, Resp, Reason}) ->
+catch_error(_HttpReq, throw, {http_abort, Resp, Reason}, _Stack) ->
     {aborted, Resp, Reason};
-catch_error(HttpReq, throw, {invalid_json, _}) ->
+catch_error(HttpReq, throw, {invalid_json, _}, _Stack) ->
     send_error(HttpReq, {bad_request, "invalid UTF-8 JSON"});
-catch_error(HttpReq, exit, {mochiweb_recv_error, E}) ->
+catch_error(HttpReq, exit, {mochiweb_recv_error, E}, _Stack) ->
     #httpd{
         mochi_req = MochiReq,
         peer = Peer,
@@ -355,22 +354,21 @@ catch_error(HttpReq, exit, {mochiweb_recv_error, E}) ->
         MochiReq:get(raw_path),
         E]),
     exit(normal);
-catch_error(HttpReq, exit, {uri_too_long, _}) ->
+catch_error(HttpReq, exit, {uri_too_long, _}, _Stack) ->
     send_error(HttpReq, request_uri_too_long);
-catch_error(HttpReq, exit, {body_too_large, _}) ->
+catch_error(HttpReq, exit, {body_too_large, _}, _Stack) ->
     send_error(HttpReq, request_entity_too_large);
-catch_error(HttpReq, throw, Error) ->
+catch_error(HttpReq, throw, Error, _Stack) ->
     send_error(HttpReq, Error);
-catch_error(HttpReq, error, database_does_not_exist) ->
+catch_error(HttpReq, error, database_does_not_exist, _Stack) ->
     send_error(HttpReq, database_does_not_exist);
-catch_error(HttpReq, error, decryption_failed) ->
+catch_error(HttpReq, error, decryption_failed, _Stack) ->
     send_error(HttpReq, decryption_failed);
-catch_error(HttpReq, error, not_ciphertext) ->
+catch_error(HttpReq, error, not_ciphertext, _Stack) ->
     send_error(HttpReq, not_ciphertext);
-catch_error(HttpReq, error, {erlfdb_error, _} = Error) ->
+catch_error(HttpReq, error, {erlfdb_error, _} = Error, _Stack) ->
     send_error(HttpReq, Error);
-catch_error(HttpReq, Tag, Error) ->
-    Stack = erlang:get_stacktrace(),
+catch_error(HttpReq, Tag, Error, Stack) ->
     % TODO improve logging and metrics collection for client disconnects
     case {Tag, Error, Stack} of
         {exit, normal, [{mochiweb_request, send, _, _} | _]} ->
diff --git a/src/chttpd/src/chttpd_handlers.erl b/src/chttpd/src/chttpd_handlers.erl
index 17d2952..43631c0 100644
--- a/src/chttpd/src/chttpd_handlers.erl
+++ b/src/chttpd/src/chttpd_handlers.erl
@@ -44,8 +44,7 @@ handler_info(HttpReq) ->
     Default = {'unknown.unknown', #{}},
     try
         select(collect(handler_info, [Method, PathParts, HttpReq]), Default)
-    catch Type:Reason ->
-        Stack = erlang:get_stacktrace(),
+    catch ?STACKTRACE(Type, Reason, Stack)
         couch_log:error("~s :: handler_info failure for ~p : ~p:~p :: ~p", [
                 ?MODULE,
                 get(nonce),
diff --git a/src/chttpd/src/chttpd_stats.erl b/src/chttpd/src/chttpd_stats.erl
index 27e9c31..ae1efa4 100644
--- a/src/chttpd/src/chttpd_stats.erl
+++ b/src/chttpd/src/chttpd_stats.erl
@@ -12,6 +12,8 @@
 
 -module(chttpd_stats).
 
+% for the stacktrace macro only so far
+-include_lib("couch/include/couch_db.hrl").
 
 -export([
     init/1,
@@ -61,8 +63,7 @@ report(HttpResp) ->
             _ ->
                 ok
         end
-    catch T:R ->
-        S = erlang:get_stacktrace(),
+    catch ?STACKTRACE(T, R, S)
         Fmt = "Failed to report chttpd request stats: ~p:~p ~p",
         couch_log:error(Fmt, [T, R, S])
     end.
diff --git a/src/couch/include/couch_db.hrl b/src/couch/include/couch_db.hrl
index 2289089..3f4d70a 100644
--- a/src/couch/include/couch_db.hrl
+++ b/src/couch/include/couch_db.hrl
@@ -178,3 +178,30 @@
 
 -define(record_to_keyval(Name, Record),
     lists:zip(record_info(fields, Name), tl(tuple_to_list(Record)))).
+
+%% Erlang/OTP 21 deprecates and 23 removes get_stacktrace(), so
+%% we have to monkey around until we can drop support < 21.
+%% h/t https://github.com/erlang/otp/pull/1783#issuecomment-386190970
+
+%% use like so:
+% try function1(Arg1)
+% catch
+%     ?STACKTRACE(exit, badarg, ErrorStackTrace)
+%         % do stuff with ErrorStackTrace
+%         % ...
+% end,
+
+% for features specific to Erlang/OTP version 20.x (and later versions)
+-ifdef(ERLANG_OTP_VERSION_20).
+-else.
+-define(ERLANG_OTP_VERSION_21_FEATURES, true).
+-endif.
+% Get the stacktrace in a way that is backwards compatible
+-ifdef(ERLANG_OTP_VERSION_21_FEATURES).
+-define(STACKTRACE(ErrorType, Error, ErrorStackTrace),
+        ErrorType:Error:ErrorStackTrace ->).
+-else.
+-define(STACKTRACE(ErrorType, Error, ErrorStackTrace),
+        ErrorType:Error ->
+            ErrorStackTrace = erlang:get_stacktrace(),).
+-endif.
diff --git a/src/couch/src/couch_proc_manager.erl b/src/couch/src/couch_proc_manager.erl
index b83d788..6a0322c 100644
--- a/src/couch/src/couch_proc_manager.erl
+++ b/src/couch/src/couch_proc_manager.erl
@@ -321,8 +321,7 @@ find_proc(#client{lang = Lang, ddoc = DDoc, ddoc_key = DDocKey} = Client) ->
 
 find_proc(Lang, Fun) ->
     try iter_procs(Lang, Fun)
-    catch error:Reason ->
-        StackTrace = erlang:get_stacktrace(),
+    catch ?STACKTRACE(error, Reason, StackTrace)
         couch_log:error("~p ~p ~p", [?MODULE, Reason, StackTrace]),
         {error, Reason}
     end.
diff --git a/src/couch_js/src/couch_js_proc_manager.erl b/src/couch_js/src/couch_js_proc_manager.erl
index db5c492..4b3354d 100644
--- a/src/couch_js/src/couch_js_proc_manager.erl
+++ b/src/couch_js/src/couch_js_proc_manager.erl
@@ -320,8 +320,7 @@ find_proc(#client{lang = Lang, ddoc = DDoc, ddoc_key = DDocKey} = Client) ->
 
 find_proc(Lang, Fun) ->
     try iter_procs(Lang, Fun)
-    catch error:Reason ->
-        StackTrace = erlang:get_stacktrace(),
+    catch ?STACKTRACE(error, Reason, StackTrace)
         couch_log:error("~p ~p ~p", [?MODULE, Reason, StackTrace]),
         {error, Reason}
     end.
diff --git a/src/couch_replicator/src/couch_replicator_job.erl b/src/couch_replicator/src/couch_replicator_job.erl
index c8c143a..d11bdb7 100644
--- a/src/couch_replicator/src/couch_replicator_job.erl
+++ b/src/couch_replicator/src/couch_replicator_job.erl
@@ -221,12 +221,12 @@ handle_info(timeout, delayed_init) ->
         {ok, State} -> {noreply, State};
         {stop, Reason, State} -> {stop, Reason, State}
     catch
-        exit:{shutdown, Exit} when Exit =:= finished orelse Exit =:= halt ->
-            Stack = erlang:get_stacktrace(),
-            {stop, {shutdown, Exit}, {init_error, Stack}};
-        _Tag:Error ->
+        ?STACKTRACE(exit, {shutdown, finished}, Stack)
+            {stop, {shutdown, finished}, {init_error, Stack}};
+        ?STACKTRACE(exit, {shtudown, halt}, Stack)
+            {stop, {shutdown, halt}, {init_error, Stack}};
+        ?STACKTRACE(_Tag, Error, Stack)
             ShutdownReason = {error, replication_start_error(Error)},
-            Stack = erlang:get_stacktrace(),
             {stop, {shutdown, ShutdownReason}, {init_error, Stack}}
     end;
 
@@ -406,16 +406,15 @@ delayed_init() ->
     try do_init(Job, JobData) of
         State = #rep_state{} -> {ok, State}
     catch
-        exit:{http_request_failed, _, _, max_backoff} ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(exit, {http_request_failed, _, _, max_backoff}, Stack)
             reschedule_on_error(undefined, Job, JobData, max_backoff),
             {stop, {shutdown, max_backoff}, {init_error, Stack}};
-        exit:{shutdown, Exit} when Exit =:= finished orelse Exit =:= halt ->
-            Stack = erlang:get_stacktrace(),
-            {stop, {shutdown, Exit}, {init_error, Stack}};
-        _Tag:Error ->
+        ?STACKTRACE(exit, {shutdown, finished}, Stack)
+            {stop, {shutdown, finished}, {init_error, Stack}};
+        ?STACKTRACE(exit, {shutdown, halt}, Stack)
+            {stop, {shutdown, halt}, {init_error, Stack}};
+        ?STACKTRACE(_Tag, Error, Stack)
             Reason = {error, replication_start_error(Error)},
-            Stack = erlang:get_stacktrace(),
             ErrMsg = "~p : job ~p failed during startup ~p stack:~p",
             couch_log:error(ErrMsg, [?MODULE, Job, Reason, Stack]),
             reschedule_on_error(undefined, Job, JobData, Reason),
diff --git a/src/couch_replicator/src/couch_replicator_parse.erl b/src/couch_replicator/src/couch_replicator_parse.erl
index 5996ec5..79c778d 100644
--- a/src/couch_replicator/src/couch_replicator_parse.erl
+++ b/src/couch_replicator/src/couch_replicator_parse.erl
@@ -22,6 +22,7 @@
 
 
 -include_lib("ibrowse/include/ibrowse.hrl").
+-include_lib("couch/include/couch_db.hrl").
 -include("couch_replicator.hrl").
 
 
@@ -59,13 +60,11 @@ parse_rep_doc(RepDoc) ->
     {ok, Rep} = try
         parse_rep(RepDoc, null)
     catch
-        throw:{error, Reason} ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(throw, {error, Reason}, Stack)
             LogErr1 = "~p parse_rep_doc fail ~p ~p",
             couch_log:error(LogErr1, [?MODULE, Reason, Stack]),
             throw({bad_rep_doc, Reason});
-        Tag:Err ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(Tag, Err, Stack)
             LogErr2 = "~p parse_rep_doc fail ~p:~p ~p",
             couch_log:error(LogErr2, [?MODULE, Tag, Err, Stack]),
             throw({bad_rep_doc, couch_util:to_binary({Tag, Err})})
@@ -83,13 +82,11 @@ parse_transient_rep(#{} = Body, UserName) ->
     {ok, Rep} = try
         parse_rep(Body, UserName)
     catch
-        throw:{error, Reason} ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(throw, {error, Reason}, Stack)
             LogErr1 = "~p parse_transient_rep fail ~p ~p",
             couch_log:error(LogErr1, [?MODULE, Reason, Stack]),
             throw({bad_request, Reason});
-        Tag:Err ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(Tag, Err, Stack)
             LogErr2 = "~p parse_transient_rep fail ~p ~p",
             couch_log:error(LogErr2, [?MODULE, Tag, Err, Stack]),
             throw({bad_request, couch_util:to_binary({Tag, Err})})
diff --git a/src/couch_views/src/couch_views_indexer.erl b/src/couch_views/src/couch_views_indexer.erl
index 88b1ff6..2164a58 100644
--- a/src/couch_views/src/couch_views_indexer.erl
+++ b/src/couch_views/src/couch_views_indexer.erl
@@ -121,8 +121,7 @@ init() ->
             ok;
         error:database_does_not_exist ->
             fail_job(Job, Data, db_deleted, "Database was deleted");
-        Error:Reason  ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(Error, Reason, Stack)
             Fmt = "Error building view for ddoc ~s in ~s: ~p:~p ~p",
             couch_log:error(Fmt, [DbName, DDocId, Error, Reason, Stack]),
 
diff --git a/src/couch_views/src/couch_views_updater.erl b/src/couch_views/src/couch_views_updater.erl
index defdb6b..7c96bb8 100644
--- a/src/couch_views/src/couch_views_updater.erl
+++ b/src/couch_views/src/couch_views_updater.erl
@@ -31,8 +31,7 @@ index(Db, #doc{id = Id, revs = Revs} = Doc, _NewWinner, _OldWinner, NewRevId,
             index_int(Db, Doc, Seq)
         end
     catch
-        error:{erlfdb_error, ErrCode} when is_integer(ErrCode) ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(error, {erlfdb_error, ErrCode}, Stack)
             DbName = fabric2_db:name(Db),
             couch_log:error("Mango index erlfdb error Db ~s Doc ~p ~p",
                 [DbName, Id, ErrCode]),
diff --git a/src/ctrace/src/ctrace.erl b/src/ctrace/src/ctrace.erl
index 5521901..8ae51c4 100644
--- a/src/ctrace/src/ctrace.erl
+++ b/src/ctrace/src/ctrace.erl
@@ -109,8 +109,7 @@ with_span(Operation, Options, Fun)  ->
     try
         start_span(Operation, Options),
         Fun()
-    catch Type:Reason ->
-        Stack = erlang:get_stacktrace(),
+    catch ?STACKTRACE(Type, Reason, Stack)
         log(#{
             ?LOG_FIELD_ERROR_KIND => Type,
             ?LOG_FIELD_MESSAGE => Reason,
diff --git a/src/fabric/src/fabric2_db_expiration.erl b/src/fabric/src/fabric2_db_expiration.erl
index 92f22e7..94cb61b 100644
--- a/src/fabric/src/fabric2_db_expiration.erl
+++ b/src/fabric/src/fabric2_db_expiration.erl
@@ -131,8 +131,7 @@ cleanup(true) ->
                 {ok, Job1, Data1} = ?MODULE:process_expirations(Job, Data),
                 ok = resubmit_job(Job1, Data1, schedule_sec())
             catch
-                _Tag:Error ->
-                    Stack = erlang:get_stacktrace(),
+                ?STACKTRACE(_Tag, Error, Stack)
                     couch_log:error("~p : processing error ~p ~p ~p",
                         [?MODULE, Job, Error, Stack]),
                     ok = resubmit_job(Job, Data, ?ERROR_RESCHEDULE_SEC),
diff --git a/src/fabric/src/fabric2_index.erl b/src/fabric/src/fabric2_index.erl
index 25c31a8..3c59cd7 100644
--- a/src/fabric/src/fabric2_index.erl
+++ b/src/fabric/src/fabric2_index.erl
@@ -67,8 +67,7 @@ cleanup(Db) ->
     catch
         error:database_does_not_exist ->
             ok;
-        Tag:Reason ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(Tag, Reason, Stack)
             DbName = fabric2_db:name(Db),
             LogMsg = "~p failed to cleanup indices for `~s` ~p:~p ~p",
             couch_log:error(LogMsg, [?MODULE, DbName, Tag, Reason, Stack])
@@ -168,8 +167,7 @@ process_updates_iter([Db | Rest], Cont) ->
     catch
         error:database_does_not_exist ->
             ok;
-        Tag:Reason ->
-            Stack = erlang:get_stacktrace(),
+        ?STACKTRACE(Tag, Reason, Stack)
             LogMsg = "~p failed to build indices for `~s` ~p:~p ~p",
             couch_log:error(LogMsg, [?MODULE, Db, Tag, Reason, Stack])
     end,
diff --git a/src/fabric/src/fabric2_util.erl b/src/fabric/src/fabric2_util.erl
index 7c21252..f95e410 100644
--- a/src/fabric/src/fabric2_util.erl
+++ b/src/fabric/src/fabric2_util.erl
@@ -383,23 +383,9 @@ pmap(Fun, Args, Opts) ->
     end, Refs).
 
 
-% OTP_RELEASE is defined in OTP 21+ only
--ifdef(OTP_RELEASE).
-
 pmap_exec(Fun, Arg) ->
     try
         {'$res', Fun(Arg)}
-    catch Tag:Reason:Stack ->
+    catch ?STACKTRACE(Tag, Reason, Stack)
         {'$err', Tag, Reason, Stack}
     end.
-
--else.
-
-pmap_exec(Fun, Arg) ->
-    try
-        {'$res', Fun(Arg)}
-    catch Tag:Reason ->
-        {'$err', Tag, Reason, erlang:get_stacktrace()}
-    end.
-
--endif.
diff --git a/src/mango/src/mango_httpd.erl b/src/mango/src/mango_httpd.erl
index 8d5a212..9ac4dfc 100644
--- a/src/mango/src/mango_httpd.erl
+++ b/src/mango/src/mango_httpd.erl
@@ -36,10 +36,9 @@ handle_req(#httpd{} = Req, Db) ->
     try
         handle_req_int(Req, Db)
     catch
-        throw:{mango_error, Module, Reason} ->
+        ?STACKTRACE(throw, {mango_error, Module, Reason}, Stack)
             case mango_error:info(Module, Reason) of
             {500, ErrorStr, ReasonStr} ->
-                Stack = erlang:get_stacktrace(),
                 chttpd:send_error(Req, {ErrorStr, ReasonStr, Stack});
             {Code, ErrorStr, ReasonStr} ->
                 chttpd:send_error(Req, Code, ErrorStr, ReasonStr)