Perl中捕获警告信息、异常信息并写入日志详解

纵然提出在各类Perl脚本和模块中展开警报,可是您又不想客商观看Perl发出的告诫。

程序脚本在运转进程中,总会遇见这么那样的主题素材,大家会预言一些主题材料并为其计划好管理代码,而有一点点无法预感。好的程序要能尽大概多的拍卖可能现身的那几个难题,本文就总计了有的艺术来解决那个非常,当然perl在此个管理了比不上别的同类语言,但也不会差到那边。在初叶前,大家先盘点一些关于perl的得失。

另一面你想在代码后面使用use
warnings
用作你的安全网,其他方面,平时警报会并发在显示器上。大多情景下,顾客不亮堂如哪个地点理那几个警报。即便幸运的话这个警报仅仅让客商惊叹一下,当然,不幸的是她们尝试着去修补它们…
(这里说的不是Perl程序猿。)

0.
历史太悠久了。你能够在壹玖玖柒年的微管理机上找到perl5.0。(只是调侃一下,历史持久没怎么不佳,与时俱进才是珍视卡塔尔(قطر‎

其三上边,你大概想要保存那个警示供之后分析。

1.
不回笼循环垃圾(那是个硬伤,可能和Perl设计的初志有关,小脚本影响相当的小;但因为这几个,perl与稍稍大学一年级点的次第就无缘了卡塔尔

其它,在不胜枚举地点还会有为数不菲Perl脚本和应用程序没有动用use
warnings也未有在#!行中应用-w。加上了use
warnings就或然会发生多量的警告。

2.
容错(如字符串和数值的隐式调换等卡塔尔,但换句话说正是对错误过度放任,程序产生错误结果并不是报错。

久远来看,当然是要息灭这么些警示,可是短时间来说吧?

3.
大器晚成件事有成千上万办法做(TIMTOWTDY卡塔尔国,但换句话说正是做少年老成件事从未“生龙活虎种生硬的做法”。比方数组的尺寸是$#somearray+1。为啥是加生龙活虎吗?难道不是somearray.length只怕len(somearray卡塔尔(قطر‎吗?

不怕是漫漫安插,你也无法写出截然没有BUG的代码,你也不可能担保应用以往恒久不会打字与印刷出警报音信。

4.
语法比较难懂。语法还地处“脚本语言”(指的是那多少个随手写随手扔的批处理脚本卡塔尔(قطر‎的固化上,只是稍稍比shell脚本更像古板的编制程序语言。比方:
4.1 大量运用$符号,以致种种用符号表示的极度变量。

你能么?

4.2 函数不带形参列表。

您能够在警戒打字与印刷到显示器此前捕获它们。

4.3
面向对象援救倒霉:语法中尚无对象语法(未有class等根本字卡塔尔国,全靠hash和bless。继承靠ISA,也从未语法援助。

信号
Perl有三个叫做%SIG的内建hash表,此中的键是操作系统数字信号的名字。对应的值是函数(大好多是函数援引),那一个函数会在一定的时域信号触发时被调用。

4.4
只有基本的可怜管理(perl的eval和die,肖似java的try和throw卡塔尔。可是这几个只是一条字符串音讯,少了依照对象的至极系统,十分管理总是不比其余语言。

而外系统提供的规范实信号以外,Perl还加多了五个里面“时限信号”。此中二个是<h__warn__<
span=””>,它在历次代码调用warn(卡塔尔函数的时候接触。此外三个是__DIE__,它在历次调用die(卡塔尔时接触。

上述那几个都会潜濡默化可读性和可写性。须要潜心的是,人的大脑同一时候能够关怀的新闻是个别的。要是写作业逻辑,却偏偏要持续保护实现细节(比方选择package,
hash, ISA,
bless来促成面向对象卡塔尔(قطر‎,程序猿的思路就能没完没了地被这么些细节打乱,急剧下滑编制程序速度,还有可能会到处犯错误,以至为了轻易性而就义功用以致准确性。

在本文中,我们会看见那一个是什么影响警示新闻的。

二个尖端的言语就是对高档的定义的空洞,使得工程师可以将思索从下层的内情中退出出来(Separation
of
Concern卡塔尔国。Perl作为文本批管理用的脚本语言是够的,能够做过多shell十分长于的字符串处理(正则表明式超级赞卡塔尔(قطر‎和计量,那在即时也是好的。不过对于有个别大片段的次序,以致须求面向对象的时候,Perl就不恰巧了。

无名函数

Perl能够写出相当轻易的代码(谷歌(Google卡塔尔(قطر‎时而“code
golf”卡塔尔,在这里一点上Ruby更像Perl。上边就介绍一些让Perl脚本编写的进一层专门的学业,以致在产出谬误时能博得很好的管理的诀窍技术。

sub {
}是佚名函数,也正是三个独有函数体而未有名字的函数。(在这里个事例中等学园函授数体也是空的,但是本身期望你能驾驭自个儿的意味。)

开荒约束指令,让编码更专门的学业

破获警示–不管理

假定您利用perl5.10
或更加高的版本,能够来得的钦点当前Perl版本号来机关张开约束指令。

万生机勃勃加多如下代码:

use 5.012; #电动启用use strict 指令
use v5.10; #活动布严苛情势

  local $SIG{__WARN__} = sub {
     # 此处能够得到警报音讯
  };

前面包车型大巴本子通过抬高上边包车型客车通令:
use strict;

那事实上意味着每回程序的有些地方时有发生了警告音讯时,不做别的管理。基本上,那会掩盖全部的告诫。

透过启用约束指令,编制程序时周边的错误超级轻巧暴表露来。而启用warnings指令的话,还是可以捕获一些其它不是很要紧的主题材料。

抓获警示–并转换到卓殊

如果担忧原先的次第启用strict后前后相继不正规运作,则能够在实质上校订代码前,先在命令行上试着启用strict看看:
perl -Mstrict freeoa_program.pl

You could also write: 你也得以写成:

Perl的封锁集包罗vars(变量卡塔尔国,subs(子程序卡塔尔(قطر‎和refs(援引卡塔尔(قطر‎这三片段,日常这两种节制同一时候利用。

  local $SIG{__WARN__} = sub {
    die;
  };

在众多情景下,系统调用只怕会停业;举例,尝试展开海市蜃楼的文件,也许去除某些仍含有文件的目录,或然尝试读取未有读权限的公文。在头里的演示中,大家早已应用了die函数,详细钻探关于错误管理和错误管理函数的有关内容。这个函数包含die函数、warn函数和eval函数。

那样会在每趟发生警报的时候调用die(卡塔尔,相当于把各类警报调换到相当。

die函数用于在指令或文件句柄退步时退出Perl脚本。

万后生可畏你想在老大中包括警报信息,能够这样写:

warn函数近似于die函数,但它不会退出脚本。

  local $SIG{__WARN__} = sub {
    my $message = shift;
    die $message;
  };

eval函数具备三种用场,但它最首要依然用来十分管理。

实则的告诫音讯会作为唯风流倜傥的参数字传送递给佚名函数。

读者或者还记得短路运算符&&和||,那八个运算符首先会求其侧面操作数的值,然后才会求其入手操作数的值。假设&&侧面操作数值为true,则求其动手的操作数。倘诺||右边操作数的值为false,那才求其动手的操作数。

破获警报–并写入日志
你大概想在在那之中做些此外事情:

Perl 5提供的Carp模块扩大了die和warn的功能

过滤嘈杂的警戒音信,留待后来深入分析:

对风流倜傥段代码中只要有一句现身极度,但先行并不知道是哪一句,如何举行特其他破获?

  local $SIG{__WARN__} = sub {
    my $message = shift;
    logger($message);
  };

eval{………};#抓获运维时的错误

这边大家假若logger(卡塔尔是您兑现的写日志函数。

if($@){……..};#拓宽错误管理

写日志

透过利用 Perl 的 eval 语句来解析错误,使用规范措施来管理 Perl
错误,请使用以下语法:
eval {enter statements you want to monitor};

倘令你的应用程序已经有日记机制。若无的话,最棒增进。尽管你不可能增多,你也要求操作系统的内建日志机制。比方Linux的syslog,MS
Windows的Event Logger,其余操作系统也可以有它们之中的日记机制。

在运行时,假如 Perl 引擎在 eval 块内的讲话中遇见错误,那么它会跳过 eval
块的盈余部分,并为对应的荒诞文本设置 $@。比如:
eval{$objectName->MethodName();};
if ($@){
 print “Error using MethodName method. Error: $@n”;
}
else{
 # continue without error …
}

在本文的例子里,大家运用叁个自制logger(卡塔尔国函数来代表那么些主见。

好几预期经常败北的函数不含有在这里约束内。越发是验证和设置字段函数再次来到错误表达,并非抛出十分。那样可阻止当中中断随机信号后对其开展管理,实际不是简约地让程序对分外按暗中同意方法实行拍卖。

抓获并写日记的完整例子

The recommended way to do some things, like timeouts, is through an
eval/die pair. However, I’ve noticed that if you set $SIG{__DIE__}
to do some custom reporting, like in:
 $SIG{__WARN__} = sub { … };   # custom error report
 $SIG{__DIE__} = sub { &{$SIG{__WARN__}; exit(1); };
 # custom error report and terminate

  #!/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”;
    }
  }

eval {
 $SIG{ALRM} = sub { die “timeoutn”; };
 alarm 20;
 …
 alarm 0;
}

上边的代码会在log.txt文件中加多下面豆蔻梢头行:

“die” isn’t caught! It remains fatal!

  Use of uninitialized value in addition (+) at code_with_warnings.pl
line 14.

The only way around this (that I found), is to clear $SIG{__DIE__}
in the eval blok:
eval{
 local($SIG{__DIE__});   #back to standard Perl die
 …
}

变量$counter和函数count(State of Qatar仅是爆发警报示例的意气风发有个别。

Question: is there another way to terminate a __DIE__ sub, that is
NOT fatal in eval? “die” doesn’t die in eval, but ONLY if you didn’t set
$SIG{__DIE__} yourself (even outside of the eval block). Surely,
this can’t be the way it’s supposed to be?

提个醒管理函数中的警示音讯

你最棒要利用Try::Tiny模块来管理这个难点,那将帮衬你幸免某些老的本子里的骗局。

__WARN__在其管理函数实践进度中是自动被剥夺的。所以在警戒处理函数实施进度中发生的(新)警报音信不会促成极端循环。

use Try::Tiny;
try{
 die “foo”;
}catch{
 warn “caught error: $_”;
};

您能够在perlvar文档中询问到越来越多细节。

有时大家平日写四个 open ,然后还要写上频仍 die
像下边,读一个文件,然后写三个文书,有的时候自个儿写的 open 会超越 4
个,要写很频仍 die 那时特别麻烦。大家如若间接 use autodie
就径直能一下子就解决了所十分,全体的 die 之处都能活动完毕。

Avoid multiple warnings

open my $fh, ‘<‘, $in or die “$!”;
open my $fh, ‘>’, $out or die “$!”;

autodie简化错误管理

亟待注意的是双重的警戒音信可能会充满日志文件。笔者能够利用贰个简约的近乎缓存的性状来减弱重复警示新闻的数目。

autodie编译指令(从5.10.1起在此以前自带,也足以平素从CPAN安装State of Qatar

#!/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, $msg) = @_;
    if (open my $out, ‘>>’, ‘log.txt’) {
        chomp $msg;
        print $out “$level – $msgn”;
    }
  }

默许情状下,autodie会对它能起成效的装有函数生效。若是只是梦想对有个别特定函数起功效,能够将各种函数的名字或大器晚成组函数的组名列出来告诉autodie:
use autodie qw(open close); #只对特定函数生效
use autodie qw(:filesys); #只对 某组函数生效

能够观察,大家把$counter变量赋值成undef,然后再一次调用count(卡塔尔国函数来发出相像的警报。

在autodie捕获错误时,它会把$@设置为autodie::exception对象,而$@正是意味eval错误变量

小编们也把__WARN__的管理函数替换来三个不怎么复杂的本子:

累积 autodie 未来就成了
use autodie
open my $fh, ‘<‘, $fin;
open my $fh, ‘>’, $fout;

  my %WARNS;
  local $SIG{__WARN__} = sub {
      my $message = shift;
      return if $WARNS{$message}++;
      logger(‘warning’, $message);
  };

有了那些便利多了,一时大家平时不想让程序 die
了后退出,想抓到原因,得使用 eval 然后来检讨 $@,像下边那样。

在调用logger此前,会检查一下当前字符串是不是早就在%WA奥迪TTNShash表中。若无的话,会增多它并调用logger(State of Qatar。假若已经有了,就调用return,并不三次记录同风流倜傥的事件。

use autodie;
eval{
 open my $fh, ‘<‘, $in;
 # …..
}
if($@){
 #TODO
}

你可能纪念起大家在unique values in an array也应用了一直以来的关键。

eval{
 open my $fh, ‘>’, $out;
}

local是什么?
在下面装有的事例中,小编利用local函数來局地化(警告管理)效果。严酷来讲,在此些事例中大家从没须要那样做,因为只要那几个代码是主脚本的率先有些。这种情景下就不留意了,毕竟是在大局功能域里面。

if($@){
 #TODO
}

而是,最棒是这么用。

幸亏上次唯有三个 open 。是还是不是感觉很痛苦,假诺 open 之类会 die
的愈扩充点的话。别的,有未有察觉,只要第二个 open 失利,第二个 if ($@State of Qatar长久会战败,因为 $@ 的记号上面也检查相同的变量。此时使用 Try::Tiny 中的
try catch 吧。

Try::Tiny模块合作使用

local对于在模块中约束(对警报)的退换是非常重要的。极其是要透露的模块。若无局地化,会影响全体应用程序。limit则会把影响范围在所在的闭合代码块里。

Perl
未有放置的可怜管理体制,所以最合适的主意正是选取Try::Tiny模块。纵然CPAN中拍卖非常的模块超级多,但是那些模块最为轻松,使用起来也绝非过多的依赖关系。

制止采用全局的%WA路虎极光NS

use autodie;
use Try::Tiny;
# handle errors with a catch handler
try{
 die “foo”;
}catch{
 warn “caught error: $_”; # not $@
};

假定你正在利用Perl
5.10依旧更新的本子,你能够改写一下代码来替换掉全局变量%WALX570NS。要那样做的话,需在剧本的起来使用use
v5.10;,然后在无名函数内部采纳state关键词来声称变量。

留意:catch
代码以分行结尾的,是八个表明式。其余它会将出错的音讯保存在变量$_而不是$@中。

  #!/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);
  };

那个时候使用很便利

use autodie;
use Try::Tiny;
try {
 open my $fh, ‘<‘, $in;
 # …..
 open my $fh, ‘>’, $out;
 # …..
}
catch {
 print “Error $_n”;
}

不经常候大家还足以在这里个地点接收’try catch’做程序流程序调节制
use autodie;
use Try::Tiny;

try {
 &sub1();
 &sub2();
 &sub3();
}
catch {
 &suba();
 &subb();
 &subc();
}

像上边,只要在 sub1, sub2, sub3 率性一个子函数 die
出来,这几个流程就觉着出错,就做任何的另豆蔻梢头种方案的流程 suba,subb,subc。

 

来源:网络

发表评论

电子邮件地址不会被公开。 必填项已用*标注