views:

1141

answers:

5

I've searched the Internet and have found some good solutions for teeing STDOUT to 2 different places. Like to a log file and also to the screen at the same time. Here's one example:

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;

But this solution leaves STDERR going only to the screen and I want STDERR go to both the screen and also to the same log file that STDOUT is being logged to. Is that even possible?

My task is to get my build process logged, but I also want to see it on my IDE's screen as usual. And logging the error messages is just as important as logging the happy messages. And getting the errors logged to a separate log file is not a good solution.

A: 

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).

mobrule
Yeah, I thought of that too. But no matter where in the code I put this line, it does not get the error messages into the log file.
Kurt W. Leucht
+9  A: 

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.

brian d foy
Doesn't Log4Perl require me to print to a filehandle? I need all output redirected because I don't control most of the print statements that are output by the Module::Build process.
Kurt W. Leucht
I did look at Log4Perl, but it didn't seem to answer the mail for me because it required me to print to a filehandle.
Kurt W. Leucht
Well, if it's Module::Build you're talking about, let's fix that problem. Is your question really "how do I capture the output from Module::Build?" That's about all I do lately. You can make a subclass and override the log_* methods to do whatever you like.
brian d foy
With Log4perl, you don't deal with filehandles at the user level. I'm not sure what you were looking at.
brian d foy
Well, I need to capture all the output from my build process, whether it came from Module::Build or whether it came from Test::Perl::Critic or whether it came from some print/die statements that I've sprinkled throughout my various build scripts or whatever. I'm looking for a generic solution that captures all output from both STDOUT and from STDERR and gets it into my build log file, but I also want to see it all on the screen like normal.
Kurt W. Leucht
Well, work as hard as you like then. I'll keep doing it the easier way.
brian d foy
In all examples I can find, they log a message with something like this: $logger->info('this is an info message'); So how do you set up Log4Perl to automatically capture all STDOUT and STDERR output generated from any module where I don't and cannot control the print statements? I don't see anything like that in any of the Log4Perl documentation.
Kurt W. Leucht
Maybe it's not clear that it's not my own code that is generating output that I want to redirect/tee. It's other people's print statements and such that I want to redirect/tee. I've looked at Log4Perl examples and I don't see it answering the mail for this task ... unless I'm missing something? It appears to me to be able to redirect/tee any outputs that I myself want to generate. I'm trying to keep an open mind, but I haven't seen any example Log4Perl code that does what I'm asking. If it can do it, then please show me an example.
Kurt W. Leucht
I don't think it can be any clearer than the answer to "Some module prints messages to STDERR. How can I funnel them to Log::Log4perl?" in the Log4perl FAQ. Read it in the Log4perl distribution.
brian d foy
+5  A: 
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.

Oesor
Wow. That sort of worked. It looks like it logged both the STDOUT and STDERR to both the screen and also to the log file, but it always gives me a "perl.exe Application Error" dialog saying that an unknown software exception occurred.
Kurt W. Leucht
Don't have much practical experience with Windows perl compatibility; I've only used it on linux. Have you tried Strawberry perl vs activestate perl? Do you have cygwin installed?
Oesor
I'm using ActiveState Perl and Eclipse EPIC IDE. No cygwin.
Kurt W. Leucht
+2  A: 

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
spong
That might do the trick ... although I was hoping for a solution that didn't use 3rd party, non-perl utilities ... nor the command line. :-)
Kurt W. Leucht
There's a perl tee here: http://www.robvanderwoude.com/tee.php that might help reduce dependencies :)
spong
+1  A: 

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.

glob
hmmmm. This didn't get the STDERR lines to my log file. Might be that I'm using ActivePerl.
Kurt W. Leucht