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

I'm writing a small program that's run by cron, and want to redirect it's STDERR to my log file. I'm trying this:
open MY_LOG, ">> /var/log/mylog.log" or die "Gah! $!"; open STDERR, ">&MY_LOG" or die "Couldn't redirect. $!";
and it seems to work.

I've also seen this funny notation that seems to do the same thing:

*STDERR = *MY_LOG;

What's that star-filehandle notation mean? Is it really doing the same thing as open STDERR, ">&MY_LOG"?

Finally, how to do I do $| = 1 on MY_LOG, so the STDERR messages and the messages printed to MY_LOG end up in the right order in the log file?

Replies are listed 'Best First'.
Re: redirect STDERR to my log file, also funny *FOO notation.
by chromatic (Archbishop) on Nov 14, 2005 at 20:51 UTC

    The funny star notation copies the contents of the one typeglob into another. Perl uses typeglobs to hold global variables -- such as filehandles. It's more or less the same thing as duping the filehandle as with your open call. (It has the same effect on the filehandle, but it copies the other slots of the typeglob. Don't worry if you don't understand that; it probably has no effect on your program.)

    Unless you're printing to the log file from multiple processes or you need the data RIGHT NOW, don't worry about unbuffering it. If you do need to unbuffer it, select the filehandle, then set $|, then select the previously-selected filehandle -- or use a module such as IO::Handle.

      > The funny star notation copies the contents of the one
      > typeglob into another.

      Ah. I hadn't read much about typeglobs. Actually, the first time I saw the word "typeglob" and saw a *, I thought "fileglob". Also, a * outside of a string makes me think "dereference", but it looks like Perl thinks of it much like any other sigil.

      > don't worry about unbuffering it.

      Oh, ok. I think I see. The output to either filehandle (MY_LOG or STDERR) will stay in the same order just fine. I suppose that, after I've written a few lines to MY_LOG, if I then write to STDERR, the buffer will get flushed at that point anyway.

Re: redirect STDERR to my log file, also funny *FOO notation.
by Zaxo (Archbishop) on Nov 14, 2005 at 21:29 UTC

    Another way to do what you want is to localize STDERR in the scope where you want logging. The trick there is to organize your code so that where you want logging coincides with a scope.

    { open local(*STDERR), '>> /var/log/mylog.log' or die 'Gah! ', $!; # or die 'Gah! ', $/; # Oops, typo # do stuff }

    Update: Localizing STDERR does two things.

    1. It confines the new global STDERR handle to the dynamic scope. The old one is automatically restored when the scope is exited. That is the reason for the enclosing curly brackets, to create a scope.
    2. It shares the new temporary STDERR seamlessly. Functions called within the dynamic scope will magically honor logging of STDERR without any need to anticipate it in their definitions.
    As I suggested, the trick is to isolate the code you want to log, and only that code, in the scope.

    After Compline,
    Zaxo

      Why not just
      open STDERR, '>> /var/log/mylog.log'
      instead of
      open local(*STDERR), '>> /var/log/mylog.log'
      ?

      Wait a sec... why did you sneak that $/ in (input record separator) at the end of the call to die()?

Re: redirect STDERR to my log file, also funny *FOO notation.
by chrism01 (Friar) on Nov 15, 2005 at 00:42 UTC
    This is what I use:
    # Get filename components ($filename, $filedir, $filext) = fileparse($0, '\..*'); # Create filename; note the ./log sub-dir $logfile = "${filedir}log/$filename.log"; # Redirect STDOUT & STDERR to named logfile open( STDOUT, ">>$logfile") or die "\nUnable to open STDOUT -> $logfile: $!\n +"; open( STDERR, ">>&STDOUT"); # Set autoflush on select (STDERR); $| = 1; select (STDOUT); $| = 1;
    Works fine for me.
    The reason I unbuffer the log file is that if the prog exits unexpectedly, you may not get the last msg or 2, which are usually the ones that tell you why/where it failed(!)
    Cheers
    Chris

      > select (STDERR); $| = 1;

      Isn't STDERR already unbuffered by default?

        Yes, but it doesn't hurt to make sure! _________________________________________________________________________________
        Without me, it's just aweso