Print override

Leon Fayer
homo technologicus’ asylum
2 min readFeb 23, 2010

I came across this problem when I decided to integrate Growl notification support for my Perl script. Instead of using a standard custom log() function, I wanted to override the print function to pipe the output to the media of my liking. Unfortunately, print is a reserved keyword in Perl which cannot be overridden. Fortunately, it's Perl - so there is no such thing as impossible.

I wrote the OmniLogger module, to allow the script to automatically pipe STDOUT and/or STDERR to any media of choice. It's short, very light-weight, and can be used as is, or sub-classed, for any number of different outputs (files, Growl, databases, etc).

package OmniLogger;
use Symbol;
sub import {
my $self = shift;
foreach (@_) {
if ($_ eq 'stdout') {
*STDOUT = $self->new;
} elsif ($_ eq 'stderr') {
*STDERR = $self->new;
}
}
$self->init(@_);
}
sub new {
my $self = shift;
my $sym = gensym;
tie *$sym, $self;
bless $sym, $self;
}
sub TIEHANDLE { bless {}, shift }sub PRINT { shift->log(shift)}sub init { return; }sub log { return; }sub DESTROY { return; }1;

That’s it. log() function is for implementing logging mechanics, and init() and DESTROY() are for methods that require constructor/destructor actions.
Let’s assume, for the example sake, that the goal is to print “Hello World!” to the file. First, let’s subclass Logger as OmniLogger::File and define the output.

package OmniLogger::File;
use base ‘OmniLogger’;
use Filehandle;
my $_fh;
sub init {
my $self = shift;
$_fh = FileHandle->new(“/var/tmp/logger.log”,”a+”);
}
sub log {
my $self = shift;
my $msg = shift;
if (defined $_fh) {
$_fh->printf(“%s”,”$msg\n”);
}
}
sub DESTROY { $_fh->close; }
1;

And now, let’s use it. The module allows two types of usage: standard function call and the full override of outputs.

Standard Use#!/usr/bin/perl
use OmniLogger::File;
my $logger = OmniLogger::File->new;
$logger->log("Hello World!");
Global Override#!/usr/bin/perl
use OmniLogger::File qw(stdout);
print "Hello World!";

Both examples will yield the same result. The major difference is that every print call in the script in the second example will be printed to the file. This allows to avoid those long annoying debugging print FILEHANDLE lines in the code and just use oh-so-familiar print.

In addition, override of STDERR is also allowed, in case logging all the errors in the script is a goal.

#!/usr/bin/perl
use OmniLogger::File qw(stderr);
die “Goodbye Cruel World!”;

Disclaimer: This trick will not work within frameworks, like Mungo, that already override print.

--

--

Leon Fayer
homo technologicus’ asylum

Technologist. Cynic. Of the opinion that nothing really works until it works for at least a million of users.