我怎样才能连接到 Perl 的打印?

发布于 2024-07-10 21:25:54 字数 1891 浏览 8 评论 0原文

这是一个场景。 您有大量遗留脚本,全部使用通用库。 所述脚本使用“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 很糟糕”等问题”,或者“这个例子有缺陷 xy 和 z”...为了简洁和清晰,这被大大简化了)

基本问题归结为捕获和处理传递给 print (或任何 perl 内置程序)的数据,沿着这些推理思路。 )。 是否可以? 有什么办法可以干干净净吗? 是否有任何日志记录模块可以让您执行此操作? 或者它是像瘟疫一样应该避免的东西,我应该放弃捕获和处理打印输出吗?

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

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

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

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

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

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

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

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

Here's a scenario. You have a large amount of legacy scripts, all using a common library. Said scripts use the 'print' statement for diagnostic output. No changes are allowed to the scripts - they range far and wide, have their approvals, and have long since left the fruitful valleys of oversight and control.

Now a new need has arrived: logging must now be added to the library. This must be done automatically and transparently, without users of the standard library needing to change their scripts. Common library methods can simply have logging calls added to them; that's the easy part. The hard part lies in the fact that diagnostic output from these scripts were always displayed using the 'print' statement. This diagnostic output must be stored, but just as importantly, processed.

As an example of this processing, the library should only record the printed lines that contain the words 'warning', 'error', 'notice', or 'attention'. The below Extremely Trivial and Contrived Example Code (tm) would record some of said output:

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

(I'd like to avoid such issues as 'what should actually be logged', 'print shouldn't be used for diagnostics', 'perl sucks', or 'this example has the flaws x y and z'...this is greatly simplified for brevity and clarity. )

The basic problem comes down to capturing and processing data passed to print (or any perl builtin, along those lines of reasoning). Is it possible? Is there any way to do it cleanly? Are there any logging modules that have hooks to let you do it? Or is it something that should be avoided like the plague, and I should give up on ever capturing and processing the printed output?

Additional: This must run cross-platform - windows and *nix alike. The process of running the scripts must remain the same, as must the output from the script.

Additional additional: An interesting suggestion made in the comments of codelogic's answer:

You can subclass http://perldoc.perl.org/IO/Handle.html and create your
own file handle which will do the logging work. – Kamil Kisiel

This might do it, with two caveats:

1) I'd need a way to export this functionality to anyone who uses the common library. It would have to apply automatically to STDOUT and probably STDERR too.

2) the IO::Handle documentation says that you can't subclass it, and my attempts so far have been fruitless. Is there anything special needed to make sublclassing IO::Handle work? The standard 'use base 'IO::Handle' and then overriding the new/print methods seem to do nothing.

Final edit: Looks like IO::Handle is a dead end, but Tie::Handle may do it. Thanks for all the suggestions; they're all really good. I'm going to give the Tie::Handle route a try. If it causes problems I'll be back!

Addendum: Note that after working with this a bit, I found that Tie::Handle will work, if you don't do anything tricky. If you use any of the features of IO::Handle with your tied STDOUT or STDERR, it's basically a crapshoot to get them working reliably - I could not find a way to get the autoflush method of IO::Handle to work on my tied handle. If I enabled autoflush before I tied the handle it would work. If that works for you, the Tie::Handle route may be acceptable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

恏ㄋ傷疤忘ㄋ疼 2024-07-17 21:25:54

您可以覆盖许多内置函数(请参阅 perlsub< /a>)。 但是,print 是不能以这种方式工作的内置函数之一。 重写 print 的困难在此 perlmonk 的线程中有详细介绍

但是,您可以

  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, @_ ));
}

请参阅 Tie::Handle 可以覆盖 STDOUT 的行为。

There are a number of built-ins that you can override (see perlsub). However, print is one of the built-ins that doesn't work this way. The difficulties of overriding print are detailed at this perlmonk's thread.

However, you can

  1. Create a package
  2. Tie a handle
  3. Select this handle.

Now, a couple of people have given the basic framework, but it works out kind of like this:

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

You can override printf in the same manner:

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, @_ ));
}

See Tie::Handle for what all you can override of STDOUT's behavior.

随心而道 2024-07-17 21:25:54

您可以使用 Perl 的 select 来重定向 STDOUT。

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

文件句柄可以是任何东西,甚至是到另一个处理日志消息的进程的管道。

PerlIO::teePerlIO::Util 模块似乎允许您“tee”a 的输出文件句柄到多个目的地(例如日志处理器和STDOUT)。

You can use Perl's select to redirect STDOUT.

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

The file handle could be anything, even a pipe to another process that post processes your log messages.

PerlIO::tee in the PerlIO::Util module seems to allows you to 'tee' the output of a file handle to multiple destinations (e.g. log processor and STDOUT).

多情出卖 2024-07-17 21:25:54

有很多选择。 使用 select() 更改打印默认的文件句柄。 或者绑定 STDOUT。 或者重新打开它。 或者给它应用一个IO层。

Lots of choices. Use select() to change the filehandle that print defaults to. Or tie STDOUT. Or reopen it. Or apply an IO layer to it.

玩物 2024-07-17 21:25:54

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

在格式错误的标头发生之前捕获它们...

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

This isn't the answer to your issue but you should be able to adopt the logic for your own use. If not, maybe someone else will find it useful.

Catching malformed headers before they happen...

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;

usage:

use PsychicSTDOUT;
print "content-type: text/html\n\n"; #try commenting out this line
print "<html>\n";
print "</html>\n";
时光匆匆的小流年 2024-07-17 21:25:54

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

You could run the script from a wrapper script that captures the original script's stdout and writes the output somewhere sensible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文