在perl中创建日志

时间:2013-06-17 05:35:38

标签: perl

我的perl脚本中有很多子例程。我想为每个子程序创建日志,即日志将写入子程序是否工作正常或失败然后失败的地方。根据我的逻辑标志应该保持&如果基于标志值,则创建子例程日志。我是perl的新手,所以任何人都可以给我一个相同的例子。

2 个答案:

答案 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块内执行,该块在主编译阶段之后但在常规执行开始之前运行。

此解决方案存在一些缺点:

  1. 它仅适用于命名的潜艇,但不适用于匿名潜艇。
  2. 默认情况下它还会修饰导入的潜艇。
  3. 没有进一步的过滤器,它将装饰所有潜艇。
  4. 我们不会将void上下文传播到原始代码。
  5. 更好的解决方案是使用子例程属性,但它们设置起来有点困难。属性是在编译时执行的处理程序,可以传送元数据。例如。在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