File Coverage

blib/lib/App/RabbitTail/FileTailer.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package App::RabbitTail::FileTailer;
2 1     1   1162 use Moose;
  0            
  0            
3             use AnyEvent;
4             use MooseX::Types::Moose qw/CodeRef Num/;
5             use MooseX::Types::Path::Class qw/File/;
6             use Coro::Handle;
7             use namespace::autoclean;
8              
9             has fn => (
10             isa => File,
11             is => 'ro',
12             required => 1,
13             coerce => 1,
14             );
15              
16             has fh => (
17             is => 'ro',
18             lazy => 1,
19             default => sub {
20             my $fh = shift->fn->openr;
21             seek $fh, 0, 2;
22             $fh;
23             },
24             );
25              
26             has cb => (
27             isa => CodeRef,
28             is => 'ro',
29             required => 1,
30             );
31              
32             has _sleep_interval => (
33             isa => Num,
34             is => 'rw',
35             default => 0,
36             init_arg => undef,
37             );
38              
39             has _next_backoff => (
40             isa => Num,
41             is => 'rw',
42             clearer => '_clear_next_backoff',
43             predicate => '_has_next_backoff',
44             init_arg => undef,
45             );
46              
47             has backoff_increment => (
48             isa => Num,
49             is => 'ro',
50             default => 0.1,
51             );
52              
53             has max_sleep => (
54             isa => Num,
55             is => 'ro',
56             default => 10,
57             );
58              
59             has _watcher => (
60             is => 'rw'
61             );
62              
63             sub tail {
64             my ($self) = @_;
65             $self->_watcher(AnyEvent->timer(
66             after => $self->_sleep_interval,
67             cb => sub {
68             if ( !$self->_read_one_line ) {
69             if (!$self->_has_next_backoff) {
70             $self->_next_backoff($self->backoff_increment);
71             }
72             $self->_sleep_interval($self->_sleep_interval + $self->_next_backoff);
73             if ($self->_sleep_interval > $self->max_sleep) {
74             $self->_sleep_interval($self->max_sleep);
75             $self->_next_backoff(0);
76             }
77             elsif ($self->_sleep_interval < $self->max_sleep) {
78             $self->_next_backoff( $self->_next_backoff * 2 );
79             }
80             }
81             $self->tail;
82             },
83             ));
84             }
85              
86             sub _read_one_line {
87             my $self = shift;
88             my $line = unblock($self->fh)->readline;
89             return if !defined $line;
90             chomp($line);
91             $self->_sleep_interval(0);
92             $self->_clear_next_backoff;
93             $self->cb->($line) if length $line;
94             return $line;
95             }
96              
97             __PACKAGE__->meta->make_immutable;
98             __END__
99              
100             =head1 NAME
101              
102             App::RabbitTail::FileTailer - responsible for tailing a file and invoking a callback for each line.
103              
104             =head1 SYNOPSIS
105              
106             use App::RabbitTail::FileTailer;
107             use AnyEvent;
108              
109             my $tailer = App::RabbitTail::FileTailer->new(
110             backoff_increment => 0.1,
111             max_sleep => 10,
112             fn => $somefile,
113             cd => sub { warn("Got line " . $_[0]) },
114             );
115             $tailer->tail; # Sets up watcher to fire callbacks, returns
116              
117             # Rest of your code.
118              
119             # Enter event loop.
120             AnyEvent->condvar->recv;
121              
122             =head1 DESCRIPTION
123              
124             An instance of App::RabbitTail::FileTailer manages tailing a file with exponential backoff
125             of checking if the file has been written when no bytes are available to minimise system load.
126              
127             =head1 AUTHOR, COPYRIGHT AND LICENSE
128              
129             See L<App::RabbitTail> for copyright and license.
130              
131             =cut