I men­tioned in pass­ing last week that the next major release of Perl, v5.36, is set to enable warnings by default for code that opts in to use v5.35; or above. Commemorating Perl’s 34th birth­day the week before that, I not­ed that the warn­ings sys­tem has been get­ting ever finer-​grained since its intro­duc­tion in 2000. And fel­low Perl blog­ger and CPAN author Tom Wyant has been cat­a­loging his favorites over the past sev­er­al months — the lat­est as of this writ­ing was on the ambigu­ous” cat­e­go­ry of warn­ings, and you can find links to pre­vi­ous entries in his series at the bot­tom of that post.

It occurred to me after­ward that there may be some con­fu­sion between the warnings prag­ma and the relat­ed warn func­tion for report­ing arbi­trary run­time errors. warn out­puts its argu­ments to the stan­dard error (STDERR) stream, or if it’s not giv­en any then you get a string with any excep­tion from [email protected] ($EVAL_ERROR under use English) fol­lowed by a tab and then “...caught at <file> line x.” If that’s emp­ty too, a plain warn just says, Warning: something's wrong at <file> line x.”, which isn’t exact­ly help­ful, but then again you didn’t give it much to go on.

warn out­put doesn’t have to go to STDERR, and this is where the rela­tion to the warn­ings prag­ma comes in because both are gov­erned by the __WARN__ sig­nal han­dler in the %SIG hash. Normally, you might opt to only dis­play run­time warn­ings if a debug­ging flag is set, like so:

#!/usr/bin/env perl

use strict;
use warnings;

my $DEBUG = 0;
$SIG{__WARN__} = sub { warn @_ if $DEBUG };
warn 'shhh'; # silenced

$DEBUG = 1;
warn 'hello warnings';

But if you set that sig­nal han­dler in a BEGIN block, it catch­es compile-​time warn­ings too, in which case flip­ping a flag after the fact has no effect — the compiler’s already run:

#!/usr/bin/env perl

use strict;
use warnings;

my $DEBUG = 0;
BEGIN { $SIG{__WARN__} = sub { warn @_ if $DEBUG } }
my $foo = 'hello';
my $foo = 'world'; # no warning issued here

$DEBUG = 1;
my $foo = 'howdy'; # still nothing

By the way, both __WARN__ and __DIE__ hooks are also used by the Carp mod­ule and its friends, so you can use the same tech­nique with their enhanced output:

#!/usr/bin/env perl

use strict;
use warnings;
use Carp qw(carp cluck);

my $DEBUG = 0;
BEGIN { $SIG{__WARN__} = sub { warn @_ if $DEBUG } }
carp 'quiet fish';

$DEBUG = 1;
loud_chicken();

sub loud_chicken {
    cluck 'here comes a stack trace';
}

You could use these as step­ping stones towards a debug log for larg­er appli­ca­tions, but at that point, I’d sug­gest look­ing into one of the log­ging mod­ules on CPAN like Log::Log4perl (not to be con­fused with that lately-​problematic Java library), Log::Dispatch (which can be wired into Log4perl), or some­thing else to suit your needs.

One thought on “Perl warnings and the warn function

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.