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

vatamane pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 92c7530fc8d5d881cdb452b5412f91b9ead4f1dd
Author: Nick Vatamaniuc <vatam...@apache.org>
AuthorDate: Wed Oct 17 16:24:28 2018 -0400

    Do not crash couch_log application when gen_* servers send extra args
    
    gen_server, gen_fsm and gen_statem might send extra args when terminating. 
This
    is a recent behavior and not handling these extra args could lead to 
couch_log
    application crashing and taking down the whole VM with it.
    
    There are two improvements to fix the issue:
    
     1) Handle the extra args. Format them and log as they might have useful
     information included.
    
     2) Wrap the whole `format` function in a `try ... catch` statement. This 
will
     avoid any other cases where the logger itself if crashing when attepting to
     format error events.
---
 src/couch_log/src/couch_log_formatter.erl       | 52 +++++++++-------
 src/couch_log/test/couch_log_formatter_test.erl | 83 ++++++++++++++++++++++++-
 2 files changed, 112 insertions(+), 23 deletions(-)

diff --git a/src/couch_log/src/couch_log_formatter.erl 
b/src/couch_log/src/couch_log_formatter.erl
index 5be3619..4d81f18 100644
--- a/src/couch_log/src/couch_log_formatter.erl
+++ b/src/couch_log/src/couch_log_formatter.erl
@@ -56,23 +56,33 @@ format(Level, Pid, Msg) ->
     }.
 
 
-format({error, _GL, {Pid, "** Generic server " ++ _, Args}}) ->
+format(Event) ->
+    try
+        do_format(Event)
+    catch
+        Tag:Err ->
+            Msg = "Encountered error ~w when formatting ~w",
+            format(error, self(),  Msg, [{Tag, Err}, Event])
+    end.
+
+
+do_format({error, _GL, {Pid, "** Generic server " ++ _, Args}}) ->
     %% gen_server terminate
-    [Name, LastMsg, State, Reason] = Args,
+    [Name, LastMsg, State, Reason | Extra] = Args,
     MsgFmt = "gen_server ~w terminated with reason: ~s~n" ++
-                "  last msg: ~p~n     state: ~p",
-    MsgArgs = [Name, format_reason(Reason), LastMsg, State],
+                "  last msg: ~p~n     state: ~p~n    extra: ~p",
+    MsgArgs = [Name, format_reason(Reason), LastMsg, State, Extra],
     format(error, Pid, MsgFmt, MsgArgs);
 
-format({error, _GL, {Pid, "** State machine " ++ _, Args}}) ->
+do_format({error, _GL, {Pid, "** State machine " ++ _, Args}}) ->
     %% gen_fsm terminate
-    [Name, LastMsg, StateName, State, Reason] = Args,
+    [Name, LastMsg, StateName, State, Reason | Extra] = Args,
     MsgFmt = "gen_fsm ~w in state ~w terminated with reason: ~s~n" ++
-                " last msg: ~p~n     state: ~p",
-    MsgArgs = [Name, StateName, format_reason(Reason), LastMsg, State],
+                " last msg: ~p~n     state: ~p~n    extra: ~p",
+    MsgArgs = [Name, StateName, format_reason(Reason), LastMsg, State, Extra],
     format(error, Pid, MsgFmt, MsgArgs);
 
-format({error, _GL, {Pid, "** gen_event handler" ++ _, Args}}) ->
+do_format({error, _GL, {Pid, "** gen_event handler" ++ _, Args}}) ->
     %% gen_event handler terminate
     [ID, Name, LastMsg, State, Reason] = Args,
     MsgFmt = "gen_event ~w installed in ~w terminated with reason: ~s~n" ++
@@ -80,20 +90,20 @@ format({error, _GL, {Pid, "** gen_event handler" ++ _, 
Args}}) ->
     MsgArgs = [ID, Name, format_reason(Reason), LastMsg, State],
     format(error, Pid, MsgFmt, MsgArgs);
 
-format({error, _GL, {emulator, "~s~n", [Msg]}}) when is_list(Msg) ->
+do_format({error, _GL, {emulator, "~s~n", [Msg]}}) when is_list(Msg) ->
     % These messages are for whenever any process exits due
     % to a throw or error. We intercept here to remove the
     % extra newlines.
     NewMsg = lists:sublist(Msg, length(Msg) - 1),
     format(error, emulator, NewMsg);
 
-format({error, _GL, {Pid, Fmt, Args}}) ->
+do_format({error, _GL, {Pid, Fmt, Args}}) ->
     format(error, Pid, Fmt, Args);
 
-format({error_report, _GL, {Pid, std_error, D}}) ->
+do_format({error_report, _GL, {Pid, std_error, D}}) ->
     format(error, Pid, print_silly_list(D));
 
-format({error_report, _GL, {Pid, supervisor_report, D}}) ->
+do_format({error_report, _GL, {Pid, supervisor_report, D}}) ->
     case lists:sort(D) of
         [{errorContext, Ctx}, {offender, Off},
                 {reason, Reason}, {supervisor, Name}] ->
@@ -111,20 +121,20 @@ format({error_report, _GL, {Pid, supervisor_report, D}}) 
->
             format(error, Pid, "SUPERVISOR REPORT " ++ print_silly_list(D))
     end;
 
