diff options
| author | Luke Bakken <luke@bakken.io> | 2020-01-14 07:06:45 -0800 |
|---|---|---|
| committer | Luke Bakken <luke@bakken.io> | 2020-01-14 15:47:33 -0800 |
| commit | 94198f125374f91c7a178f8e0ac62d0b0184d63a (patch) | |
| tree | 0b1493e03381f48c8057d9deea6e4ffdabc5bb3b /deps | |
| parent | 84f50193640118fa724edefd8a9461446c1a97d6 (diff) | |
| download | rabbitmq-server-git-94198f125374f91c7a178f8e0ac62d0b0184d63a.tar.gz | |
Store last error log in state
This allows logging errors only once every 10 minutes. Prior to an
earlier change this time was stored in the process dictionary. Storing
in the state is the "correct" way to do it.
Follow-up to #90
Diffstat (limited to 'deps')
| -rw-r--r-- | deps/rabbitmq_management_agent/src/rabbit_mgmt_external_stats.erl | 279 |
1 files changed, 182 insertions, 97 deletions
diff --git a/deps/rabbitmq_management_agent/src/rabbit_mgmt_external_stats.erl b/deps/rabbitmq_management_agent/src/rabbit_mgmt_external_stats.erl index 01927d1a80..fbbfa33697 100644 --- a/deps/rabbitmq_management_agent/src/rabbit_mgmt_external_stats.erl +++ b/deps/rabbitmq_management_agent/src/rabbit_mgmt_external_stats.erl @@ -44,6 +44,8 @@ db_dir, config_files, net_ticktime, enabled_plugins, mem_calculation_strategy, ra_open_file_metrics]). +-define(TEN_MINUTES_AS_SECONDS, 600). + %%-------------------------------------------------------------------- -record(state, { @@ -51,7 +53,8 @@ fhc_stats, node_owners, last_ts, - interval + interval, + error_logged_time }). %%-------------------------------------------------------------------- @@ -61,50 +64,56 @@ start_link() -> %%-------------------------------------------------------------------- -get_used_fd() -> +get_used_fd(State0) -> try - case get_used_fd(os:type()) of - Fd when is_number(Fd) -> - Fd; - _Other -> + case get_used_fd(os:type(), State0) of + {State1, UsedFd} when is_number(UsedFd) -> + {State1, UsedFd}; + {State1, _Other} -> %% Defaults to 0 if data is not available - 0 + {State1, 0} end catch _:Error -> - log_fd_error("Could not infer the number of file handles used: ~p~n", - [Error]), - 0 + State2 = log_fd_error("Could not infer the number of file handles used: ~p~n", [Error], State0), + {State2, 0} end. -get_used_fd({unix, linux}) -> +get_used_fd({unix, linux}, State0) -> case file:list_dir("/proc/" ++ os:getpid() ++ "/fd") of - {ok, Files} -> length(Files); - {error, _} -> get_used_fd({unix, generic}) + {ok, Files} -> + {State0, length(Files)}; + {error, _} -> + get_used_fd({unix, generic}, State0) end; -get_used_fd({unix, BSD}) +get_used_fd({unix, BSD}, State0) when BSD == openbsd; BSD == freebsd; BSD == netbsd -> - Digit = fun (D) -> lists:member(D, "0123456789*") end, + IsDigit = fun (D) -> lists:member(D, "0123456789*") end, Output = os:cmd("fstat -p " ++ os:getpid()), try - length( - lists:filter( - fun (Line) -> - lists:all(Digit, (lists:nth(4, string:tokens(Line, " ")))) - end, string:tokens(Output, "\n"))) + F = fun (Line) -> + lists:all(IsDigit, lists:nth(4, string:tokens(Line, " "))) + end, + UsedFd = length(lists:filter(F, string:tokens(Output, "\n"))), + {State0, UsedFd} catch _:Error -> - log_fd_error("Could not parse fstat output:~n~s~n~p~n", - [Output, {Error, erlang:get_stacktrace()}]) + State1 = log_fd_error("Could not parse fstat output:~n~s~n~p~n", + [Output, {Error, erlang:get_stacktrace()}], State0), + {State1, 0} end; -get_used_fd({unix, _}) -> +get_used_fd({unix, _}, State0) -> Cmd = rabbit_misc:format( "lsof -d \"0-9999999\" -lna -p ~s || echo failed", [os:getpid()]), Res = os:cmd(Cmd), case string:right(Res, 7) of - "failed\n" -> log_fd_error("Could not obtain lsof output~n", []); - _ -> string:words(Res, $\n) - 1 + "failed\n" -> + State1 = log_fd_error("Could not obtain lsof output~n", [], State0), + {State1, 0}; + _ -> + UsedFd = string:words(Res, $\n) - 1, + {State0, UsedFd} end; %% handle.exe can be obtained from @@ -134,27 +143,55 @@ get_used_fd({unix, _}) -> %% WindowStation : 2 %% Total handles: 238 +%% Nthandle v4.22 - Handle viewer +%% Copyright (C) 1997-2019 Mark Russinovich +%% Sysinternals - www.sysinternals.com +%% +%% Handle type summary: +%% <Unknown type> : 1 +%% <Unknown type> : 166 +%% ALPC Port : 11 +%% Desktop : 1 +%% Directory : 2 +%% Event : 226 +%% File : 122 +%% IoCompletion : 8 +%% IRTimer : 6 +%% Key : 42 +%% Mutant : 7 +%% Process : 3 +%% Section : 2 +%% Semaphore : 43 +%% Thread : 36 +%% TpWorkerFactory : 3 +%% WaitCompletionPacket: 25 +%% WindowStation : 2 +%% Total handles: 706 + %% Note that the "File" number appears to include network sockets too; I assume %% that's the number we care about. Note also that if you omit "-s" you will %% see a list of file handles *without* network sockets. If you then add "-a" %% you will see a list of handles of various types, including network sockets %% shown as file handles to \Device\Afd. -get_used_fd({win32, _}) -> +get_used_fd({win32, _}, State0) -> Handle = rabbit_misc:os_cmd( "handle.exe /accepteula -s -p " ++ os:getpid() ++ " 2> nul"), case Handle of - [] -> rabbit_log:warning("Could not find handle.exe, please install from " - "sysinternals~n", []); - _ -> case find_files_line(string:tokens(Handle, "\r\n")) of - unknown -> - log_fd_error("handle.exe output did not contain " - "a line beginning with ' File ', unable " - "to determine used file descriptor " - "count: ~p~n", [Handle]); - Any -> - Any - end + [] -> + State1 = log_fd_error("Could not find handle.exe, please install from sysinternals~n", [], State0), + {State1, 0}; + _ -> + case find_files_line(string:tokens(Handle, "\r\n")) of + unknown -> + State1 = log_fd_error("handle.exe output did not contain " + "a line beginning with ' File ', unable " + "to determine used file descriptor " + "count: ~p~n", [Handle], State0), + {State1, 0}; + UsedFd -> + {State0, UsedFd} + end end. find_files_line([]) -> @@ -177,66 +214,112 @@ get_disk_free_limit() -> ?SAFE_CALL(rabbit_disk_monitor:get_disk_free_limit(), get_disk_free() -> ?SAFE_CALL(rabbit_disk_monitor:get_disk_free(), disk_free_monitoring_disabled). -log_fd_error(Fmt, Args) -> - rabbit_log:error(Fmt, Args). +log_fd_error(Fmt, Args, #state{error_logged_time = undefined}=State) -> + % rabbitmq/rabbitmq-management#90 + % no errors have been logged, so log it and make a note of when + Now = erlang:monotonic_time(second), + ok = rabbit_log:error(Fmt, Args), + State#state{error_logged_time = Now}; +log_fd_error(Fmt, Args, #state{error_logged_time = Time}=State) -> + Now = erlang:monotonic_time(second), + case Now >= Time + ?TEN_MINUTES_AS_SECONDS of + true -> + % rabbitmq/rabbitmq-management#90 + % it has been longer than 10 minutes, + % re-log the error + ok = rabbit_log:error(Fmt, Args), + State#state{error_logged_time = Now}; + _ -> + % 10 minutes have not yet passed + State + end. %%-------------------------------------------------------------------- -infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. - -i(name, _State) -> node(); -i(partitions, _State) -> rabbit_node_monitor:partitions(); -i(fd_used, _State) -> get_used_fd(); -i(fd_total, #state{fd_total = FdTotal}) -> FdTotal; -i(sockets_used, _State) -> - proplists:get_value(sockets_used, file_handle_cache:info([sockets_used])); -i(sockets_total, _State) -> - proplists:get_value(sockets_limit, file_handle_cache:info([sockets_limit])); -i(os_pid, _State) -> list_to_binary(os:getpid()); - -i(mem_used, _State) -> vm_memory_monitor:get_process_memory(); -i(mem_calculation_strategy, _State) -> vm_memory_monitor:get_memory_calculation_strategy(); -i(mem_limit, _State) -> vm_memory_monitor:get_memory_limit(); -i(mem_alarm, _State) -> resource_alarm_set(memory); -i(proc_used, _State) -> erlang:system_info(process_count); -i(proc_total, _State) -> erlang:system_info(process_limit); -i(run_queue, _State) -> erlang:statistics(run_queue); -i(processors, _State) -> erlang:system_info(logical_processors); -i(disk_free_limit, _State) -> get_disk_free_limit(); -i(disk_free, _State) -> get_disk_free(); -i(disk_free_alarm, _State) -> resource_alarm_set(disk); -i(contexts, _State) -> rabbit_web_dispatch_contexts(); -i(uptime, _State) -> {Total, _} = erlang:statistics(wall_clock), - Total; -i(rates_mode, _State) -> rabbit_mgmt_db_handler:rates_mode(); -i(exchange_types, _State) -> list_registry_plugins(exchange); -i(log_files, _State) -> [list_to_binary(F) || F <- rabbit:log_locations()]; -i(db_dir, _State) -> list_to_binary(rabbit_mnesia:dir()); -i(config_files, _State) -> [list_to_binary(F) || F <- rabbit:config_files()]; -i(net_ticktime, _State) -> net_kernel:get_net_ticktime(); -i(persister_stats, State) -> persister_stats(State); -i(enabled_plugins, _State) -> {ok, Dir} = application:get_env( - rabbit, enabled_plugins_file), - rabbit_plugins:read_enabled(Dir); -i(auth_mechanisms, _State) -> +infos([], Acc, State) -> + {State, lists:reverse(Acc)}; +infos([Item|T], Acc0, State0) -> + {State1, Infos} = i(Item, State0), + Acc1 = [{Item, Infos}|Acc0], + infos(T, Acc1, State1). + +i(name, State) -> + {State, node()}; +i(partitions, State) -> + {State, rabbit_node_monitor:partitions()}; +i(fd_used, State) -> + get_used_fd(State); +i(fd_total, #state{fd_total = FdTotal}=State) -> + {State, FdTotal}; +i(sockets_used, State) -> + {State, proplists:get_value(sockets_used, file_handle_cache:info([sockets_used]))}; +i(sockets_total, State) -> + {State, proplists:get_value(sockets_limit, file_handle_cache:info([sockets_limit]))}; +i(os_pid, State) -> + {State, list_to_binary(os:getpid())}; +i(mem_used, State) -> + {State, vm_memory_monitor:get_process_memory()}; +i(mem_calculation_strategy, State) -> + {State, vm_memory_monitor:get_memory_calculation_strategy()}; +i(mem_limit, State) -> + {State, vm_memory_monitor:get_memory_limit()}; +i(mem_alarm, State) -> + {State, resource_alarm_set(memory)}; +i(proc_used, State) -> + {State, erlang:system_info(process_count)}; +i(proc_total, State) -> + {State, erlang:system_info(process_limit)}; +i(run_queue, State) -> + {State, erlang:statistics(run_queue)}; +i(processors, State) -> + {State, erlang:system_info(logical_processors)}; +i(disk_free_limit, State) -> + {State, get_disk_free_limit()}; +i(disk_free, State) -> + {State, get_disk_free()}; +i(disk_free_alarm, State) -> + {State, resource_alarm_set(disk)}; +i(contexts, State) -> + {State, rabbit_web_dispatch_contexts()}; +i(uptime, State) -> + {Total, _} = erlang:statistics(wall_clock), + {State, Total}; +i(rates_mode, State) -> + {State, rabbit_mgmt_db_handler:rates_mode()}; +i(exchange_types, State) -> + {State, list_registry_plugins(exchange)}; +i(log_files, State) -> + {State, [list_to_binary(F) || F <- rabbit:log_locations()]}; +i(db_dir, State) -> + {State, list_to_binary(rabbit_mnesia:dir())}; +i(config_files, State) -> + {State, [list_to_binary(F) || F <- rabbit:config_files()]}; +i(net_ticktime, State) -> + {State, net_kernel:get_net_ticktime()}; +i(persister_stats, State) -> + {State, persister_stats(State)}; +i(enabled_plugins, State) -> + {ok, Dir} = application:get_env(rabbit, enabled_plugins_file), + {State, rabbit_plugins:read_enabled(Dir)}; +i(auth_mechanisms, State) -> {ok, Mechanisms} = application:get_env(rabbit, auth_mechanisms), - list_registry_plugins( - auth_mechanism, - fun (N) -> lists:member(list_to_atom(binary_to_list(N)), Mechanisms) end); -i(applications, _State) -> - [format_application(A) || - A <- lists:keysort(1, rabbit_misc:which_applications())]; -i(gc_num, _State) -> + F = fun (N) -> + lists:member(list_to_atom(binary_to_list(N)), Mechanisms) + end, + {State, list_registry_plugins(auth_mechanism, F)}; +i(applications, State) -> + {State, [format_application(A) || A <- lists:keysort(1, rabbit_misc:which_applications())]}; +i(gc_num, State) -> {GCs, _, _} = erlang:statistics(garbage_collection), - GCs; -i(gc_bytes_reclaimed, _State) -> + {State, GCs}; +i(gc_bytes_reclaimed, State) -> {_, Words, _} = erlang:statistics(garbage_collection), - Words * erlang:system_info(wordsize); -i(context_switches, _State) -> + {State, Words * erlang:system_info(wordsize)}; +i(context_switches, State) -> {Sw, 0} = erlang:statistics(context_switches), - Sw; -i(ra_open_file_metrics, _State) -> - [{ra_log_wal, ra_metrics(ra_log_wal)}, - {ra_log_segment_writer, ra_metrics(ra_log_segment_writer)}]. + {State, Sw}; +i(ra_open_file_metrics, State) -> + {State, [{ra_log_wal, ra_metrics(ra_log_wal)}, + {ra_log_segment_writer, ra_metrics(ra_log_segment_writer)}]}. ra_metrics(K) -> try @@ -384,16 +467,18 @@ code_change(_, State, _) -> {ok, State}. %%-------------------------------------------------------------------- emit_update(State0) -> - State = update_state(State0), - MStats = infos(?METRICS_KEYS, State), - [{persister_stats, PStats0}] = PStats = infos(?PERSISTER_KEYS, State), - [{name, _Name} | OStats0] = OStats = infos(?OTHER_KEYS, State), + State1 = update_state(State0), + {State2, MStats} = infos(?METRICS_KEYS, [], State1), + {State3, PStats} = infos(?PERSISTER_KEYS, [], State2), + {State4, OStats} = infos(?OTHER_KEYS, [], State3), + [{persister_stats, PStats0}] = PStats, + [{name, _Name} | OStats0] = OStats, rabbit_core_metrics:node_stats(persister_metrics, PStats0), rabbit_core_metrics:node_stats(coarse_metrics, MStats), rabbit_core_metrics:node_stats(node_metrics, OStats0), rabbit_event:notify(node_stats, PStats ++ MStats ++ OStats), - erlang:send_after(State#state.interval, self(), emit_update), - emit_node_node_stats(State). + erlang:send_after(State4#state.interval, self(), emit_update), + emit_node_node_stats(State4). emit_node_node_stats(State = #state{node_owners = Owners}) -> Links = cluster_links(), |
