| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package App::PerlWatcher::Watcher::FileTail; |
|
2
|
|
|
|
|
|
|
{ |
|
3
|
|
|
|
|
|
|
$App::PerlWatcher::Watcher::FileTail::VERSION = '0.18'; |
|
4
|
|
|
|
|
|
|
} |
|
5
|
|
|
|
|
|
|
# ABSTRACT: Watches for changes file and outputs new added lines (a-la 'tail -f') |
|
6
|
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
286116
|
use 5.12.0; |
|
|
2
|
|
|
|
|
7
|
|
|
|
2
|
|
|
|
|
86
|
|
|
8
|
2
|
|
|
2
|
|
11
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
51
|
|
|
9
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
|
2
|
|
|
|
|
8
|
|
|
|
2
|
|
|
|
|
61
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
2
|
|
|
2
|
|
10
|
use Carp; |
|
|
2
|
|
|
|
|
8
|
|
|
|
2
|
|
|
|
|
134
|
|
|
12
|
2
|
|
|
2
|
|
9
|
use Devel::Comments; |
|
|
2
|
|
|
|
|
9
|
|
|
|
2
|
|
|
|
|
19
|
|
|
13
|
2
|
|
|
2
|
|
8387
|
use File::ReadBackwards; |
|
|
2
|
|
|
|
|
4016
|
|
|
|
2
|
|
|
|
|
81
|
|
|
14
|
2
|
|
|
2
|
|
4090
|
use Linux::Inotify2; |
|
|
2
|
|
|
|
|
8817
|
|
|
|
2
|
|
|
|
|
400
|
|
|
15
|
2
|
|
|
2
|
|
2788
|
use Moo; |
|
|
2
|
|
|
|
|
46595
|
|
|
|
2
|
|
|
|
|
13
|
|
|
16
|
2
|
|
|
2
|
|
7269
|
use aliased 'Path::Class::File'; |
|
|
2
|
|
|
|
|
1975
|
|
|
|
2
|
|
|
|
|
12
|
|
|
17
|
|
|
|
|
|
|
|
|
18
|
2
|
|
|
2
|
|
77636
|
use AnyEvent::Handle; |
|
|
2
|
|
|
|
|
54791
|
|
|
|
2
|
|
|
|
|
102
|
|
|
19
|
2
|
|
|
2
|
|
4757
|
use App::PerlWatcher::EventItem; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
use App::PerlWatcher::Levels; |
|
21
|
|
|
|
|
|
|
use aliased 'App::PerlWatcher::Status'; |
|
22
|
|
|
|
|
|
|
use App::PerlWatcher::Watcher; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
has 'file' => ( is => 'ro', required => 1); |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
has 'lines_number' => ( is => 'ro', required => 1); |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
has 'filter' => ( is => 'ro', default => sub { return sub {1; } } ); |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
has 'inotify' => ( is => 'lazy' ); |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
has 'events' => ( is => 'lazy', default => sub { [] } ); |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
has 'reverse' => ( is => 'lazy', default => sub{ 0 }); |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
with qw/App::PerlWatcher::Watcher/; |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub _build_inotify { |
|
47
|
|
|
|
|
|
|
my $inotify = Linux::Inotify2->new |
|
48
|
|
|
|
|
|
|
or croak("unable to create new inotify object: $!"); |
|
49
|
|
|
|
|
|
|
return $inotify; |
|
50
|
|
|
|
|
|
|
} |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub build_watcher_guard { |
|
53
|
|
|
|
|
|
|
my $self = shift; |
|
54
|
|
|
|
|
|
|
return AnyEvent->io( |
|
55
|
|
|
|
|
|
|
fh => $self->inotify->fileno, |
|
56
|
|
|
|
|
|
|
poll => 'r', |
|
57
|
|
|
|
|
|
|
cb => sub { |
|
58
|
|
|
|
|
|
|
$self->inotify->poll |
|
59
|
|
|
|
|
|
|
if $self->active; |
|
60
|
|
|
|
|
|
|
}, |
|
61
|
|
|
|
|
|
|
); |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub start { |
|
65
|
|
|
|
|
|
|
my ($self, $callback) = @_; |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
return unless($self->active); |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
my $fail_start = sub { |
|
70
|
|
|
|
|
|
|
my $msg = shift; |
|
71
|
|
|
|
|
|
|
$self->poll_callback->($self); |
|
72
|
|
|
|
|
|
|
$self->callback->( |
|
73
|
|
|
|
|
|
|
Status->new( |
|
74
|
|
|
|
|
|
|
watcher => $self, |
|
75
|
|
|
|
|
|
|
level => LEVEL_ANY, |
|
76
|
|
|
|
|
|
|
description => sub { $self->description . " : $msg " }, |
|
77
|
|
|
|
|
|
|
) |
|
78
|
|
|
|
|
|
|
); |
|
79
|
|
|
|
|
|
|
}; |
|
80
|
|
|
|
|
|
|
my $path = File->new($self->file); |
|
81
|
|
|
|
|
|
|
return $fail_start->($!) unless $path->open('r'); |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
eval { |
|
84
|
|
|
|
|
|
|
$self->_try_start; |
|
85
|
|
|
|
|
|
|
$self->watcher_guard( $self->build_watcher_guard ); |
|
86
|
|
|
|
|
|
|
}; |
|
87
|
|
|
|
|
|
|
$fail_start->($@) if($@); |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub _try_start { |
|
91
|
|
|
|
|
|
|
my $self = shift; |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
my $file_handle = $self->_initial_read; |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
$self->inotify->watch( |
|
96
|
|
|
|
|
|
|
$self->file, |
|
97
|
|
|
|
|
|
|
IN_MODIFY, |
|
98
|
|
|
|
|
|
|
sub { |
|
99
|
|
|
|
|
|
|
my $e = shift; |
|
100
|
|
|
|
|
|
|
my $name = $e->fullname; |
|
101
|
|
|
|
|
|
|
# cancel this watcher: remove no further events |
|
102
|
|
|
|
|
|
|
#$e->w->cancel; |
|
103
|
|
|
|
|
|
|
my $ae_handle; |
|
104
|
|
|
|
|
|
|
$ae_handle = AnyEvent::Handle->new( |
|
105
|
|
|
|
|
|
|
fh => $file_handle, |
|
106
|
|
|
|
|
|
|
on_read => sub { |
|
107
|
|
|
|
|
|
|
my ($ea_handle) = @_; |
|
108
|
|
|
|
|
|
|
$ea_handle->push_read( |
|
109
|
|
|
|
|
|
|
line => sub { |
|
110
|
|
|
|
|
|
|
my ( $ea_handle, $line, $eof ) = @_; |
|
111
|
|
|
|
|
|
|
#print $line, $eof; |
|
112
|
|
|
|
|
|
|
$self->_add_line($line); |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
); |
|
115
|
|
|
|
|
|
|
}, |
|
116
|
|
|
|
|
|
|
on_eof => sub { |
|
117
|
|
|
|
|
|
|
# eof |
|
118
|
|
|
|
|
|
|
undef $ae_handle; |
|
119
|
|
|
|
|
|
|
}, |
|
120
|
|
|
|
|
|
|
); |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
); |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub description { |
|
126
|
|
|
|
|
|
|
my $self = shift; |
|
127
|
|
|
|
|
|
|
return "FileWatcher [" . $self->file . "]"; |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub _add_item { |
|
131
|
|
|
|
|
|
|
my ($self, $item) = @_; |
|
132
|
|
|
|
|
|
|
my $events = $self->events; |
|
133
|
|
|
|
|
|
|
if (! $self->reverse) { |
|
134
|
|
|
|
|
|
|
push @$events, $item; |
|
135
|
|
|
|
|
|
|
shift @$events if @$events > $self->lines_number; |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
else { |
|
138
|
|
|
|
|
|
|
unshift @$events, $item; |
|
139
|
|
|
|
|
|
|
pop @$events if @$events > $self->lines_number; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub _add_line { |
|
144
|
|
|
|
|
|
|
my ( $self, $line ) = @_; |
|
145
|
|
|
|
|
|
|
if ( defined $line ) { |
|
146
|
|
|
|
|
|
|
chomp $line; |
|
147
|
|
|
|
|
|
|
if ( $self->filter->(local $_ = $line) ) { |
|
148
|
|
|
|
|
|
|
my $event_item = App::PerlWatcher::EventItem->new( |
|
149
|
|
|
|
|
|
|
content => $line, |
|
150
|
|
|
|
|
|
|
timestamp => 0, |
|
151
|
|
|
|
|
|
|
); |
|
152
|
|
|
|
|
|
|
$self->_add_item($event_item); |
|
153
|
|
|
|
|
|
|
$self->_trigger_callback; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub _trigger_callback { |
|
159
|
|
|
|
|
|
|
my ($self) = @_; |
|
160
|
|
|
|
|
|
|
my @events = @{ $self->events }; |
|
161
|
|
|
|
|
|
|
my $status = Status->new( |
|
162
|
|
|
|
|
|
|
watcher => $self, |
|
163
|
|
|
|
|
|
|
level => LEVEL_NOTICE, |
|
164
|
|
|
|
|
|
|
description => sub { $self->description }, |
|
165
|
|
|
|
|
|
|
items => sub { \@events }, |
|
166
|
|
|
|
|
|
|
); |
|
167
|
|
|
|
|
|
|
$self->poll_callback->($self); |
|
168
|
|
|
|
|
|
|
$self->callback->($status); |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub _initial_read { |
|
172
|
|
|
|
|
|
|
my ($self) = @_; |
|
173
|
|
|
|
|
|
|
my $frb = File::ReadBackwards->new( $self->file ); |
|
174
|
|
|
|
|
|
|
my $end_position = $frb->tell; |
|
175
|
|
|
|
|
|
|
my @last_lines; |
|
176
|
|
|
|
|
|
|
my $line; |
|
177
|
|
|
|
|
|
|
do { |
|
178
|
|
|
|
|
|
|
$line = $frb->readline; |
|
179
|
|
|
|
|
|
|
unshift @last_lines, $line |
|
180
|
|
|
|
|
|
|
if ( $line && $self->filter->(local $_ = $line) ); |
|
181
|
|
|
|
|
|
|
} while (defined($line) && @last_lines < $self->lines_number ); |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
$self->_add_line($_) for (@last_lines); |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
my $file_handle = $frb->get_handle; |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# move file pointer to the end |
|
188
|
|
|
|
|
|
|
seek $file_handle, 0, 2; |
|
189
|
|
|
|
|
|
|
return $file_handle; |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
1; |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
__END__ |