我的perl脚本中有很多子例程。我想为每个子程序创建日志,即日志将写入子程序是否工作正常或失败然后失败的地方。根据我的逻辑标志应该保持&如果基于标志值,则创建子例程日志。我是perl的新手,所以任何人都可以给我一个相同的例子。
答案 0 :(得分:1)
最直接的解决方案是编写debug
子例程并在适当的地方使用它:
sub debug {
my($p, $f, $l) = caller;
print "$p, $f, $l\n";
}
sub test {
debug;
print "something\n";
debug;
}
您可以在caller
手册页上查找perlfunc
。
如果你想要更加漂亮,可以Aspect进行旋转。
答案 1 :(得分:0)
您尝试执行的操作可以通过手动插入日志记录语句来实现:
use constant LOG => 1;
sub foo {
debug 'BEFORE', 'main::foo', @_ if LOG; # gets optimized away if LOG is false
do stuff;
debug 'AFTER', 'main::foo', if LOG; # the same
return $things;
}
(假设debug
是执行日志记录的函数)
但是,对于某些情况,我们可以自动执行此操作。特别是,我们可以为每个命名的子例程添加日志包装器。我们将通过包存储进行元编程,这是符号表。
存储是一个名为%main::
的大哈希,请注意尾随的双冒号。它包含 globs ,它们是带有一组固定键的哈希值。他们有*
sigil。 glob的CODE
条目包含代码引用。
我们可以选择包含
等代码条目的所有存储空间my $stash = \%main::;
my @interesting_globs = grep *$_{CODE}, values %$stash;
我们可以为glob分配一个引用,这将填充glob中的正确插槽。例如,
sub foo { say 1 }
与
大致相同BEGIN {
*foo = sub { say 1 };
}
所以现在我们可以用一个执行日志记录的包装器包装原始子:
for my $glob (@interesting_globs) {
my $code = *$glob{CODE}; # store the coderef in a lexical variable
no warnings 'redefine';
*$glob = sub {
debug 'BEFORE', $glob, @_ if LOG;
my @return_value = wantarray ? &$code : scalar &$code;
debug 'AFTER', $glob, @return_value if LOG;
return wantarray ? @return_value : $return_value[0];
}
}
wantarray
内容确保在正确的上下文中调用内部子(列表上下文/标量上下文)。但是,我们不检查void上下文。 &$code
(注意丢失的parens)是一种说$code->(@_)
或&$code(@_)
的奇特方式。
在所有潜艇编制完成后装饰潜艇是很重要的。因此,它应该在INIT
块内执行,该块在主编译阶段之后但在常规执行开始之前运行。
此解决方案存在一些缺点:
更好的解决方案是使用子例程属性,但它们设置起来有点困难。属性是在编译时执行的处理程序,可以传送元数据。例如。在sub foo :log_this { ... }
中,将调用log_this
处理程序。
完整示例:
$ perl -E'
sub foo {say "@_"};
sub bar { foo(0, @_, "inf") }
INIT{
for my $glob (grep *$_{CODE}, values %main::){
my $orig = *$glob{CODE};
*$glob = sub {
say "BEFORE $glob: @_";
my @ret = $orig->(@_); # this demo misses context handling
say "AFTER $glob: @ret";
@ret;
};
}
}
bar(1,2,3)'
BEFORE *main::bar: 1 2 3
BEFORE *main::foo: 0 1 2 3 inf
0 1 2 3 inf
AFTER *main::foo: 1
AFTER *main::bar: 1