-format({error_report, _GL, {Pid, crash_report, [Report, Neighbors]}}) ->
+do_format({error_report, _GL, {Pid, crash_report, [Report, Neighbors]}}) ->
     Msg = "CRASH REPORT " ++ format_crash_report(Report, Neighbors),
     format(error, Pid, Msg);
 
-format({warning_msg, _GL, {Pid, Fmt, Args}}) ->
+do_format({warning_msg, _GL, {Pid, Fmt, Args}}) ->
     format(warning, Pid, Fmt, Args);
 
-format({warning_report, _GL, {Pid, std_warning, Report}}) ->
+do_format({warning_report, _GL, {Pid, std_warning, Report}}) ->
     format(warning, Pid, print_silly_list(Report));
 
-format({info_msg, _GL, {Pid, Fmt, Args}}) ->
+do_format({info_msg, _GL, {Pid, Fmt, Args}}) ->
     format(info, Pid, Fmt, Args);
 
-format({info_report, _GL, {Pid, std_info, D}}) when is_list(D) ->
+do_format({info_report, _GL, {Pid, std_info, D}}) when is_list(D) ->
     case lists:sort(D) of
         [{application, App}, {exited, Reason}, {type, _Type}] ->
             MsgFmt = "Application ~w exited with reason: ~s",
@@ -133,10 +143,10 @@ format({info_report, _GL, {Pid, std_info, D}}) when 
is_list(D) ->
             format(info, Pid, print_silly_list(D))
     end;
 
-format({info_report, _GL, {Pid, std_info, D}}) ->
+do_format({info_report, _GL, {Pid, std_info, D}}) ->
     format(info, Pid, "~w", [D]);
 
-format({info_report, _GL, {Pid, progress, D}}) ->
+do_format({info_report, _GL, {Pid, progress, D}}) ->
     case lists:sort(D) of
         [{application, App}, {started_at, Node}] ->
             MsgFmt = "Application ~w started on node ~w",
@@ -150,7 +160,7 @@ format({info_report, _GL, {Pid, progress, D}}) ->
             format(info, Pid, "PROGRESS REPORT " ++ print_silly_list(D))
     end;
 
-format(Event) ->
+do_format(Event) ->
     format(warning, self(), "Unexpected error_logger event ~w", [Event]).
 
 
diff --git a/src/couch_log/test/couch_log_formatter_test.erl 
b/src/couch_log/test/couch_log_formatter_test.erl
index a8f69b2..795efcf 100644
--- a/src/couch_log/test/couch_log_formatter_test.erl
+++ b/src/couch_log/test/couch_log_formatter_test.erl
@@ -37,6 +37,29 @@ format_reason_test() ->
     ?assertEqual(Formatted, lists:flatten(Entry#log_entry.msg)).
 
 
+crashing_formatting_test() ->
+    Pid = self(),
+    Event = {
+        error,
+        erlang:group_leader(),
+        {
+            Pid,
+            "** Generic server and some stuff",
+            [a_gen_server, {foo, bar}, server_state]  % not enough args!
+        }
+    },
+    ?assertMatch(
+        #log_entry{
+            level = error,
+            pid = Pid
+        },
+        do_format(Event)
+    ),
+    do_matches(do_format(Event), [
+        "Encountered error {error,{badmatch"
+    ]).
+
+
 gen_server_error_test() ->
     Pid = self(),
     Event = {
@@ -59,7 +82,35 @@ gen_server_error_test() ->
         "gen_server a_gen_server terminated",
         "with reason: some_reason",
         "last msg: {foo,bar}",
-        "state: server_state"
+        "state: server_state",
+        "extra: \\[\\]"
+    ]).
+
+
+gen_server_error_with_extra_args_test() ->
+    Pid = self(),
+    Event = {
+        error,
+        erlang:group_leader(),
+        {
+            Pid,
+            "** Generic server and some stuff",
+            [a_gen_server, {foo, bar}, server_state, some_reason, sad, args]
+        }
+    },
+    ?assertMatch(
+        #log_entry{
+            level = error,
+            pid = Pid
+        },
+        do_format(Event)
+    ),
+    do_matches(do_format(Event), [
+        "gen_server a_gen_server terminated",
+        "with reason: some_reason",
+        "last msg: {foo,bar}",
+        "state: server_state",
+        "extra: \\[sad,args\\]"
     ]).
 
 
@@ -85,7 +136,35 @@ gen_fsm_error_test() ->
         "gen_fsm a_gen_fsm in state state_name",
         "with reason: barf",
         "last msg: {ohai,there}",
-        "state: curr_state"
+        "state: curr_state",
+        "extra: \\[\\]"
+    ]).
+
+
+gen_fsm_error_with_extra_args_test() ->
+    Pid = self(),
+    Event = {
+        error,
+        erlang:group_leader(),
+        {
+            Pid,
+            "** State machine did a thing",
+            [a_gen_fsm, {ohai,there}, state_name, curr_state, barf, sad, args]
+        }
+    },
+    ?assertMatch(
+        #log_entry{
+            level = error,
+            pid = Pid
+        },
+        do_format(Event)
+    ),
+    do_matches(do_format(Event), [
+        "gen_fsm a_gen_fsm in state state_name",
+        "with reason: barf",
+        "last msg: {ohai,there}",
+        "state: curr_state",
+        "extra: \\[sad,args\\]"
     ]).
 
 

Reply via email to