Capture detailed Erlang logs on connection failure

This commit is contained in:
Diana Corbacho 2016-02-23 12:06:05 +00:00
parent 8f217bd3fa
commit bd639b8b2c
2 changed files with 200 additions and 5 deletions

View File

@ -0,0 +1,175 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
%%
-module(rabbit_error_logger_handler).
-behaviour(gen_event).
%% API
-export([start_link/0, add_handler/0]).
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2,
handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {report = []}).
%%%===================================================================
%%% API
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% Creates an event manager
%%
%% @spec start_link() -> {ok, Pid} | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_event:start_link({local, ?SERVER}).
%%--------------------------------------------------------------------
%% @doc
%% Adds an event handler
%%
%% @spec add_handler() -> ok | {'EXIT', Reason} | term()
%% @end
%%--------------------------------------------------------------------
add_handler() ->
gen_event:add_handler(?SERVER, ?MODULE, []).
%%%===================================================================
%%% gen_event callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever a new event handler is added to an event manager,
%% this function is called to initialize the event handler.
%%
%% @spec init(Args) -> {ok, State}
%% @end
%%--------------------------------------------------------------------
init([]) ->
{ok, #state{}}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever an event manager receives an event sent using
%% gen_event:notify/2 or gen_event:sync_notify/2, this function is
%% called for each installed event handler to handle the event.
%%
%% @spec handle_event(Event, State) ->
%% {ok, State} |
%% {swap_handler, Args1, State1, Mod2, Args2} |
%% remove_handler
%% @end
%%--------------------------------------------------------------------
handle_event({info_report, _Gleader, {_Pid, _Type,
{net_kernel, {'EXIT', _, Reason}}}},
#state{report = Report} = State) ->
NewReport = case format(Reason) of
[] -> Report;
Formatted -> [Formatted | Report]
end,
{ok, State#state{report = NewReport}};
handle_event(_Event, State) ->
{ok, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever an event manager receives a request sent using
%% gen_event:call/3,4, this function is called for the specified
%% event handler to handle the request.
%%
%% @spec handle_call(Request, State) ->
%% {ok, Reply, State} |
%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |
%% {remove_handler, Reply}
%% @end
%%--------------------------------------------------------------------
handle_call(get_connection_report, State) ->
{ok, lists:reverse(State#state.report), State#state{report = []}};
handle_call(_Request, State) ->
Reply = ok,
{ok, Reply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called for each installed event handler when
%% an event manager receives any other message than an event or a
%% synchronous request (or a system message).
%%
%% @spec handle_info(Info, State) ->
%% {ok, State} |
%% {swap_handler, Args1, State1, Mod2, Args2} |
%% remove_handler
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{ok, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever an event handler is deleted from an event manager, this
%% function is called. It should be the opposite of Module:init/1 and
%% do any necessary cleaning up.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
format({check_dflag_xnc_failed, _What}) ->
{" * Remote node is using an incompatible Erlang version ~n", []};
format({recv_challenge_failed, no_node, Node}) ->
{" * Hostname mismatch ~p~n", [Node]};
format({recv_challenge_failed, Error}) ->
{" * Distribution failed unexpectedly while waiting for challenge: ~p~n", [Error]};
format({recv_challenge_ack_failed, bad_cookie}) ->
{" * Authorisation rejected by local node, please verify the cookie~n", []};
format({recv_challenge_ack_failed, {error, closed}}) ->
{" * Authorisation rejected by remote node, please verify the cookie~n", []};
format({recv_status_failed, not_allowed}) ->
{" * Host not in remote allowed list (see net_kernel:allow/1)~n", []};
format({recv_status_failed, {error, closed}}) ->
{" * Remote host closed the connection. Is the Erlang distribution using TLS?~n", []};
format(setup_timer_timeout) ->
{" * Remote connection has timed out. Is the Erlang distribution using TLS?~n", []};
format(_) ->
[].

View File

@ -25,6 +25,7 @@
-define(EPMD_TIMEOUT, 30000).
-define(TCP_DIAGNOSTIC_TIMEOUT, 5000).
-define(ERROR_LOGGER_HANDLER, rabbit_error_logger_handler).
%%----------------------------------------------------------------------------
%% Specs
@ -62,12 +63,21 @@ names(Hostname) ->
end.
diagnostics(Nodes) ->
verbose_erlang_distribution(true),
NodeDiags = [{"~nDIAGNOSTICS~n===========~n~n"
"attempted to contact: ~p~n", [Nodes]}] ++
[diagnostics_node(Node) || Node <- Nodes] ++
current_node_details(),
verbose_erlang_distribution(false),
rabbit_misc:format_many(lists:flatten(NodeDiags)).
verbose_erlang_distribution(true) ->
net_kernel:verbose(1),
error_logger:add_report_handler(?ERROR_LOGGER_HANDLER);
verbose_erlang_distribution(false) ->
net_kernel:verbose(0),
error_logger:delete_report_handler(?ERROR_LOGGER_HANDLER).
current_node_details() ->
[{"~ncurrent node details:~n- node name: ~w", [node()]},
case init:get_argument(home) of
@ -136,11 +146,7 @@ dist_broken_diagnostics(Name, Host, NamePorts) ->
[{" * epmd reports node '~s' running on port ~b", [Name, Port]} |
case diagnose_connect(Host, Port) of
ok ->
[{" * TCP connection succeeded but Erlang distribution "
"failed~n"
" * suggestion: hostname mismatch?~n"
" * suggestion: is the cookie set correctly?~n"
" * suggestion: is the Erlang distribution using TLS?", []}];
connection_succeeded_diagnostics();
{error, Reason} ->
[{" * can't establish TCP connection, reason: ~s~n"
" * suggestion: blocked by firewall?",
@ -148,6 +154,20 @@ dist_broken_diagnostics(Name, Host, NamePorts) ->
end]
end.
connection_succeeded_diagnostics() ->
case gen_event:call(error_logger, ?ERROR_LOGGER_HANDLER, get_connection_report) of
[] ->
[{" * TCP connection succeeded but Erlang distribution "
"failed~n"
" * suggestion: hostname mismatch?~n"
" * suggestion: is the cookie set correctly?~n"
" * suggestion: is the Erlang distribution using TLS?", []}];
Report ->
[{" * TCP connection succeeded but Erlang distribution "
"failed~n", []}]
++ Report
end.
diagnose_connect(Host, Port) ->
case inet:gethostbyname(Host) of
{ok, #hostent{h_addrtype = Family}} ->