这是一个场景。您有大量遗留脚本,全部使用通用库。所述脚本使用“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 的答案的评论中提出了一个有趣的建议:

你可以子类化 http://perldoc.perl.org/IO/Handle.html 并创建自己的文件句柄,该文件将完成日志记录工作。——卡米尔·基西尔

这可能会做到这一点,但有两个注意事项:

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

2) IO::句柄 文档说你不能对它进行子类化,到目前为止我的尝试没有结果。是否需要任何特殊的东西才能使子类化 IO::Handle 工作?标准的“use base 'IO::Handle'”然后覆盖 new/print 方法似乎什么也没做。

最终编辑:看起来 IO::Handle 是死胡同,但 Tie::Handle 可以做到。感谢所有的建议;他们都非常好。我将尝试 Tie::Handle 路线。如果它引起问题我会回来的!

附录:请注意,经过一番处理后,我发现如果您不做任何棘手的事情,Tie::Handle 就可以工作。如果您将 IO::Handle 的任何功能与绑定的 STDOUT 或 STDERR 一起使用,那么要让它们可靠地工作基本上是一个冒险 - 我找不到一种方法来让 IO::Handle 的自动刷新方法在我的绑定上工作处理。如果我在绑手柄之前启用自动冲洗,它就会起作用。如果这对您有用,那么 Tie::Handle 路线可能是可以接受的。

有帮助吗?

解决方案

您可以覆盖许多内置函数(请参阅 perlsub)。然而, print 是不能以这种方式工作的内置函数之一。超越的困难 print 详细介绍在这 erlmonk 的主题.

然而,你

  1. 创建一个包
  2. 系上一个把手
  3. 选择该手柄。

现在,几个人已经给出了基本框架,但它的工作原理是这样的:

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 );

您可以覆盖 printf 以相同的方式:

sub PRINTF { 
    shift;
    # You can do pretty much anything you want here. 
    # And it's printing to what was STDOUT at the start.
    # 
    my $format = shift;
    print $OLD_STDOUT join( '', 'NOTICE: ', sprintf( $format, @_ ));
}

领带::手柄 您可以覆盖 STDOUT 的行为。

其他提示

您可以使用Perl的选择重定向STDOUT。

open my $fh, ">log.txt";
print "test1\n";
my $current_fh = select $fh;
print "test2\n";
select $current_fh;
print "test3\n";

在文件句柄可以是任何东西,甚至管到另一个过程,后期处理您的日志信息。

PerlIO的::三通在在 PerlIO的::的Util 模块似乎可以让你“三通”的文件句柄到多个目的地(例如,日志处理器和STDOUT)。

的输出

的选择很多。使用select()来更改打印默认的文件句柄。或配合STDOUT。或重新打开它。或施加IO层到它。

这是不回答你的问题,但你应该能够通过逻辑供自己使用。如果不是这样,也许别人会觉得它有用。

追赶标题格式错误,防患于未然......

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";

您可以从捕获原始脚本的标准输出,并写入输出某处明智的包装脚本运行的脚本。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top