File-Tail

 view release on metacpan or  search on metacpan

Tail.pm.debug  view on Meta::CPAN

	$mustreturn=time()+$timeout;
    } else {
	$minpred=$fds[0]->predict;
    }
    foreach (@fds) {
	my $val=$_->predict;
	$object->logit("minpred calc 1 ($minpred,$val)");
	$minpred=$val if $minpred>$val;
    }
    my ($nfound,$timeleft);
    my @retarr;
    while (defined($timeout)?(!$nfound && (time()<$mustreturn)):!$nfound) {
# Restore bitmaps in case we called select before
	splice(@_,0,3,$savein,$saveout,$saveerr);

	$object->logit("calling select(".bitprint($_[0]).",".bitprint($_[1]).",".bitprint($_[2]).",$minpred)\n");

	($nfound,$timeleft)=select($_[0],$_[1],$_[2],$minpred);

	$object->logit("returning ($nfound,$timeleft)=select(".bitprint($_[0]).",".bitprint($_[1]).",".bitprint($_[2]).",$minpred)\n");

	if (defined($timeout)) {
	    $minpred=$timeout;
	} else {
	    $minpred=$fds[0]->predict;
	}
	undef @retarr;
	foreach (@fds) {
	    my $val=$_->predict;
	    $nfound++ unless $val;
	    $object->logit("minpred calc 2 ($minpred,$val)");
	    $minpred=$val if $minpred>$val;
	    push(@retarr,$_) unless $val;
	}
    }
    if (wantarray) {
	return ($nfound,$timeleft,@retarr);
    } else {
	return $nfound;
    }
}

sub readin {
    my $crs;
    my ($object,$len)=@_;
    logit($object,"Readin, len=$len");
    if (length($object->{"buffer"})) {
	# this means the file was reset AND a tail -n was active
	$crs=$object->{"buffer"}=~tr/\n//; # Count newlines in buffer 
	logit($object,"buffer already contains $crs lines ",length($object->{"buffer"})," bytes");
	return $crs if $crs;
    }
    $len=$object->{"maxbuf"} if ($len>$object->{"maxbuf"});
    logit($object,"Preparing to read in $len bytes");
    my $nlen=$len;
    while ($nlen>0) {
	$len=sysread($object->{handle},$object->{"buffer"},
		     $nlen,length($object->{"buffer"}));
        $object->{"buffer"} =~ s/\015\012/\n/g if $Is_Win32;

	last if $len==0; # Some busy filesystems return 0 sometimes, 
                             # and never give anything more from then on if 
                             # you don't give them time to rest. This return 
                             # allows File::Tail to use the usual exponential 
                             # backoff.
	$nlen=$nlen-$len;
    }
    $object->{curpos}=sysseek($object->{handle},0,SEEK_CUR);
    
    $crs=$object->{"buffer"}=~tr/\n//;
    $object->logit(" Got something ($len). there are now $crs newlines in buffer (".length($object->{"buffer"})." bytes)");
    
    if ($crs) {
	my $tmp=time;
	$object->{lastread}=$tmp if $object->{lastread}>$tmp; #???
	$object->interval(($tmp-($object->{lastread}))/$crs);
	$object->{lastread}=$tmp;
    }
    return ($crs);
}

sub read {
    my $object=shift @_;
    my $len;
    my $pending=$object->{"endpos"}-$object->{"curpos"};
    $object->logit("- $pending bytes unread in file (endpos:$object->{endpos}- curpos:$object->{curpos})");
    my $crs=$object->{"buffer"}=~m/\n/;
    while (!$pending && !$crs) {
	$object->logit(" Reading loop entered");
	$object->{"sleepcount"}=0;
	while ($object->predict) {
	    if ($object->nowait) {
		if (wantarray) {
		    return ();
		} else {
		    return "";
		}
	    }
	    sleep($object->interval) if ($object->interval>0);
	}
	$pending=$object->{"endpos"}-$object->{"curpos"};
	$crs=$object->{"buffer"}=~m/\n/;
    }
    
    if (!length($object->{"buffer"}) || index($object->{"buffer"},"\n")<0) {
	logit($object,"reading loop finished. ($pending pending) Calling readin");
	readin($object,$pending);
    }
    logit($object,"Buffer now contains ",length($object->{"buffer"})," bytes");
    unless (wantarray) {
	my $str=substr($object->{"buffer"},0,
		       1+index($object->{"buffer"},"\n"));
	$object->{"buffer"}=substr($object->{"buffer"},
				   1+index($object->{"buffer"},"\n"));
	return $str;
    } else {
	my @str;
	while (index($object->{"buffer"},"\n")>-1) {
	    push(@str,substr($object->{"buffer"},0,
			     1+index($object->{"buffer"},"\n")));
	    $object->{"buffer"}=substr($object->{"buffer"},
				       1+index($object->{"buffer"},"\n"));

	}
	return @str;
    }
}

