Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Re (tilly) 3: Seeking Feed back

by tilly (Archbishop)
on Jul 05, 2001 at 15:58 UTC ( [id://94064]=note: print w/replies, xml ) Need Help??


in reply to Re: Re: Seeking Feed back
in thread Seeking Feedback on Chat Program (was Seeking Feed back)

And so you have an error, you know that there is no such file or directory. What then? How does this help in debugging.

As perlstyle says:

o Always check the return codes of system calls. Good error messages should go to STDERR, include which program caused the problem, what the failed system call and arguments were, and (VERY IMPORTANT) should contain the standard system error message for what went wrong. Here's a simple but sufficient example: opendir(D, $dir) or die "can't opendir $dir: $!";
By not including debugging information on your arguments you will make such basic things as a renamed directory painful to track down.

Replies are listed 'Best First'.
Re: Re (tilly) 3: Seeking Feed back
by azatoth (Curate) on Jul 05, 2001 at 16:20 UTC

    And as a side-note, something I learned the other day for error handling in system calls :

    system("/usr/bin/scp $file $username\@$host:$remoteFile"); die "System call to scp command failed! : $!\n" if ($? != 0);

    The special variable $? returns a boolean value for success or fail on system calls. So the above snippet performs a reliable "or die" check on your command. A handy one to know, especially as system tends to be a little unreliable in terms of error checking / return values when using a straight die, IMHO.

    Update: as per comments below, not a "boolean" in the strict sense of the word. Call it an "azatoth boolean" :)

    Azatoth a.k.a Captain Whiplash

    Make Your Die Messages Full of Wisdom!
    Get YOUR PerlMonks Stagename here!
    Want to speak like a Londoner?
      Actually $? is not a boolean. As documented in perlvar, it is the return code of the last system call. That is 0 for success, and all else is an error. If the error is under 256, then it is a system error. If the error is over, then divide by 256 and you have the program's return code. The return code of your programs are set by exit, are 0 by default, and are set from $? on a die.

      Unfortunately that means that every program has its own possible meanings for exit codes. Traditionally, however, 1 means "Error, but not an important one" and 2 means "Bad error". For instance rm on my system will return 1 if you don't give it any files to delete, but a 2 if you try to delete something and it can't.

      Nice. I think this could be done even simpler and more readable using unless instead of if !=:
      system("/usr/bin/scp $file $username\@$host:$remoteFile"); die "System call to scp command failed! : $!\n" unless ($?);

      pmas

      To make errors is human. But to make million errors per second, you need a computer.

Re: Re (tilly) 3: Seeking Feed back
by orkysoft (Friar) on Jul 05, 2001 at 16:53 UTC

    Well, my CGI program was installed in a controlled environment, where errors should be very rare, and users were generally _very_ clueless. I can't just have my program die on them! Instead, my program would skip the file operation in question, but log an error, and display an error message with a possible solution, print out the rest of the page layout, and then end at the same spot the program would normally.

    Maybe it's a matter of taste, but I just don't like the or die construct, it doesn't give me the ability to neatly and readably handle the exception.

    Example:

    sub display_values_from_file { my $filename = shift; if(open FILE, "<$filename") { # check return value of open flock FILE, 1; while(<FILE>) { print &process_line_from_file; } close FILE; } else { print "The file could not be opened. Please try again later!"; log_error "open $filename : $!"; } } # main program ... # do something print $some_nice_html; display_values_from_file "/foo/bar.baz"; print $rest_of_html_page; # end

    This way, when an error occurs, the user will be informed of the error, and all the links the user would need would still be there.

      I was not commenting on the error being silent or not.

      I am commenting that your initial version failed to log critical information that would make it possible to debug the problem later. And no matter how controlled the environment may be, you are not going to convince me that you won't ever have bugs to track down.

      I am glad that your current example does that better. It does not do it well enough to make me happy, but it is better.

      Here is a better version of the same thing that you have:

      sub ret_read_fh { my $filename = shift; my $fh = do {local *FH}; unless(open($fh, "< $filename")) { log_error("open '$filename': $!"); return; } unless(flock($fh, 1)) { log_error("flock '$filename': $!"); return; } return $fh; } sub display_values_from_file { my $fh = ret_read_fh(shift); if ($fh) { while(<$fh>) { print process_line_from_file($_); } } else { print "Error opening file. Please try again later!"; } }
      Why better? Well suppose that some bright sysadmin gets the idea of having your data mounted from another machine over NFS. (This is not an unreasonable thing for a sysadmin to have reason to do.) With the revised version you will get notified that flock no longer works. With the original version the error would be silent until someone noticed sporadic loss of data over 6 months and thought to wonder why. (At which point you read your code and have no a clue where it could fail.)

      My points?

      1. Don't trust system calls to succeed.
      2. Make sure that when they go wrong you have enough information to debug.
      3. Realistically you have a choice. Make errors fatal, or else encapsulate code which needs to handle errors. After all when you find a new possible cause of errors you do not want handling it to be harder than necessary.
      Also a middle ground to consider is to make errors be fatal, but make sections with fatal error handling be trapped by an eval block. That makes breaking out on large numbers of possible errors easy to implement, while not interfering with your ability to handle the exception for the user.

        I agree with your NFS/flock point. That could still go wrong if there were to be an NFS system (or a Windows machine for that matter).

        (My 'initial version' was just something that looked like the code I used. I might have forgotten to log the actual error message there, but I wasn't coding 'literal' there.)

        But apart from that and your localised filehandle, your code looks very much like mine, with just some inverted logical structures.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://94064]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (4)
As of 2024-04-25 09:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found