Reputation: 1272
I would like to copy stdout and stderr of my Perl script to a file, while retaining it also onscreen, and preferrably using some trick inside the script itself. I.e. I want something similar to
./test.pl 2>&1 | tee foo.bar
but hidden inside the perl script implementation. For the moment I've just written a subroutine which prints all messages both onscreen and to a filehandle, but the drawback is that if the script dies, the die message will not appear in the log. Is there a way to do it?
Upvotes: 2
Views: 4697
Reputation: 129529
Please see section "13.15.9. Tie Example: Multiple Sink Filehandles" in the Perl cookbook.
The main thing to know is tie *TEE, "Tie::Tee", @handles;
NOTE: Tie::Tee package is something you need to create yourself, the code is in that same section in the book.
NOTE: you can then do select(TEE)
and it will be used in every regular print
statement, so you don't need to do print TEE xxx
.
NOTE: To have any STDERRy output (including die) go to the same filehandle, change the STDERR to TEE like this:
use Tie::Tee;
use Symbol;
@handles = (*STDOUT);
push(@handles, $handle = gensym( ));
open($handle, ">/tmp/teetest.xxx");
tie *TEE, "Tie::Tee", @handles;
select(TEE);
*STDERR = *TEE;
print "raw print\n";
die "XXXX\n";
The output will be:
raw print
XXXX
And the file contents will be:
raw print
XXXX
Upvotes: 7
Reputation: 255
Depending on the size of your script, I highly recommend Log::Log4perl ... saves reimplementing logging every time you write a script.
If you use Log4perl in easy mode, it only adds about 10 lines or so of code and is very useful.
Log4perl is extremely flexible in redirecting output to almost anywhere you want, including multiple outputs at once.
Upvotes: 1
Reputation: 11933
Take a look at Log::Dispatch
Dispatches messages to one or more outputs.
use Log::Dispatch;
# Simple API
#
my $log =
Log::Dispatch->new
( outputs =>
[ [ 'File', min_level => 'debug', filename => 'logfile' ],
[ 'Screen', min_level => 'warning' ],
],
);
$log->info('Blah, blah');
# More verbose API
#
my $log = Log::Dispatch->new();
$log->add( Log::Dispatch::File->new
( name => 'file1',
min_level => 'debug',
filename => 'logfile'
)
);
$log->add( Log::Dispatch::Screen->new
( name => 'screen',
min_level => 'warning',
)
);
$log->log( level => 'info', message => 'Blah, blah' );
my $sub = sub { my %p = @_; return reverse $p{message}; };
my $reversing_dispatcher = Log::Dispatch->new( callbacks => $sub );
It has a few helper/utility modules you should look at too.
Log::Dispatch::DBI - Log output to a database table.
Log::Dispatch::FileRotate - Rotates log files periodically as part of its usage.
Log::Dispatch::File::Stamped - Stamps log files with date and time information.
Log::Dispatch::Jabber - Logs messages via Jabber.
Log::Dispatch::Tk - Logs messages to a Tk window.
Log::Dispatch::Win32EventLog - Logs messages to the Windows event log.
Log::Dispatch::Config - Allows configuration of logging via a text file similar (or so I'm told) to how it is done with log4j.
Upvotes: 4
Reputation: 191
This sounds like just what you need, unfortunately it's a bash script and you would like this all to be handled in your perl code.
Upvotes: -1