From 1ea378af9cdc3027d1cce19f3c754964a3cdcbd2 Mon Sep 17 00:00:00 2001 From: Scott Lystig Fritchie Date: Tue, 23 Apr 2013 10:19:14 -0500 Subject: [PATCH] Disable I/O to the console if the old-style Erlang console is used There's a nasty problem with the Erlang VM + lager when the old-style Erlang console is used. You can use the "-oldshell" flag to explicitly get the old-style shell. However, if the Erlang VM is started when *not* associated with a pseudo-tty, the VM will silently use the old-style shell (because the new-style shell requires a pty to support command line editing, etc.). The most common way of starting the Erlang VM without a pty is to start it via a non-interactive SSH session. This patch is opinionated in what to do in the case when the old- style shell is detected. My opinion is: 1. Shout loudly to the console log (in a separate Erlang process, so that the main lager event handler proc won't be blocked by any problems with the old-style console). It's almost certainly likely that you really don't want to run Erlang with the old-style shell. But many sysadmins don't look closely at their systems' log files, so (for example) shouting the same message 10x in a row is legit. Reviewers: YMMV. 2. Set a SASL alarm. Again, many sysadmins' are bad at looking at log messages. Setting a SASL alarm is another method to try to get their attention. To test, create a test script called `/tmp/testit.sh`, changing the `-pz` parameter to point to the correct place for lager's `ebin` subdirectory: #!/bin/sh erl -pz /Users/fritchie/b/src/lager/ebin -eval '{application:start(sasl), lager_console_backend:init([error, true]), timer:sleep(5*1000), io:format("\n\nInfo: ~p\n\n", [{{alarms, alarm_handler:get_alarms()}, {user_drv, whereis(user_drv)}}]), erlang:halt()}.' Then run it twice, using the following: First time: ssh -t localhost sh /tmp/testit.sh Second time: ssh localhost sh /tmp/testit.sh The last lines of the first time should look like: Info: {{alarms,[]},{user_drv,<0.22.0>}} The last lines of the second time should look like: Info: {{alarms, [{lager_console_backend, "WARNING: old-style console is in use, so lager_console_backend log output to the console is disabled. Restart the VM on a pseudo-tty to ensure use of the new-style VM console."}]}, {user_drv,undefined}} --- src/lager_console_backend.erl | 43 +++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/lager_console_backend.erl b/src/lager_console_backend.erl index 8f968e0..c0dc4e1 100644 --- a/src/lager_console_backend.erl +++ b/src/lager_console_backend.erl @@ -40,6 +40,13 @@ init([Level, true]) -> % for backwards compatibility init([Level,false]) -> % for backwards compatibility init([Level,{lager_default_formatter,?TERSE_FORMAT ++ [eol()]}]); init([Level,{Formatter,FormatterConfig}]) when is_atom(Formatter) -> + IsSafe = case is_new_style_console_available() of + false=Res -> + spawn(fun warn_user/0), + Res; + Res -> + Res + end, Colors = case application:get_env(lager, colored) of {ok, true} -> {ok, LagerColors} = application:get_env(lager, colors), @@ -47,8 +54,10 @@ init([Level,{Formatter,FormatterConfig}]) when is_atom(Formatter) -> _ -> [] end, - try lager_util:config_to_mask(Level) of - Levels -> + try {IsSafe, lager_util:config_to_mask(Level)} of + {false, _} -> + {error, "Old style console was detected"}; + {true, Levels} -> {ok, #state{level=Levels, formatter=Formatter, format_config=FormatterConfig, @@ -108,6 +117,36 @@ eol() -> "\r\n" end. +-ifdef(TEST). +is_new_style_console_available() -> + true. +-else. +is_new_style_console_available() -> + %% Criteria: + %% 1. If the user has specified '-noshell' on the command line, + %% then we will pretend that the new-style console is available. + %% If there is no shell at all, then we don't have to worry + %% about log events being blocked by the old-style shell. + %% 2. If the user_drv process iss registered, all is OK. + %% 'user_drv' is a registered proc name used by the "new" + %% console driver. + init:get_argument(noshell) /= error orelse + is_pid(whereis(user_drv)). +-endif. + +warn_user() -> + Msg = lists:flatten( + io_lib:format("WARNING: old-style console is in use, so ~s " + "log output to the console is disabled. " + "Restart the VM on a pseudo-tty to ensure " + "use of the new-style VM console.", + [?MODULE])), + catch alarm_handler:set_alarm({?MODULE, Msg}), + [begin + error_logger:warning_msg(Msg), + timer:sleep(1000) + end || _ <- lists:seq(1, 10)]. + -ifdef(TEST). console_log_test_() -> %% tiny recursive fun that pretends to be a group leader