【问题标题】:Can I send STDOUT and STDERR to a log file and also to the screen in Win32 Perl?我可以将 STDOUT 和 STDERR 发送到日志文件以及 Win32 Perl 中的屏幕吗?
【发布时间】:2010-12-04 16:33:02
【问题描述】:

我在 Internet 上进行了搜索,并找到了一些将 STDOUT 发球到 2 个不同地方的好解决方案。喜欢日志文件,同时也喜欢屏幕。这是一个例子:

use IO::Tee;
my $log_filename = "log.txt";
my $log_filehandle;
open( $log_filehandle, '>>', $log_filename )
  or die("Can't open $log_filename for append: $!");
my $tee = IO::Tee->new( $log_filehandle, \*STDOUT );
select $tee;

但是这个解决方案让 STDERR 只进入屏幕,我希望 STDERR 进入屏幕和 STDOUT 被记录到的同一个日志文件。这可能吗?

我的任务是记录我的构建过程,但我也想像往常一样在我的 IDE 屏幕上看到它。记录错误消息与记录快乐消息一样重要。并且将错误记录到单独的日志文件中并不是一个好的解决方案。

【问题讨论】:

    标签: perl logging redirect stdout stderr


    【解决方案1】:

    所以你想让STDERR 表现得像STDOUT,同时进入屏幕和同一个日志文件?你能用 dup STDERR 吗?

    open(STDERR, ">&STDOUT") or warn "failed to dup STDOUT:$!";
    

    (我不知道您是否会在致电IO::tee->new 之前或之后这样做)。

    【讨论】:

    • 是的,我也想过。但是无论我将这一行放在代码中的哪个位置,它都不会将错误消息写入日志文件。
    【解决方案2】:

    我使用Log::Log4perl 来处理这样的事情。它为您处理将输出发送到多个位置,包括屏幕、文件、数据库或您喜欢的任何其他位置。一旦你变得有点复杂,你不应该自己做这些事情。

    您只需给 Log4perl 一条消息,它就会计算出其余的,而不是打印到文件句柄。我在Mastering Perl 中有一个简短的介绍。它基于 Log4j,而您在 Log4j 中可以做的大部分事情都可以在 Log4perl 中完成,这也意味着一旦您了解它,它就成为一种可转移的技能。

    【讨论】:

    • Log4Perl 不要求我打印到文件句柄吗?我需要重定向所有输出,因为我不控制 Module::Build 进程输出的大部分打印语句。
    • 我确实看过 Log4Perl,但它似乎没有为我回复邮件,因为它需要我打印到文件句柄。
    • 好吧,如果你说的是 Module::Build,我们来解决这个问题。您的问题真的是“我如何从 Module::Build 捕获输出?”这就是我最近所做的一切。你可以创建一个子类并覆盖 log_* 方法来做任何你喜欢的事情。
    • 使用 Log4perl,您无需在用户级别处理文件句柄。我不确定你在看什么。
    • 我认为这比“某些模块将消息打印到 STDERR。我如何将它们汇集到 Log::Log4perl?”的答案更清楚了。在 Log4perl 常见问题解答中。在 Log4perl 发行版中阅读它。
    【解决方案3】:
    use PerlIO::Util;
    *STDOUT->push_layer(tee => ">>/dir/dir/file");
    *STDERR->push_layer(tee => ">>/dir/dir/file");
    

    虽然我广泛使用Log::Dispatch,但我已经使用上述方法将实际显示在屏幕上的内容记录到文件中。

    【讨论】:

    • 哇。那种工作。看起来它同时将 STDOUT 和 STDERR 记录到屏幕和日志文件中,但它总是给我一个“perl.exe 应用程序错误”对话框,说发生了未知的软件异常。
    • 对Windows perl兼容性没有太多实践经验;我只在linux上使用过。您是否尝试过草莓 perl 与 activestate perl?你安装了cygwin吗?
    • 我正在使用 ActiveState Perl 和 Eclipse EPIC IDE。没有cygwin。
    • 看来Ubuntu没有PerlIO::Utildeb包:-(
    【解决方案4】:

    您可以通过执行以下操作在 Windows shell 级别将 stderr 重定向到 stdout

    perl stuff.pl 2>&1
    

    官方说法见support article here

    然后你可以使用this stackoverflow answer 在shell 中执行tee

    perl stuff.pl 2>&1 | tee stuff.txt
    

    【讨论】:

    • 这可能会奏效......虽然我希望有一个不使用第 3 方、非 perl 实用程序......也不使用命令行的解决方案。 :-)
    • 这里有一个 perl tee:robvanderwoude.com/tee.php 这可能有助于减少依赖关系:)
    • 我看过它写的“永远不要相信包含print $_;的Perl脚本”
    【解决方案5】:

    只需重新分配 STDERR 文件句柄 ...

    use IO::Tee;
    my $log_filename = "log.txt";
    my $log_filehandle;
    open( $log_filehandle, '>>', $log_filename )
      or die("Can't open $log_filename for append: $!");
    my $tee = IO::Tee->new( $log_filehandle, \*STDOUT );
    *STDERR = *$tee{IO};
    select $tee;
    

    应该提到我在 Windows 上测试过,它可以工作,但是我使用的是 StrawberryPerl。

    【讨论】:

    • 嗯。这没有将 STDERR 行添加到我的日志文件中。可能是我正在使用 ActivePerl。
    • 像魅力一样工作,STDERR 也一样。这个解决方案应该在恕我直言。也很积极:IO::Tee 是为 Ubuntu 打包的,对在这些系统上的部署有很大帮助。
    【解决方案6】:

    我写了一个简约的perl logger,带有可配置的动态日志记录,为您提供以下 API:

            use strict ; use warnings ; use Exporter;
            use Configurator ; 
            use Logger ; 
    
    
            #   anonymous hash !!!
            our $confHolder = () ; 
    
            sub main {
    
                    # strip the remote path and keep the bare name
                    $0=~m/^(.*)(\\|\/)(.*)\.([a-z]*)/; 
                    my $MyBareName = $3; 
                    my $RunDir= $1 ; 
    
                    # create the configurator object 
                    my $objConfigurator = new Configurator($RunDir , $MyBareName ); 
                    # get the hash having the vars 
                    $confHolder = $objConfigurator ->getConfHolder () ; 
                    # pring the hash vars 
                    print $objConfigurator->dumpIni();  
    
                    my $objLogger = new Logger (\$confHolder) ; 
                    $objLogger->LogMsg  (   " START MAIN " ) ;  
    
                    $objLogger->LogMsg  (   "my \$RunDir is $RunDir" ) ; 
                    $objLogger->LogMsg  (   "this is a simple message" ) ; 
                    $objLogger->LogErrorMsg (   "This is an error message " ) ; 
                    $objLogger->LogWarningMsg   (   "This is a warning message " ) ; 
                    $objLogger->LogInfoMsg  (   "This is a info message " ) ; 
                    $objLogger->LogDebugMsg (   "This is a debug message " ) ; 
                    $objLogger->LogTraceMsg (   "This is a trace message " ) ; 
                    $objLogger->LogMsg  (   "using the following log file " .  "$confHolder->{'LogFile'}" ) ; 
                    $objLogger->LogMsg  (   " STOP MAIN \n\n" ) ; 
    
            } #eof main 
    
    
    
            #Action !!!
            main(); 
    
            1 ; 
    
            __END__
    

    【讨论】:

      【解决方案7】:

      我没有 Windows 框来测试它,但也许你可以做一些事情,比如制作一个绑定的句柄,它将打印到 STDOUT 和日志,然后将 STDOUT 和 STDERR 重定向到它?

      编辑:我唯一担心的是存储 STDOUT 以供以后使用的方法,如果第一种在 Windows 上不起作用,我添加了第二种可能性来存储 STDOUT 以供以后使用。它们都在 Linux 上为我工作。

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      
      tie *NEWOUT, 'MyHandle', 'test.log';
      *STDOUT = *NEWOUT;
      *STDERR = *NEWOUT;
      
      print "Print\n";
      warn "Warn\n";
      
      package MyHandle;
      
      sub TIEHANDLE {
        my $class = shift;
        my $filename = shift;
      
        open my $fh, '>', $filename or die "Could not open file $filename";
      
        ## Use one of these next two lines to store STDOUT for later use.
        ## Both work for me on Linux, if one does not work on Windows try the other.
        open(OLDSTDOUT, '>&STDOUT') or die "Could not store STDOUT";
        #*OLDSTDOUT = *STDOUT;
      
        my $self = {
          loghandle => $fh,
          logfilename => $filename,
          stdout => \*OLDSTDOUT,
        };
      
        bless $self, $class;
      
        return $self;
      }
      
      sub PRINT {
        my $self = shift;
        my $log = $self->{loghandle};
        my $stdout = $self->{stdout};
        print $log @_;
        print $stdout @_;
      }
      

      【讨论】:

        【解决方案8】:

        试试:

        my logfh;
        my $logfn = "some/path/to/file.log";
        open ($logfh, '>',$logfn ) or die "Error opening logfile $logfn\n";
        my $tee = IO::Tee->new( $logfh);
        my $tee2 = IO::Tee->new( $logfh, \*STDOUT );
        # all naked print statements will send output to log file
        select($tee);
        # all STDERR print statements will send output to console
        *STDERR = *$tee2{IO};
        

        所有未指定文件句柄的打印语句(即任何常规消息)都会将输出发送到日志文件。所有使用 STDERR 文件句柄的打印语句(即所有错误)都会将输出发送到控制台和日志文件。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-03-29
          • 1970-01-01
          • 1970-01-01
          • 2022-01-18
          • 2020-05-10
          • 1970-01-01
          • 2017-05-24
          相关资源
          最近更新 更多