Mac has asked for the wisdom of the Perl Monks concerning the following question:

Hi all, I need some pointers or help. Globally, the idea is to take everything thats printed to stdout, and print it to a log aswell - so it logs all the output from the program.

The script I am writing is a database query, so along with printing to the screen, I'd like to also output to a file which 'logs' their session.

I realise there are idiots ways to do this:

open file handle
print to stdout
print same text to file handle


# Session closes

close filehandle


Is there a perl module that already does this? ie, intercepts stdout and copies it to a defined file handle?

Also, if not related, could someone explain select(FILEHANDLE) and $| = 1 to me? Thanks.

Replies are listed 'Best First'.
Re (tilly) 1: printing and logging to a file
by tilly (Archbishop) on Jan 09, 2002 at 03:57 UTC
    You can use btrott's Filter::Handle. (Untested code ahead.)
    use Filter::Handle; my $logfile = "program.log"; open(LOG, ">", $logfile) or die "Cannot write to '$logfile': $!"; Filter( \*STDOUT, sub { print LOG @_; return @_; } );
    Note the gotchas documented in the module involving system calls, xsubs, etc due to how tie works in Perl. Another solution is to pipe your output to a program that does your log and STDOUT. On Unix the "tee" program would usually be chosen for this. Solutions involving passing data to an external program do not suffer from the limitations above.

    UPDATE
    To answer the second question, I suggest reading Suffering from Buffering by Dominus.

Re: printing and logging to a file
by FoxtrotUniform (Prior) on Jan 09, 2002 at 03:59 UTC
(crazyinsomniac) Re: printing and logging to a file
by crazyinsomniac (Prior) on Jan 09, 2002 at 12:40 UTC
Re: printing and logging to a file
by particle (Vicar) on Jan 09, 2002 at 04:02 UTC
    well, i don't know anything about a module for printing to multiple streams. if you're on a unix system, you could use a system call to 'tee.'

    i rolled my own sub a long time ago, when i was pretty new to perl, and wanted the excersize. i dug it up for you:

    #! /usr/local/bin/perl -w use strict; use FileHandle; use Getopt::Std; use vars qw($opt_n); die( "Error: No arguments passed.\n" ) unless( $#ARGV >= 0 ); my $text = pop @ARGV; die("no opts\n") unless getopts('n'); unless ( $opt_n ) { print $text."\n"; } while ($ARGV[0]) { $_ = shift; my $FH = new FileHandle ">>$_" or die( "oops! $!\n" ); print $FH $text."\n"; close( $FH ); }
    i used it like so:
    mprint($::AUDITLOG, $::BUILDLOG, " ** dbcfg.tmp file in $::BUILDPATH n +ot found * Run Aborted!!!");

    ~Particle

Re: printing and logging to a file
by Matts (Deacon) on Jan 09, 2002 at 15:00 UTC
    Check out Log::Dispatch::Config - after all, you never know when you're going to need something more than just STDOUT and a log file. Some day you might want Syslog too.
Re: printing and logging to a file
by Mac (Acolyte) on Jan 09, 2002 at 04:36 UTC
    Thanks for the responses - the Suffering from Buffering is a great read.

    I have a question regarding select - doesn't all STDOUT go to the selected file handle unless otherwise stated?

    for example:

    
    #!/usr/local/bin/perl
    
    open (OUT, ">/tmp/out.file");
    
    $fh = select (OUT);
    $| = 1;
    select($fh);
    
    print STDOUT "Hello!\n"; # prints to stdout
    print "Hello!\n";        # prints to $fh = /tmp/out.file as no filehandle has been stated
    
    
    So, in the above example, we have selected a filehandle - Am I correct in believing that unless explicitly stated, all output will be directed to OUT - the selected filehandle?
      The return value from select() is the previously selected filehandle. Therefore $fh is actually STDOUT (i.e. the filehandle that was hot before the select), so when you select($fh) you are back with STDOUT as your Currently-Selected-Filehandle. Does that make sense?

      -Blake

Re: printing and logging to a file
by mr_mischief (Monsignor) on Jan 09, 2002 at 05:41 UTC
    Not the greatest invention of mankind, nor even as handy as IO::Tee probably, but this works pretty well for the amount of typing and thought I put into it:
    open my $foo, ">>bar" || die "Can't write to bar : $!\n"; sub print2 { my $fh = shift; print STDOUT "$_[0]"; print $fh "$_[0]"; } ### needs a comma, unlike print() print2 $foo, "Hello, world!\n";
    Still, IO::Tee is built for what you want to do.

    As for your other questions, the probability that someone will learn from answers about them is higher if they are in their own thread. So, if you post those in their own node, they're also more likely to be answered. :-)