处理告警
Perl的写法不止一种,有的方式让人迷惑,有的甚至不正确。Perl的告警系统可以帮你避免这些情况的发生。
产生告警
使用内置关键字 warn 产生一个告警:
warn 'Something went wrong!';
warn会将列表值输出到STDERR句柄(标准错误输出)。除非列表的最后一个元素以换行符结尾,否则Perl还会在告警中带上文件名和warn所在行号来帮助定位问题。
核心模块Carp扩展了Perl的告警机制。它的函数carp(),从调用的角度报告一个告警信息。
use Carp 'carp';
sub only_two_arguments
{
my ($lop, $rop) = @_;
carp( 'Too many arguments provided' ) if @_ > 2;
...
}
告警信息将会包含文件名和调用代码的行号。还有个函数cluck()功能类似,但是它会包含回溯所有当前函数的调用者。
Carp的详细模式会将整个文件的carp()和croak()产生的告警信息加上回溯信息:
$ perl -MCarp=verbose my_prog.pl
写模块时建议使用Carp来替代warn和die。
注入Carp
有时候你要调试一些未使用carp()和croak()的代码。这种情况下,使用Carp::Always模块来为所有的warn或die增加回溯信息:
perl -MCarp::Always some_program.pl
启用和禁用告警
有的老代码会使用-w命令行参数。-w参数将使整个程序启用告警,即使是外部别人写的模块。如果你有时间和精力去消除所有代码库的告警,那当然好,否则信息太多反而会淹没了你的关注点。在很多年里,-w参数是启用告警的唯一手段。
现代更好的方式是使用warnings编译指示来启用告警,这样就能在一定范围内(词法范围内)启用告警机制,范围之外的则不受影响。
全局告警标识
-W参数会无视任何warngins编译指示,强制整个程序启用告警。
-X参数则会强制整个程序禁用告警。
它们都不常见。
-w,-W,-X 都会设置全局变量$W。在编译指示warnings出现之前,通常会将变量$W本地化赋值,来控制作用范围。
禁用告警类别
要在词法范围内禁用指定类型的告警,使用no warnings后面跟指定的类型参数列表。
perldoc perllexwarn可查看当前版本Perl所支持的告警种类。有些警告可能没有什么实际的作用,比如递归的警告(recursion)--调用自身超过100次就会触发该警告。如果你写的程序可以正常终止,但是次数可能超过100,这时你就需要禁用此类警告。
如果你的程序会生成代码,或者重新定义了符号引用,你可能想要禁用redefine警告。
一些有经验的Perl程序员可能会禁用 uninitialized警告,因为字符串的值的来源有很多。如果你总是很注意初始化你的变量,你就永远不需要禁用这类警告。
提升告警的严重性
如果你的项目需要将告警的严重性升级,那么可以将告警提升为致命错误。(告警可以认为是一种提示信息,而致命错误则要求你必须修复)在词法范围内将所有种类的告警信息提升为致命错误:
use warnings FATAL => 'all';
你也可仅将指定类别的告警信息提升为致命错误:
use warnings FATAL => 'deprecated';
有了这些纪律,就能构建健壮的代码--但还是要谨慎,因为很多的告警信息的产生依赖于运行时条件。如果你的测试条目未能覆盖所有可能的警告,那么你的程序可能在运行时会因为异常而退出。
新版Perl增加了新的告警并且可以配置提升告警为致命错误。
捕获告警
如果有需要,你还可以捕获告警。变量%SIG保存有信号处理的信息, 我们可以通过设置$SIG{WARN} 的处理程序来捕获告警:
{
my $warning;
local $SIG{__WARN__} = sub { $warning .= shift };
# do something risky
...
say "Caught warning:\n$warning" if $warning;
}
告警的处理程序中,第一个参数就是告警的内容。
有点要注意的是: %SIG是一个全局变量。
自定义告警
编译指示warnings::register允许你创建自己的告警:
package Scary::Monkey;
use warnings::register;
这样就会创建一个叫 Scary::Monkey的告警类。
启用这个警告:
use warnings 'Scary::Monkey';
禁用这个警告:
no warnings 'Scary::Monkey';
warnings::enabled()用来测试在调用者的词法访问内是否启用了告警类。 warnings::warnif()如果告警有效就产生一个告警信息。例如,在deprecated类中产生一个警告:
package Scary::Monkey;
use warnings::register;
sub import
{
warnings::warnif( 'deprecated', 'empty imports from ' . __PACKAGE__ . ' are now deprecated' )
unless @_;
}
perldoc perllexwarn可查看更多细节。