1;

__END__

=head1 NAME

File::Tail - Perl extension for reading from continously updated files

=head1 SYNOPSIS

  use File::Tail;
  $file=File::Tail->new("/some/log/file");
  while (defined($line=$file->read)) {
      print "$line";
  }

  use File::Tail;
  $file=File::Tail->new(name=>$name, maxinterval=>300, adjustafter=>7);
  while (defined($line=$file->read)) {
      print "$line";
  }

OR, you could use tie (additional parameters can be passed with the name, or 
can be set using $ref):

    use File::Tail;
    my $ref=tie *FH,"File::Tail",(name=>$name);
    while (<FH>) {
        print "$_";
    }


Note that the above script will never exit. If there is nothing being written
to the file, it will simply block.

You can find more synopsii in the file logwatch, which is included
in the distribution.

Note: Select functionality was added in version 0.9, and it required 
some reworking of all routines. ***PLEASE*** let me know if you see anything
strange happening. 

You can find two way of using select in the file select_demo which is included
in the ditribution.

=head1 DESCRIPTION

The primary purpose of File::Tail is reading and analysing log files while
they are being written, which is especialy usefull if you are monitoring
the logging process with a tool like Tobias Oetiker's MRTG.

The module tries very hard NOT to "busy-wait" on a file that has little 
traffic. Any time it reads new data from the file, it counts the number
of new lines, and divides that number by the time that passed since data
were last written to the file before that. That is considered the average
time before new data will be written. When there is no new data to read, 
C<File::Tail> sleeps for that number of seconds. Thereafter, the waiting 
time is recomputed dynamicaly. Note that C<File::Tail> never sleeps for
more than the number of seconds set by C<maxinterval>.

If the file does not get altered for a while, C<File::Tail> gets suspicious 
and startschecking if the file was truncated, or moved and recreated. If 
anything like that had happened, C<File::Tail> will quietly reopen the file,
and continue reading. The only way to affect what happens on reopen is by 
setting the reset_tail parameter (see below). The effect of this is that
the scripts need not be aware when the logfiles were rotated, they will
just quietly work on.

Note that the sleep and time used are from Time::HiRes, so this module
should do the right thing even if the time to sleep is less than one second.

The logwatch script (also included) demonstrates several ways of calling 
the methods.

=head1 CONSTRUCTOR

=head2 new ([ ARGS ]) 

Creates a C<File::Tail>. If it has only one paramter, it is assumed to 
be the filename. If the open fails, the module performs a croak. I
am currently looking for a way to set $! and return undef. 

You can pass several parameters to new:

=over 4

=item name

This is the name of the file to open. The file will be opened for reading.
This must be a regular file, not a pipe or a terminal (i.e. it must be
seekable).

=item maxinterval

The maximum number of seconds (real number) that will be spent sleeping.
Default is 60, meaning C<File::Tail> will never spend more than sixty
seconds without checking the file.

=item interval

The initial number of seconds (real number) that will be spent sleeping,
before the file is first checked. Default is ten seconds, meaning C<File::Tail>
will sleep for 10 seconds and then determine, how many new lines have appeared 
in the file.

=item adjustafter

The number of C<times> C<File::Tail> waits for the current interval,
before adjusting the interval upwards. The default is 10.

=item resetafter



( run in 2.137 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )