21

これがシナリオです。大量のレガシー スクリプトがあり、すべてが共通のライブラリを使用しています。上記のスクリプトは、診断出力に「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;
    }
}

(「実際にログに記録すべきもの」、「print を診断に使用すべきではない」、「perl はひどい」、「この例には xy と z に欠陥がある」などの問題は避けたいと思います...これは簡潔さと明確さのために大幅に簡略化されています。)

基本的な問題は、print に渡されたデータをキャプチャして処理することです (または、これらの推論に沿った任意の perl ビルトイン)。出来ますか?きれいにする方法はありますか?それを可能にするフックを備えたログモジュールはありますか? それとも、ペストのように避けるべきものであり、印刷出力をキャプチャして処理することをあきらめるべきですか?

追加: これは、クロスプラットフォームで実行する必要があります (Windows と *nix の両方)。スクリプトを実行するプロセスは、スクリプトからの出力と同様に、同じままにする必要があります。

追加の追加:codelogicの回答のコメントで行われた興味深い提案:

http://perldoc.perl.org/IO/Handle.htmlをサブクラス化し、ロギング作業を行う独自のファイル ハンドルを作成できます。– カミル・キシエル

これでうまくいくかもしれませんが、2 つの注意点があります。

1) この機能を共通ライブラリを使用する人にエクスポートする方法が必要です。STDOUT とおそらく STDERR にも自動的に適用する必要があります。

2) IO::Handle のドキュメントには、サブクラス化できないと書かれており、これまでの私の試みは無駄でした。サブクラス化 IO::Handle を機能させるために特別に必要なものはありますか? 標準の 'use base 'IO::Handle' を使用してから、new/print メソッドをオーバーライドしても何も起こらないようです。

最終編集: IO::Handle は行き止まりのように見えますが、Tie::Handle はそれを行う可能性があります。すべての提案に感謝します。彼らはすべて本当に良いです。Tie::Handle ルートを試してみます。問題が発生した場合は、戻ってきます。

補遺: これを少し使ってみたところ、何もトリッキーなことをしなければ Tie::Handle が機能することがわかりました。結合された STDOUT または STDERR で IO::Handle の機能のいずれかを使用する場合、基本的にそれらを確実に動作させるためのクラップシュートです。扱う。ハンドルを結ぶ前に自動フラッシュを有効にすると、機能します。それがうまくいく場合は、 Tie::Handle ルートが受け入れられるかもしれません。

4

5 に答える 5

25

オーバーライドできるビルトインがいくつかあります ( perlsubを参照)。ただし、printこの方法では機能しない組み込みの 1 つです。オーバーライドの難し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, @_ ));
}

STDOUT の動作をオーバーライドできるものについては、 Tie::Handleを参照してください。

于 2008-12-23T04:57:59.110 に答える
9

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 ::UtilモジュールのPerlIO ::teeを使用すると、ファイル ハンドルの出力を複数の宛先 (ログ プロセッサや STDOUT など) に「tee」できるようです。

于 2008-12-22T23:29:46.637 に答える
7

たくさんの選択肢。select() を使用して、出力のデフォルトのファイルハンドルを変更します。または、STDOUT を結びます。または、開き直ってください。または、それに IO レイヤーを適用します。

于 2008-12-23T01:57:32.407 に答える
3

これはあなたの問題に対する答えではありませんが、あなたはあなた自身の使用のために論理を採用することができるはずです。そうでない場合は、他の誰かがそれを役立つと思うかもしれません。

不正な形式のヘッダーを発生する前にキャッチしています...

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";
于 2011-06-08T15:51:04.003 に答える
-1

元のスクリプトの stdout をキャプチャし、適切な場所に出力を書き込むラッパー スクリプトからスクリプトを実行できます。

于 2008-12-22T23:37:51.643 に答える