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

ライブラリを修正する以外に、私はより良いかもしれない唯一の解決策を考えることができます。

ハンドルを再度開いSTDOUTSTDERR、独自のファイルハンドルにファイルすることができます。次に、再び開いSTDOUTて、STDERRハンドルを結びます。

たとえば、次のようにします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 に答える