Monthly Archives: December 2016

File::Tail and name_changes

I recently wrote a script using File::Tail and name_changes. I started out with the project page: https://metacpan.org/pod/File::Tail

The instructions here don’t explain how to use File::Tail so I googled and found lots of advice. Everyone agrees that the first step is to create a code reference, a variable that points to a block of code. There is less agreement on the correct way to reference the code. Here’s how I setup a variable pointing to code that selects the newest file matching “weblog”:

Code:

my $log_location = sub {
 my $inputdir = '/usr/local/netscaler/logs';
 opendir INPUTDIR, $inputdir or die "$0 : $!\n";
 my @dirlines = grep { $_ =~ /weblog/} readdir INPUTDIR;
 closedir INPUTDIR;
 #print "dirlines = @dirlines\n";
 print "0 = $dirlines[0]\n";
 my @sorted_dirlines = rev_nsort_by { stat("/usr/local/netscaler/logs/$_")->mtime } @dirlines;
 my $logfile = $sorted_dirlines[0];
 print "logfile = $logfile\n";
 $logfile = '/usr/local/netscaler/logs/' . $logfile;
 return $logfile;
};

The first site I read said to use &$log_location. I tried that but got an error “Can’t use string (“/usr/local/netscaler/logs/globe_”) as a subroutine ref while “strict refs” in use”

Code (incorrect):

my $file=File::Tail->new(
 name => &$log_location,
 resetafter => 60,
 interval => 1,
 tail => 0,
 name_changes => &$log_location)||warn $!;

The next advice was to use $log_location->() so I tried that but got the same error:

Code (incorrect):

my $file=File::Tail->new(
 name => $log_location->(),
 resetafter => 60,
 interval => 1,
 tail => 0,
 name_changes => $log_location->())||warn $!;

Eventually I asked on #perl (freenode) and got the correct answer. The name has to be a string, but name_changes has to be a code reference. &$log_location and $log_location->() both call the code reference and return the string, so they both work in “name” but “name_changes” must be just the code reference $log_location. This is the correct code:

Code:

my $file=File::Tail->new(
 name => $log_location->(),
 resetafter => 60,
 interval => 1,
 tail => 0,
 name_changes => $log_location)||warn $!;

The script tails a log continuously through log rotation. Here is the working version:

Code:

#!/usr/bin/perl
use strict;
use warnings;
use File::Tail;
use List::UtilsBy qw( rev_nsort_by );
use File::stat qw( stat );

my $log_location = sub {
 my $inputdir = '/usr/local/netscaler/logs';
 opendir INPUTDIR, $inputdir or die "$0 : $!\n";
 my @dirlines = grep { $_ =~ /weblog/} readdir INPUTDIR;
 closedir INPUTDIR;
 #print "dirlines = @dirlines\n";
 print "0 = $dirlines[0]\n";
 my @sorted_dirlines = rev_nsort_by { stat("/usr/local/netscaler/logs/$_")->mtime } @dirlines;
 my $logfile = $sorted_dirlines[0];
 print "logfile = $logfile\n";
 $logfile = '/usr/local/netscaler/logs/' . $logfile;
 return $logfile;
};

my $file=File::Tail->new(
 name => $log_location->(),
 resetafter => 60,
 interval => 1,
 tail => 0,
 name_changes => $log_location)||warn $!;

while (defined(my $line=$file->read))
{
 print "$line";
}