./ct_report/coverage/safely.COVER.html

1 -module(safely).
2 %% This module preserves the return types of a `catch' statement,
3 %% but doesn't silently convert `throw's to normal values.
4 %% The issue is that throws have different semantics than errors:
5 %% https://erlang.org/doc/reference_manual/expressions.html#catch-and-throw,
6 %% so a throw is actually a control flow escape, while an error is an actual crash.
7 %% If it is an error, we want the stacktrace, if not, we don't.
8 %% For more information, see usage in test/safely_SUITE.erl
9
10 -include("safely.hrl").
11 -include_lib("kernel/include/logger.hrl").
12
13 -ifdef(TEST).
14 -export([apply/2, apply/3]).
15 -endif.
16 -export([apply_and_log/3, apply_and_log/4]).
17 -ignore_xref([apply_and_log/4]).
18
19 -type error_class() :: error | exit | throw.
20 -type error_info() :: #{class => error_class(), reason => term(), stacktrace => [term()]}.
21 -type exception() :: {exception, error_info()}.
22 -export_type([exception/0]).
23
24 -define(MATCH_EXCEPTIONS_DO_LOG(F, Context),
25 try F catch
26 error:R:S ->
27 Info = #{class => error, reason => R, stacktrace => S},
28 ?LOG_ERROR(maps:merge(Context, Info)),
29 {exception, Info};
30 throw:R ->
31 Info = #{class => throw, reason => R},
32 ?LOG_ERROR(maps:merge(Context, Info)),
33 {exception, Info};
34 exit:R:S ->
35 Info = #{class => exit, reason => R, stacktrace => S},
36 ?LOG_ERROR(maps:merge(Context, Info)),
37 {exception, Info}
38 end).
39
40 -ifdef(TEST).
41 -spec apply(fun((...) -> A), [term()]) -> A | exception().
42 apply(Function, Args) when is_function(Function), is_list(Args) ->
43 ?SAFELY(erlang:apply(Function, Args)).
44
45 -spec apply(atom(), atom(), [term()]) -> term() | exception().
46 apply(Module, Function, Args) when is_atom(Function), is_list(Args) ->
47 ?SAFELY(erlang:apply(Module, Function, Args)).
48 -endif.
49
50 -spec apply_and_log(fun((...) -> A), [term()], map()) -> A | exception().
51 apply_and_log(Function, Args, Context)
52 when is_function(Function), is_list(Args), is_map(Context) ->
53 499 ?MATCH_EXCEPTIONS_DO_LOG(erlang:apply(Function, Args), Context).
54
55 -spec apply_and_log(atom(), atom(), [term()], map()) -> term() | exception().
56 apply_and_log(Module, Function, Args, Context)
57 when is_atom(Function), is_list(Args), is_map(Context) ->
58 3616 ?MATCH_EXCEPTIONS_DO_LOG(erlang:apply(Module, Function, Args), Context).
Line Hits Source