File Coverage

blib/lib/Logfile/Tail/Switch.pm
Criterion Covered Total %
statement 50 51 98.0
branch 19 22 86.3
condition 11 12 91.6
subroutine 8 8 100.0
pod 2 2 100.0
total 90 95 94.7


line stmt bran cond sub pod time code
1             package Logfile::Tail::Switch;
2              
3             our $DATE = '2017-06-09'; # DATE
4             our $VERSION = '0.002'; # VERSION
5              
6 1     1   34258 use 5.010001;
  1         4  
7 1     1   5 use strict;
  1         2  
  1         17  
8 1     1   5 use warnings;
  1         2  
  1         29  
9              
10 1     1   402 use Time::HiRes 'time';
  1         1164  
  1         5  
11              
12             sub new {
13 2     2 1 3390 my ($class, $glob, $opts) = @_;
14              
15 2 50       7 defined($glob) or die "Please specify glob";
16 2   100     12 $opts //= {};
17              
18 2   100     10 $opts->{check_freq} //= 5;
19              
20 2         8 my $self = {
21             glob => $glob,
22             opts => $opts,
23             _cur_file => undef,
24             _cur_fh => undef,
25             _pending => {},
26             };
27              
28 2         7 bless $self, $class;
29             }
30              
31             sub _switch {
32 3     3   12 my ($self, $filename, $seek_end) = @_;
33              
34             #say "D: opening $filename";
35 3         8 $self->{_cur_file} = $filename;
36 3 50       87 open my $fh, "<", $filename or die "Can't open $filename: $!";
37 3 100       13 seek $fh, 0, 2 if $seek_end;
38 3         11 $self->{_cur_fh} = $fh;
39             }
40              
41             sub _getline {
42 16     16   30 my $self = shift;
43              
44 16         28 my $fh = $self->{_cur_fh};
45 16         110 my $size = -s $fh;
46 16         37 my $pos = tell $fh;
47 16 100       48 if ($pos == $size) {
    50          
48             # we are still at the end of file, return empty string
49 9         24 return '';
50             } elsif ($pos > $size) {
51             # file reduced in size, it probably means it has been rotated, start
52             # from the beginning
53 0         0 seek $fh, 0, 0;
54             } else {
55             # there are new content to read after our position
56             }
57 7   50     58 return(<$fh> // '');
58             }
59              
60             sub getline {
61 15     15 1 41 my $self = shift;
62              
63 15         49 my $now = time();
64              
65             CHECK_NEWER_FILES:
66             {
67 15         29 last if $self->{_last_check_time} &&
68 15 100 100     109 $self->{_last_check_time} >= $now - $self->{opts}{check_freq};
69             #say "D: checking for newer file";
70 3         286 my @files = sort glob($self->{glob});
71             #say "D: files matching glob: ".join(", ", @files);
72 3         14 $self->{_last_check_time} = $now;
73 3 100       11 last unless @files;
74 2 100       10 if (defined $self->{_cur_fh}) {
75 1         49 for (@files) {
76             # there is a newer file than the current one, add to the pending
77             # list of files to be read after the current one
78             #say "D: there is a newer file: $_";
79             $self->{_pending}{$_} = 1
80 4 100       28 if $_ gt $self->{_cur_file};
81             }
82             } else {
83             # this is our first time, pick the newest file in the pattern and
84             # tail it.
85 1         5 $self->_switch($files[-1], 1);
86             }
87             }
88              
89             # we don't have any matching files
90 15 100       50 return '' unless $self->{_cur_fh};
91              
92 14         31 my $line = $self->_getline;
93 14 100 100     51 if (!length($line) && keys %{$self->{_pending}}) {
  9         44  
94             # switch to a newer named file
95 2         4 my @files = sort keys %{$self->{_pending}};
  2         12  
96 2         11 $self->_switch($files[0]);
97 2         22 delete $self->{_pending}{$files[0]};
98 2         6 $line = $self->_getline;
99             }
100 14         69 $line;
101             }
102              
103             1;
104             # ABSTRACT: Tail a file, but switch when another file with newer name appears
105              
106             __END__