Perl中捕获警告信息、异常信息并写入日志详解
虽然建议在每个Perl脚本和模块中开启警告,可是你又不想用户看到Perl发出的警告。 一方面你想在代码前面使用use warnings作为你的安全网,另一方面,通常警告会出现在屏幕上。多数情况下,客户不知道如何处理这些警告。如果幸运的话这些警告仅仅让客户惊讶一下,当然,不幸的是他们尝试着去修复它们... (这里说的不是Perl程序员。) 第三方面,你或许想要保存这些警告供之后分析。 此外,在很多地方还有很多Perl脚本和应用程序没有使用use warnings也没有在#!行中使用-w。加上了use warnings就可能会产生大量的警告。 长远来看,当然是要消除这些警告,但是短期来说呢? 即便是长期计划,你也不能写出完全没有BUG的代码,你也不能确保应用将来永远不会打印出警告信息。 你能么? 你可以在警告打印到屏幕之前捕获它们。 信号 除了系统提供的标准信号以外,Perl还添加了两个内部“信号”。其中一个是<h__warn__< span="">,它在每次代码调用warn()函数的时候触发。另外一个是__DIE__,它在每次调用die()时触发。 在本文中,我们会看到这些是怎样影响警告信息的。 匿名函数 sub { }是匿名函数,也就是一个只有函数体而没有名字的函数。(在这个例子中函数体也是空的,但是我希望你能明白我的意思。) 捕获警告--不处理 如果添加如下代码: 复制代码 代码如下: local $SIG{__WARN__} = sub { # 此处可以获得警告信息 }; 这实际上表示每次程序的某个地方产生了警告信息时,不做任何处理。基本上,这会隐藏所有的警告。 捕获警告--并转换成异常 You could also write: 你也可以写成: 复制代码 代码如下: local $SIG{__WARN__} = sub { die; }; 这样会在每次产生警告的时候调用die(),也就是把每个警告转换成异常。 如果你想在异常中包含警告信息,可以这么写: 复制代码 代码如下: local $SIG{__WARN__} = sub { my $message = shift; die $message; }; 实际的警告信息会作为唯一的参数传递给匿名函数。 捕获警告--并写入日志 过滤嘈杂的警告信息,留待后来分析: 复制代码 代码如下: local $SIG{__WARN__} = sub { my $message = shift; logger($message); }; 这里我们假设logger()是你实现的写日志函数。 写日志 假设你的应用程序已经有日志机制。如果没有的话,最好加上。即便你不能添加,你也需要操作系统的内建日志机制。例如Linux的syslog,MS Windows的Event Logger,其它操作系统也有它们内部的日志机制。 在本文的例子里,我们使用一个自制logger()函数来代表这个想法。 捕获并写日志的完整例子 复制代码 代码如下: #!/usr/bin/perl use strict; use warnings; local $SIG{__WARN__} = sub { my $message = shift; logger('warning',$message); }; my $counter; count(); print "$countern"; sub count { $counter = $counter + 42; } sub logger { my ($level,$msg) = @_; if (open my $out,'>>','log.txt') { chomp $msg; print $out "$level - $msgn"; } } 上面的代码会在log.txt文件中添加下面一行: 复制代码 代码如下: Use of uninitialized value in addition (+) at code_with_warnings.pl line 14. 变量$counter和函数count()仅是产生警告示例的一部分。 警告处理函数中的警告信息 __WARN__在其处理函数执行过程中是自动被禁用的。所以在警告处理函数执行过程中产生的(新)警告信息不会导致无限循环。 你可以在perlvar文档中了解到更多细节。 复制代码 代码如下: #!/usr/bin/perl use strict; use warnings; my %WARNS; local $SIG{__WARN__} = sub { my $message = shift; return if $WARNS{$message}++; logger('warning',$message); }; my $counter; count(); print "$countern"; $counter = undef; count(); sub count { $counter = $counter + 42; } sub logger { my ($level,'log.txt') { chomp $msg; print $out "$level - $msgn"; } } 可以看到,我们把$counter变量赋值成undef,然后再次调用count()函数来产生同样的警告。 我们也把__WARN__的处理函数替换成一个稍微复杂的版本: 复制代码 代码如下: my %WARNS; local $SIG{__WARN__} = sub { my $message = shift; return if $WARNS{$message}++; logger('warning',$message); }; 在调用logger之前,会检查一下当前字符串是否已经在%WARNShash表中。如果没有的话,会添加它并调用logger()。如果已经有了,就调用return,并不二次记录同样的事件。 你可能回忆起我们在unique values in an array也使用了同样的点子。 local是什么? 然而,最好是这么用。 local对于在模块中限制(对警告)的改变是很重要的。特别是要发布的模块。如果没有局部化,会影响整个应用程序。limit则会把影响限制在所在的闭合代码块里。 避免使用全局的%WARNS 如果你正在使用Perl 5.10或者更新的版本,你可以改写一下代码来替换掉全局变量%WARNS。要这么做的话,需在脚本的开头使用use v5.10;,然后在匿名函数内部使用state关键词来声明变量。 复制代码 代码如下: #!/usr/bin/perl use strict; use warnings; use v5.10; local $SIG{__WARN__} = sub { state %WARNS; my $message = shift; return if $WARNS{$message}++; logger('warning',$message); }; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |