Ver código fonte

Merge pull request #469 from erlang-lager/adt/remote_console

Allow the console backend to redirect output to a remote node
pull/472/head
Mark Allen 6 anos atrás
committed by GitHub
pai
commit
a7c20a91fe
Nenhuma chave conhecida encontrada para esta assinatura no banco de dados ID da chave GPG: 4AEE18F83AFDEB23
3 arquivos alterados com 141 adições e 6 exclusões
  1. +26
    -0
      README.md
  2. +7
    -1
      src/lager.erl
  3. +108
    -5
      src/lager_console_backend.erl

+ 26
- 0
README.md Ver arquivo

@ -982,6 +982,32 @@ You can remove the trace when you're done by doing:
lager:remove_trace(Pid).
```
Console output to another group leader process
----------------------------------------------
If you want to send your console output to another group_leader (typically on
another node) you can provide a `{group_leader, Pid}` argument to the console
backend. This can be combined with another console config option, `id` and
gen_event's `{Module, ID}` to allow remote tracing of a node to standard out via
nodetool:
```erlang
GL = erlang:group_leader(),
Node = node(GL),
lager_app:start_handler(lager_event, {lager_console_backend, Node},
[{group_leader, GL}, {level, none}, {id, {lager_console_backend, Node}}]),
case lager:trace({lager_console_backend, Node}, Filter, Level) of
...
```
In the above example, the code is assumed to be running via a `nodetool rpc`
invocation so that the code is executing on the Erlang node, but the
group_leader is that of the reltool node (eg. appname_maint_12345@127.0.0.1).
If you intend to use tracing with this feature, make sure the second parameter
to start_handler and the `id` parameter match. Thus when the custom gen_leader
process exits, lager will remove any associated traces for that handler.
Elixir Support
--------------

+ 7
- 1
src/lager.erl Ver arquivo

@ -32,7 +32,7 @@
rotate_handler/1, rotate_handler/2, rotate_sink/1, rotate_all/0,
trace/2, trace/3, trace_file/2, trace_file/3, trace_file/4, trace_console/1, trace_console/2,
install_trace/2, remove_trace/1, trace_func/3,
list_all_sinks/0, clear_all_traces/0, stop_trace/1, stop_trace/3, status/0,
list_all_sinks/0, clear_all_traces/0, clear_trace_by_destination/1, stop_trace/1, stop_trace/3, status/0,
get_loglevel/1, get_loglevel/2, set_loglevel/2, set_loglevel/3, set_loglevel/4, get_loglevels/1,
update_loglevel_config/1, posix_error/1, set_loghwm/2, set_loghwm/3, set_loghwm/4,
safe_format/3, safe_format_chop/3, unsafe_format/2, dispatch_log/5, dispatch_log/7, dispatch_log/9,
@ -336,6 +336,12 @@ clear_traces_by_sink(Sinks) ->
end,
Sinks).
clear_trace_by_destination(ID) ->
Sinks = lists:sort(list_all_sinks()),
Traces = find_traces(Sinks),
[ stop_trace_int({Filter, Level, Destination}, Sink) || {Sink, {Filter, Level, Destination}} <- Traces, Destination == ID].
clear_all_traces() ->
Handlers = lager_config:global_get(handlers, []),
clear_traces_by_sink(list_all_sinks()),

+ 108
- 5
src/lager_console_backend.erl Ver arquivo

@ -34,7 +34,8 @@
code_change/3]).
-record(state, {level :: {'mask', integer()},
out = user :: user | standard_error,
out = user :: user | standard_error | pid(),
id :: atom() | {atom(), any()},
formatter :: atom(),
format_config :: any(),
colors=[] :: list()}).
@ -93,17 +94,26 @@ init(Options) when is_list(Options) ->
?INT_LOG(warning, Msg, []),
{error, {fatal, old_shell}};
{true, L} ->
[UseErr, Formatter, Config] = [ get_option(K, Options, Default) || {K, Default} <- [
[UseErr, GroupLeader, ID, Formatter, Config] = [ get_option(K, Options, Default) || {K, Default} <- [
{use_stderr, false},
{group_leader, false},
{id, ?MODULE},
{formatter, lager_default_formatter},
{formatter_config, ?DEFAULT_FORMAT_CONFIG}
]
],
Out = case UseErr of
false -> user;
false ->
case GroupLeader of
false -> user;
GLPid when is_pid(GLPid) ->
erlang:monitor(process, GLPid),
GLPid
end;
true -> standard_error
end,
{ok, #state{level=L,
id=ID,
out=Out,
formatter=Formatter,
format_config=Config,
@ -134,6 +144,10 @@ validate_options([{formatter, M}|T]) when is_atom(M) ->
validate_options(T);
validate_options([{formatter_config, C}|T]) when is_list(C) ->
validate_options(T);
validate_options([{group_leader, L}|T]) when is_pid(L) ->
validate_options(T);
validate_options([{id, {?MODULE, _}}|T]) ->
validate_options(T);
validate_options([H|_]) ->
throw({error, {fatal, {bad_console_config, H}}}).
@ -159,8 +173,8 @@ handle_call(_Request, State) ->
%% @private
handle_event({log, Message},
#state{level=L,out=Out,formatter=Formatter,format_config=FormatConfig,colors=Colors} = State) ->
case lager_util:is_loggable(Message, L, ?MODULE) of
#state{level=L,out=Out,formatter=Formatter,format_config=FormatConfig,colors=Colors,id=ID} = State) ->
case lager_util:is_loggable(Message, L, ID) of
true ->
io:put_chars(Out, Formatter:format(Message,FormatConfig,Colors)),
{ok, State};
@ -171,10 +185,16 @@ handle_event(_Event, State) ->
{ok, State}.
%% @private
handle_info({'DOWN', _, process, Out, _}, #state{out=Out}) ->
remove_handler;
handle_info(_Info, State) ->
{ok, State}.
%% @private
terminate(remove_handler, _State=#state{id=ID}) ->
%% have to do this asynchronously because we're in the event handlr
spawn(fun() -> lager:clear_trace_by_destination(ID) end),
ok;
terminate(_Reason, _State) ->
ok.
@ -455,6 +475,89 @@ console_log_test_() ->
?assert(true)
end
end
},
{"console backend with custom group leader",
fun() ->
Pid = spawn(F(self())),
gen_event:add_handler(lager_event, lager_console_backend, [{level, info}, {group_leader, Pid}]),
lager_config:set({lager_event, loglevel}, {element(2, lager_util:config_to_mask(info)), []}),
lager:info("Test message"),
?assertNotEqual({group_leader, Pid}, erlang:process_info(whereis(lager_event), group_leader)),
receive
{io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} ->
From1 ! {io_reply, ReplyAs1, ok},
TestMsg = "Test message" ++ eol(),
?assertMatch([_, "[info]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}]))
after
1000 ->
?assert(false)
end,
%% killing the pid should prevent any new log messages (to prove we haven't accidentally redirected
%% the group leader some other way
exit(Pid, kill),
timer:sleep(100),
%% additionally, check the lager backend has been removed (because the group leader process died)
?assertNot(lists:member(lager_console_backend, gen_event:which_handlers(lager_event))),
lager:error("Test message"),
receive
{io_request, From2, ReplyAs2, {put_chars, unicode, _Msg2}} ->
From2 ! {io_reply, ReplyAs2, ok},
?assert(false)
after
500 ->
?assert(true)
end
end
},
{"console backend with custom group leader using a trace and an ID",
fun() ->
Pid = spawn(F(self())),
ID = {?MODULE, trace_test},
Handlers = lager_config:global_get(handlers, []),
HandlerInfo = lager_app:start_handler(lager_event, ID,
[{level, none}, {group_leader, Pid},
{id, ID}]),
lager_config:global_set(handlers, [HandlerInfo|Handlers]),
lager:info("Test message"),
?assertNotEqual({group_leader, Pid}, erlang:process_info(whereis(lager_event), group_leader)),
receive
{io_request, From, ReplyAs, {put_chars, unicode, _Msg}} ->
From ! {io_reply, ReplyAs, ok},
?assert(false)
after
500 ->
?assert(true)
end,
lager:trace(ID, [{module, ?MODULE}], debug),
lager:info("Test message"),
receive
{io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} ->
From1 ! {io_reply, ReplyAs1, ok},
TestMsg = "Test message" ++ eol(),
?assertMatch([_, "[info]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}]))
after
500 ->
?assert(false)
end,
?assertNotEqual({0, []}, lager_config:get({lager_event, loglevel})),
%% killing the pid should prevent any new log messages (to prove we haven't accidentally redirected
%% the group leader some other way
exit(Pid, kill),
timer:sleep(100),
%% additionally, check the lager backend has been removed (because the group leader process died)
?assertNot(lists:member(lager_console_backend, gen_event:which_handlers(lager_event))),
%% finally, check the trace has been removed
?assertEqual({0, []}, lager_config:get({lager_event, loglevel})),
lager:error("Test message"),
receive
{io_request, From3, ReplyAs3, {put_chars, unicode, _Msg3}} ->
From3 ! {io_reply, ReplyAs3, ok},
?assert(false)
after
500 ->
?assert(true)
end
end
}
]
}.

Carregando…
Cancelar
Salvar