2

我需要使用一些不幸地将诊断消息记录到 STDOUT 和 STDERR 的库。通过使用tie,我可以将这些写入重定向到捕获这些写入的函数。由于我不希望通过绑定句柄捕获我的程序的所有 STDOUT 和 STDERR 输出,因此我只想对某些包执行此操作。

我想出了一个解决方案,其中实际行为是通过查看 caller() 来确定的,如下所示,但我觉得必须有更好的方法......有更优雅的解决方案吗?

package My::Log::Capture;
use strict;
use warnings;
use 5.010;

sub TIEHANDLE {
    my ($class, $channel, $fh, $packages) = @_;
    bless {
        channel => lc $channel,
        fh => $fh,
        packages => $packages,
    }, $class;
}

sub PRINT {
    my $self = shift;
    my $caller = (caller)[0];
    if ($caller ~~ $self->{packages}) {
        local *STDOUT = *STDOUT;
        local *STDERR = *STDERR;
        given ($self->{channel}) {
            when ('stdout') {
                *STDOUT = $self->{fh};
            }
            when ('stderr') {
                *STDERR = $self->{fh};
            }
        }
        # Capturing/Logging code goes here...
    } else {
        $self->{fh}->print(@_);
    }
}

1;

package main;

use My::Foo;
# [...]
use My::Log::Capture;
open my $stderr, '>&', *STDERR;
tie *STDERR, 'My::Log::Capture', (stderr => $stderr, [qw< My::Foo >]);
# My::Foo's STDERR output will be captured, everyone else's STDERR
# output will just be relayed.
4

1 回答 1

2

除了修复库之外,我只能想到一种可能更好的解决方案。

您可以重新打开STDOUT并将STDERR文件句柄转换为您自己的文件句柄。然后,重新打开STDOUTSTDERR使用系好把手。

例如,这是你如何做到的STDOUT

open my $fh, ">&", \*STDOUT or die "cannot reopen STDOUT: $!";
close STDOUT; 

open STDOUT, ">", "/tmp/test.txt"; 

say $fh "foo"; # goes to real STDOUT
say "bar";     # goes to /tmp/test.txt

您可以阅读perldoc -f open以了解有关 ">&" 等功能的所有血腥细节。

无论如何,您可以用绑定文件句柄的设置替换该打开调用,而不是“/tmp/test.txt”。

您的代码必须始终使用显式文件句柄来编写或使用select来切换文件句柄:

select $fh;
say "foo"; # goes to real STDOUT

select STDOUT;
say "bar"; # goes to /tmp/test.txt
于 2012-07-13T12:12:50.943 回答