如何连接到Perl';什么是印刷品?

如何连接到Perl';什么是印刷品?,perl,printing,hook,tie,Perl,Printing,Hook,Tie,下面是一个场景。您有大量的遗留脚本,都使用一个公共库。所述脚本使用“打印”语句进行诊断输出。不允许对脚本进行任何更改-它们的范围很广,得到了它们的批准,并且早就离开了监督和控制的富有成效的山谷 现在有了新的需求:现在必须将日志添加到库中。这必须自动且透明地完成,标准库的用户无需更改其脚本。公共库方法可以简单地添加日志调用;这是最简单的部分。困难的部分在于,这些脚本的诊断输出总是使用“print”语句显示。必须存储此诊断输出,但同样重要的是,必须对其进行处理 作为此处理的示例,库应仅记录包含“警告

下面是一个场景。您有大量的遗留脚本,都使用一个公共库。所述脚本使用“打印”语句进行诊断输出。不允许对脚本进行任何更改-它们的范围很广,得到了它们的批准,并且早就离开了监督和控制的富有成效的山谷

现在有了新的需求:现在必须将日志添加到库中。这必须自动且透明地完成,标准库的用户无需更改其脚本。公共库方法可以简单地添加日志调用;这是最简单的部分。困难的部分在于,这些脚本的诊断输出总是使用“print”语句显示。必须存储此诊断输出,但同样重要的是,必须对其进行处理

作为此处理的示例,库应仅记录包含“警告”、“错误”、“通知”或“注意”字样的打印行。以下极为琐碎和人为的示例代码(tm)将记录一些上述输出:

sub CheckPrintOutput
{
    my @output = @_; # args passed to print eventually find their way here.
    foreach my $value (@output) {
         Log->log($value) if $value =~ /warning|error|notice|attention/i;
    }
}
(我希望避免诸如“实际应该记录什么”、“打印不应该用于诊断”、“perl很糟糕”或“此示例存在缺陷x、y和z”……为了简洁和清晰,这一点被大大简化了。)

基本问题归结为捕获和处理传递给print的数据(或任何perl内置的数据,按照这些推理思路)。可能吗?有什么办法可以干净利落地做吗?是否有任何日志记录模块可以让您使用挂钩?还是像瘟疫一样应该避免,我应该放弃捕获和处理打印输出

附加:这必须跨平台运行-windows和*nix一样。运行脚本的过程必须保持不变,脚本的输出也必须保持不变

补充:codelogic回答的评论中提出了一个有趣的建议:

您可以子类化并创建 自己的文件句柄,该句柄将执行日志记录工作卡米尔·基希尔

这可能会做到,但有两个警告:

1) 我需要一种方法将此功能导出给使用公共库的任何人。它必须自动应用于STDOUT,也可能应用于STDERR

2) 文档中说您不能对它进行子类化,我的尝试到目前为止都是徒劳的。要使子分类IO::Handle正常工作,是否需要一些特殊的东西?标准的“use base”IO::Handle,然后重写new/print方法,似乎什么也做不了

最终编辑:看起来IO::Handle是一条死胡同,但Tie::Handle可以做到这一点。谢谢你的建议;他们都很好。我要试试这条路线。如果它引起问题,我会回来的

附录:注意,在处理了这一点之后,我发现如果您不做任何棘手的事情,Tie::Handle将起作用。如果将IO::Handle的任何功能与绑定的标准输出或标准输出一起使用,那么要让它们可靠地工作基本上是一件糟糕的事情——我找不到方法让IO::Handle的自动刷新方法在绑定的句柄上工作。如果我在系上手柄之前启用了自动刷新,它就会工作。如果这对您有效,那么Tie::Handle路由可能是可以接受的。

您可以使用Perl重定向STDOUT

open my $fh, ">log.txt";
print "test1\n";
my $current_fh = select $fh;
print "test2\n";
select $current_fh;
print "test3\n";
文件句柄可以是任何东西,甚至可以是到另一个进程的管道,该进程将日志消息进行post处理


在模块中,似乎允许您将文件句柄的输出“T”到多个目标(例如,日志处理器和标准输出)。

您可以从捕获原始脚本标准输出并将输出写入合理位置的包装器脚本运行脚本。

有很多选择。使用select()更改打印默认为的文件句柄。或者打领带。或者重新打开它。或者对其应用IO层。

有许多内置组件可以覆盖(请参阅)。但是,
print
是一个内置的,不能以这种方式工作。覆盖
print
的困难将在下面详细介绍

但是,你可以

  • 创建一个包
  • 系把手
  • 选择此句柄 现在,有几个人给出了基本框架,但结果是这样的:

    package IO::Override;
    use base qw<Tie::Handle>;
    use Symbol qw<geniosym>;
    
    sub TIEHANDLE { return bless geniosym, __PACKAGE__ }
    
    sub PRINT { 
        shift;
        # You can do pretty much anything you want here. 
        # And it's printing to what was STDOUT at the start.
        # 
        print $OLD_STDOUT join( '', 'NOTICE: ', @_ );
    }
    
    tie *PRINTOUT, 'IO::Override';
    our $OLD_STDOUT = select( *PRINTOUT );
    

    查看您可以覆盖STDOUT行为的所有内容。

    这不是您问题的答案,但您应该能够采用自己使用的逻辑。如果没有,也许其他人会发现它很有用

    在发生错误的标题之前捕获它们

    package PsychicSTDOUT;
    use strict;
    
    my $c = 0;
    my $malformed_header = 0;
    open(TRUE_STDOUT, '>', '/dev/stdout');
    tie *STDOUT, __PACKAGE__, (*STDOUT);
    
    sub TIEHANDLE {
        my $class = shift;
        my $handles = [@_];
        bless $handles, $class;
        return $handles;
    }
    
    sub PRINT {
        my $class = shift;
        if (!$c++ && @_[0] !~ /^content-type/i) {
            my (undef, $file, $line) = caller;
            print STDERR "Missing content-type in $file at line $line!!\n";
            $malformed_header = 1;
        }
        return 0 if ($malformed_header);
        return print TRUE_STDOUT @_;
    }
    1;
    
    用法:

    use PsychicSTDOUT;
    print "content-type: text/html\n\n"; #try commenting out this line
    print "<html>\n";
    print "</html>\n";
    
    使用心理测试;
    打印“内容类型:text/html\n\n”#试着注释这句话
    打印“\n”;
    打印“\n”;
    
    select很好,但我们也需要处理这些数据。是否存在一种文件句柄类型,该类型为钩子提供求值和处理传递给它的数据的方法?此外,我们无法修改这些脚本的当前行为,因此输出也必须保留在STDOUT上。您可以将文件句柄作为管道打开到另一个进程。在那里做日志记录,然后你可以从里面打印到stdout。你可以让管道进程将其STDIN重定向到stdout,除了处理它之外,这样当前的行为就不会受到影响。我不知道有任何模块抽象了这个功能。有趣的想法。。。那是用fork()之类的东西吗?还有另一个要求……这必须跨平台运行:windows和*nix一样。据我所知,fork在windows中并不真正起作用。或者您正在谈论一种不同的管道机制?不幸的是,这违反了“脚本必须与以前运行相同”的要求。用户不必做任何不同的事情来获取此日志信息。它不会违反任何规定。没有人说当你输入“perl”时执行的是真正的perl;包装器脚本是一种可能的解决方案。但是,要求用户运行另一个
    use PsychicSTDOUT;
    print "content-type: text/html\n\n"; #try commenting out this line
    print "<html>\n";
    print "</html>\n";