Can I send STDOUT and STDERR to a log file and also to the screen in Win32 Perl?

时光怂恿深爱的人放手 提交于 2019-11-29 03:41:58
msandiford

You can redirect stderr to stdout at the windows shell level by doing something like:

perl stuff.pl 2>&1

See support article here for the official word.

Then you could use this stackoverflow answer to do a tee from the shell.

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

I use Log::Log4perl for things like this. It handles sending output to multiple places for you, including the screen, files, databases, or whatever else you like. Once you get even a little bit complex, you shouldn't be doing this stuff on your own.

Instead of printing to filehandles, you just give Log4perl a message and it figures out the rest. I have a short introduction to it in Mastering Perl. It's based on Log4j, and most of the stuff you can do in Log4j you can do in Log4perl, which also means that once you know it, it becomes a transferrable skill.

Oesor
use PerlIO::Util;
*STDOUT->push_layer(tee => ">>/dir/dir/file");
*STDERR->push_layer(tee => ">>/dir/dir/file");

Though I use Log::Dispatch extensively, I've used the above to log what actually got displayed to the screen to a file.

Simply reassign the STDERR filehandle ...

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;

Should mention that I tested this on Windows, it works, however I use StrawberryPerl.

I don't have a windows box to test this on, but perhaps you could do something like making a tied handle which will print to both STDOUT and a log, then redirecting STDOUT and STDERR to it?

EDIT: The only fear I have is the method of storing STDOUT for later use, I have added a second possibility for storing STDOUT for later use should the first not work on Windows. They both work for me on 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 @_;
}
Divyanand

try :

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

All print statements that do not specify a file handle (that is, any regular messages) will send output to the log file. All print statements that use the STDERR file handle (that is, all errors) will send output to both the console and the log file.

So you want STDERR to behave like STDOUT, going to both the screen and the same log file? Can you just dup STDERR with

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

(I don't know offhand whether you would do this before or after the call to IO::tee->new).

I wrote a minimalistic perl logger with configurable dynamic logging giving you the following 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__
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!