Skip to content

Commit

Permalink
Merge pull request #2047 from rabbitmq/rabbitmq_cli_log_commands
Browse files Browse the repository at this point in the history
Helpers for CLI logs command

(cherry picked from commit df5952a)
  • Loading branch information
michaelklishin committed Jul 6, 2019
1 parent a1a904a commit 8c4d0ef
Showing 1 changed file with 95 additions and 0 deletions.
95 changes: 95 additions & 0 deletions src/rabbit_log_tail.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
-module(rabbit_log_tail).

-export([tail_n_lines/2]).
-export([init_tail_stream/4]).

-define(GUESS_OFFSET, 200).

init_tail_stream(Filename, Pid, Ref, Duration) ->
RPCProc = self(),
Reader = spawn(fun() ->
link(Pid),
case file:open(Filename, [read, binary]) of
{ok, File} ->
TimeLimit = case Duration of
infinity -> infinity;
_ -> erlang:system_time(second) + Duration
end,
{ok, _} = file:position(File, eof),
RPCProc ! {Ref, opened},
read_loop(File, Pid, Ref, TimeLimit);
{error, _} = Err ->
RPCProc ! {Ref, Err}
end
end),
receive
{Ref, opened} -> {ok, Ref};
{Ref, {error, Err}} -> {error, Err}
after 5000 ->
exit(Reader, timeout),
{error, timeout}
end.

read_loop(File, Pid, Ref, TimeLimit) ->
case is_integer(TimeLimit) andalso erlang:system_time(second) > TimeLimit of
true -> Pid ! {Ref, <<>>, finished};
false ->
case file:read(File, ?GUESS_OFFSET) of
{ok, Data} ->
Pid ! {Ref, Data, confinue},
read_loop(File, Pid, Ref, TimeLimit);
eof ->
timer:sleep(1000),
read_loop(File, Pid, Ref, TimeLimit);
{error, _} = Err ->
Pid ! {Ref, Err, finished}
end
end.

tail_n_lines(Filename, N) ->
case file:open(Filename, [read, binary]) of
{ok, File} ->
{ok, Eof} = file:position(File, eof),
%% Eof may move. Only read up to the current one.
Result = reverse_read_n_lines(N, N, File, Eof, Eof),
file:close(File),
Result;
{error, _} = Error -> Error
end.

reverse_read_n_lines(N, OffsetN, File, Position, Eof) ->
GuessPosition = offset(Position, OffsetN),
case read_lines_from_position(File, GuessPosition, Eof) of
{ok, Lines} ->
NLines = length(Lines),
case {NLines >= N, GuessPosition == 0} of
%% Take only N lines if there is more
{true, _} -> lists:nthtail(NLines - N, Lines);
%% Safe to assume that NLines is less then N
{_, true} -> Lines;
%% Adjust position
_ ->
reverse_read_n_lines(N, N - NLines + 1, File, GuessPosition, Eof)
end;
{error, _} = Error -> Error
end.

read_from_position(File, GuessPosition, Eof) ->
file:pread(File, GuessPosition, max(0, Eof - GuessPosition)).

read_lines_from_position(File, GuessPosition, Eof) ->
case read_from_position(File, GuessPosition, Eof) of
{ok, Data} ->
Lines = binary:split(Data, <<"\n">>, [global, trim]),
case {GuessPosition, Lines} of
%% If position is 0 - there are no partial lines
{0, _} -> {ok, Lines};
%% Remove first line as it can be partial
{_, [_ | Rest]} -> {ok, Rest};
{_, []} -> {ok, []}
end;
{error, _} = Error -> Error
end.

offset(Base, N) ->
max(0, Base - N * ?GUESS_OFFSET).

0 comments on commit 8c4d0ef

Please sign in to comment.