File Coverage

blib/lib/Tail/Tool.pm
Criterion Covered Total %
statement 50 99 50.5
branch 5 32 15.6
condition 0 12 0.0
subroutine 12 14 85.7
pod 3 3 100.0
total 70 160 43.7


line stmt bran cond sub pod time code
1             package Tail::Tool;
2              
3             # Created on: 2010-10-06 14:15:40
4             # Create by: Ivan Wills
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 2     2   85047 use Moose;
  2         779557  
  2         14  
10 2     2   12956 use warnings;
  2         4  
  2         64  
11 2     2   1222 use version;
  2         3430  
  2         9  
12 2     2   138 use Carp;
  2         4  
  2         142  
13 2     2   997 use English qw/ -no_match_vars /;
  2         3855  
  2         9  
14 2     2   1486 use Tail::Tool::File;
  2         8  
  2         1021  
15              
16             our $VERSION = version->new('0.4.8');
17              
18             has files => (
19             is => 'rw',
20             isa => 'ArrayRef[Tail::Tool::File]',
21             default => sub {[]},
22             );
23             has lines => (
24             is => 'rw',
25             isa => 'Int',
26             default => 10,
27             );
28             has pre_process => (
29             is => 'rw',
30             isa => 'ArrayRef',
31             default => sub {[]},
32             trigger => \&_pre_process_set,
33             );
34             has post_process => (
35             is => 'rw',
36             isa => 'ArrayRef',
37             default => sub {[]},
38             );
39             has printer => (
40             is => 'rw',
41             isa => 'CodeRef',
42             predicate => 'has_printer',
43             #default => sub {
44             # sub { print "Default printer\n", ( ref $_ eq 'ARRAY' ? @$_ : @_ ) };
45             #},
46             );
47             has last => (
48             is => 'rw',
49             isa => 'Tail::Tool::File',
50             );
51              
52             around BUILDARGS => sub {
53             my ($orig, $class, @params) = @_;
54             my %param;
55              
56             if ( ref $params[0] eq 'HASH' ) {
57             %param = %{ shift @params };
58             }
59             else {
60             %param = @params;
61             }
62              
63             $param{pre_process} ||= [];
64             $param{post_process} ||= [];
65              
66             for my $key ( keys %param ) {
67             next if $key eq 'post_process' || $key eq 'pre_process';
68              
69             if ( $key eq 'files' ) {
70             my @extra = (
71             no_inotify => $param{no_inotify},
72             restart => $param{restart},
73             );
74             for my $file ( @{ $param{$key} } ) {
75             $file = Tail::Tool::File->new(
76             ref $file ? $file : ( name => $file, @extra )
77             );
78             }
79             }
80             elsif ( $key eq 'lines' || $key eq 'printer' || $key eq 'no_inotify' || $key eq 'restart' ) {
81             }
82             else {
83             my $plg = _new_plugin( $key, $param{$key} );
84             delete $param{$key};
85              
86             push @{ $param{ ( $plg->post ? 'post' : 'pre' ) . '_process' } }, $plg;
87             }
88             }
89              
90             return $class->$orig(%param);
91             };
92              
93             sub _new_plugin {
94 2     2   1203 my ( $name, $value ) = @_;
95 2         9 my $plugin = _load_plugin($name);
96              
97 2         14 my $plg = $plugin->new($value);
98              
99 2         963 return $plg;
100             }
101              
102             sub _load_plugin {
103 2     2   5 my ( $name ) = @_;
104 2 100       15 my $plugin
105             = $name =~ /^\+/
106             ? substr $name, 1, 999
107             : "Tail::Tool::Plugin::$name";
108 2         8 my $plugin_file = $plugin;
109 2         10 $plugin_file =~ s{::}{/}gxms;
110 2         5 $plugin_file .= '.pm';
111             {
112             # don't load twice
113 2     2   21 no strict qw/refs/; ## no critic
  2         4  
  2         1400  
  2         5  
114 2 50       3 if ( !${"Tail::Tool::Plugin::${name}::"}{VERSION} ) {
  2         31  
115 2         4 eval { require $plugin_file };
  2         542  
116 2 50       8 if ( $EVAL_ERROR ) {
117 0         0 confess "Could not load the plugin $name (via $plugin_file)\n";
118             }
119             }
120             }
121              
122 2         6 return $plugin;
123             }
124              
125             sub tail {
126 1     1 1 1390 my ( $self, $no_start ) = @_;
127              
128 1         2 for my $file (@{ $self->files }) {
  1         29  
129 0 0       0 next if $file->runner;
130 0     0   0 $file->runner( sub { $self->run(@_) } );
  0         0  
131 0         0 $file->tailer($self);
132 0         0 $file->watch();
133 0 0       0 $file->run() if !$no_start;
134             }
135             }
136              
137             sub run {
138 0     0 1 0 my ( $self, $file ) = @_;
139              
140 0         0 my $first = !$file->started;
141 0         0 my @lines = $file->get_line;
142              
143 0 0 0     0 if ( $first && @lines > $self->lines ) {
144 0         0 @lines = @lines[ -$self->lines .. -1 ];
145             }
146              
147 0         0 for my $pre ( @{ $self->pre_process } ) {
  0         0  
148 0         0 my @new;
149 0 0 0     0 if (@lines) {
    0          
150 0         0 for my $line (@lines) {
151 0         0 push @new, $pre->process($line, $file);
152             }
153             }
154             elsif ( $pre->can('allow_empty') && $pre->allow_empty ) {
155 0         0 push @new, $pre->process('', $file);
156             }
157 0         0 @lines = @new;
158             }
159 0         0 for my $post ( @{ $self->post_process } ) {
  0         0  
160 0         0 my @new;
161 0         0 for my $line (@lines) {
162 0         0 push @new, $post->process($line, $file);
163             }
164 0         0 @lines = @new;
165             }
166              
167 0 0       0 if ( @lines ) {
168 0 0 0     0 if ( @{ $self->files } > 1 && ( !$self->last || $file ne $self->last ) ) {
  0   0     0  
169 0         0 unshift @lines, "\n==> " . $file->name . " <==\n";
170             }
171 0         0 $self->last($file);
172             }
173              
174             #warn join "", @lines if @lines;
175 0 0       0 if ( $self->has_printer ) {
176 0         0 my $printer = $self->printer;
177 0         0 warn "Lines = " . scalar @lines, "\tPrinter " . $printer . "\n";
178              
179 0         0 $_ = \@lines;
180 0         0 eval { &{$printer}() };
  0         0  
  0         0  
181 0 0       0 warn "Error in printer: " . $@ if $@;
182             }
183             else {
184 0         0 $self->default_printer(@lines);
185             }
186              
187 0 0       0 $file->started(1) if $first;
188 0         0 return;
189             }
190              
191             sub default_printer {
192 1     1 1 1528 my ( $self, @lines ) = @_;
193 1         55 print @lines;
194             }
195              
196             sub _pre_process_set {
197 1     1   541 my ($self, $pre_process) = @_;
198 1         2 my @pre = @{ $pre_process };
  1         3  
199 1         2 my @group;
200             my @other;
201              
202             # sort (in order) pre process plugins
203 1         3 for my $pre (@pre) {
204 0 0       0 if ( ref $pre eq 'Tail::Tool::Plugin::GroupLines' ) {
205 0         0 push @group, $pre;
206             }
207             else {
208 0         0 push @other, $pre;
209             }
210             }
211              
212             # check that the sorted plugins match the current order
213 1         2 my $differ = 0;
214 1         4 for my $new_pre ( @group, @other ) {
215 0 0       0 if ( $new_pre != shift @pre ) {
216 0         0 $differ = 1;
217 0         0 last;
218             }
219             }
220              
221             # if the orders differ, reset the plugins.
222 1 50       6 if ($differ) {
223 0           $self->pre_process([ @group, @other ]);
224             }
225             }
226              
227             1;
228              
229             __END__
230              
231             =head1 NAME
232              
233             Tail::Tool - Tool for sophisticated tailing of files
234              
235             =head1 VERSION
236              
237             This documentation refers to Tail::Tool version 0.4.8.
238              
239             =head1 SYNOPSIS
240              
241             use Tail::Tool;
242              
243             # Create a new Tail::Tool object tailing /tmp/test.log
244             # with the spacing plugin initialised.
245             my $tt = Tail::Tool->new(
246             files => [
247             '/tmpl/test.log',
248             ],
249             Spacing => {
250             short_time => 2,
251             short_lines => 2,
252             long_time => 5,
253             long_lines => 10,
254             },
255             ...
256             );
257              
258             # run the tail
259             $tt->tail();
260              
261             =head1 DESCRIPTION
262              
263             =head1 SUBROUTINES/METHODS
264              
265             =head2 C<tail ()>
266              
267             Description: Start tailing?
268              
269             =head2 C<run ($file, $first)>
270              
271             Param: C<$file> - Tail::Tool::File - The file to run
272              
273             Param: C<$first> - bool - Specifies that this is the first time run has been
274             called.
275              
276             =head2 C<run ( $file )>
277              
278             Runs the the tailing of C<$file>.
279              
280             =head2 C<default_printer ( @lines )>
281              
282             Prints C<@lines> to STDOUT
283              
284             =head1 DIAGNOSTICS
285              
286             =head1 CONFIGURATION AND ENVIRONMENT
287              
288             =head1 DEPENDENCIES
289              
290             =head1 INCOMPATIBILITIES
291              
292             =head1 BUGS AND LIMITATIONS
293              
294             There are no known bugs in this module.
295              
296             Please report problems to Ivan Wills (ivan.wills@gmail.com).
297              
298             Patches are welcome.
299              
300             =head1 AUTHOR
301              
302             Ivan Wills - (ivan.wills@gmail.com)
303              
304             =head1 LICENSE AND COPYRIGHT
305              
306             Copyright (c) 2010 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW, Australia).
307             All rights reserved.
308              
309             This module is free software; you can redistribute it and/or modify it under
310             the same terms as Perl itself. See L<perlartistic>. This program is
311             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
312             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
313             PARTICULAR PURPOSE.
314              
315             =cut