1+ -module (rabbit_log_tail ).
2+
3+ -export ([tail_n_lines /2 ]).
4+ % -export([init_tail_stream/2, tail_send/2, tail_receive/2]).
5+
6+ -define (GUESS_OFFSET , 200 ).
7+
8+ tail_n_lines (Filename , N ) ->
9+ case file :open (Filename , [read , binary ]) of
10+ {ok , File } ->
11+ {ok , Eof } = file :position (File , eof ),
12+ % % Eof may move. Only read up to the current one.
13+ Result = reverse_read_n_lines (N , N , File , Eof , Eof ),
14+ file :close (File ),
15+ Result ;
16+ Error -> Error
17+ end .
18+
19+ reverse_read_n_lines (N , OffsetN , File , Position , Eof ) ->
20+ GuessPosition = offset (Position , OffsetN ),
21+ case read_lines_from_position (File , GuessPosition , Eof ) of
22+ {ok , Lines } ->
23+ NLines = length (Lines ),
24+ case {NLines >= N , GuessPosition == 0 } of
25+ % % Take only N lines if there is more
26+ {true , _ } -> lists :nthtail (NLines - N , Lines );
27+ % % Safe to assume that NLines is less then N
28+ {_ , true } -> Lines ;
29+ % % Adjust position
30+ _ ->
31+ reverse_read_n_lines (N , N - NLines + 1 , File , GuessPosition , Eof )
32+ end ;
33+ Error -> Error
34+ end .
35+
36+ read_from_position (File , GuessPosition , Eof ) ->
37+ file :pread (File , GuessPosition , max (0 , Eof - GuessPosition )).
38+
39+ read_lines_from_position (File , GuessPosition , Eof ) ->
40+ case read_from_position (File , GuessPosition , Eof ) of
41+ {ok , Data } -> {ok , crop_lines (Data )};
42+ Error -> Error
43+ end .
44+
45+ crop_lines (Data ) ->
46+ % % Drop the first line, because it's most likely partial.
47+ case binary :split (Data , <<" \n " >>, [global , trim ]) of
48+ [_ |Rest ] -> Rest ;
49+ [] -> []
50+ end .
51+
52+ offset (Base , N ) ->
53+ max (0 , Base - N * ? GUESS_OFFSET ).
0 commit comments