From 1b6e3ac9c0e3b77215b119427b9a4d530cb08e1d Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 31 Jan 2019 09:22:23 -0500 Subject: Unhang logger when swapping the TTY in shell Starting with OTP-21.2.3, some output appears to be sent to the shell while we're taking down the TTY, which ends up stalling the whole log flow for the default handler. I don't have proof but suspect this is due to the system logging going directly from the ERTS runtime to the logger, which may be trigger when we kill the TTY driver. This patch makes it so whenever we detect that logger is active, we save the default config, disable the default logger, and only then we can kill the TTY driver. Once the driver is started and all the group leaders have been rewritten, the default logger is re-added. This all takes place _before_ the logger configuration is updated as part of booting apps, and the change should be fully backwards compatible. --- src/rebar_prv_shell.erl | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 760f0d8..4ba3c39 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -128,6 +128,7 @@ info() -> "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n". setup_shell(ShellArgs) -> + LoggerState = maybe_remove_logger(), OldUser = kill_old_user(), %% Test for support here NewUser = try erlang:open_port({spawn,"tty_sl -c -e"}, []) of @@ -138,7 +139,28 @@ setup_shell(ShellArgs) -> error:_ -> setup_old_shell() end, - rewrite_leaders(OldUser, NewUser). + rewrite_leaders(OldUser, NewUser), + maybe_reset_logger(LoggerState). + +%% @private starting with OTP-21.2.3, there's an oddity where the logger +%% likely tries to handle system logs while we take down the TTY, which +%% ends up hanging the default logger. This function (along with +%% `maybe_reset_logger/1') removes and re-adds the default logger before and +%% after the TTY subsystem is taken offline, which prevents such hanging. +maybe_remove_logger() -> + case erlang:function_exported(logger, module_info, 0) of + false -> + ignore; + true -> + {ok, Cfg} = logger:get_handler_config(default), + logger:remove_handler(default), + {restart, Cfg} + end. + +maybe_reset_logger(ignore) -> + ok; +maybe_reset_logger({restart, #{module := Mod, config := Cfg}}) -> + logger:add_handler(default, Mod, Cfg). kill_old_user() -> OldUser = whereis(user), -- cgit v1.1