Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Style guide for error messages?

by xdg (Monsignor)
on Sep 21, 2005 at 19:53 UTC ( #493945=perlmeditation: print w/ replies, xml ) Need Help??

Recently, as I was writing error messages for various instances of carp, croak, warn and die, I realized that -- unlike with my code -- I've never quite settled into a consistent, personal "style" for phrasing and formatting error messages.

Some of this, I think, is a result of the error messages being something of an afterthought -- a sort of, "oh, yeah, I should throw an error if that fails" postcript, where once the die statement has hit the screen, I want to get back to coding and the error message winds up being whatever quickly comes to mind.

As I'm trying to more regularly practice test-first development, I'm trying to moderate the habit of sprinkling error handling willy-nilly as I code unless I actually document the need for it in a test script first. Or, I'm quickly coming back after a Devel::Cover run and filling in test scripts for all the untriggered error handlers.

In writing the test scripts, actually thinking about how my code should behave with an exception has me thinking more about what are good practices and good style. I thought I'd throw out a few potential ideas for a style guide to jumpstart a wider discussion:

  • Target the error message at the source of the error

    • Use carp and croak for messages about parameters or other user input
    • Use warn and die for unexpected system behavior or failures that "shouldn't happen"

  • Provide the line number if the error condition came from the code or a system failure to help with debugging (i.e. don't put "\n" on the message)

  • Don't provide the line number if the error came from runtime input (i.e. end the message with "\n")

  • Be consistent in whatever style is chosen. E.g. All fatal errors should start with "Error:" or "Fatal Error:"; all warnings should start with "Warning:"

  • Start the message with a brief description of the result of the error

  • Include the cause of the error by appending $! or $@ as appropriate

If a style guide was in use, a template could be used with throws_ok from Test::Exception to ensure some minimal conformity, without necessarily (unless desired) needing to check the entire error string. E.g.:

throws_ok { some_function() } qr{\AError: .+}, "some_function() error +handling";

What best practices have you found for error messages? Does anyone have a particular style guide within their company or team? Other than die mentioning that adding ", stopped" can help readability, I've not seen much by way of concrete recommendations.

-xdg

Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Comment on Style guide for error messages?
Select or Download Code
Re: Style guide for error messages?
by ambrus (Abbot) on Sep 21, 2005 at 21:35 UTC

    My practices are these.

    I always make sure that it would be clear from the error message that it's an error. I don't use a single prefix, just include one some word like qw"error cannot failed failure". It can be very confusing if you write

    read $FOO, $s, $l or do { warn "reading foo file"; last PROCESS_FOO; }
    because it will seem to be a status message saying the program is starting to read the foo file.

    I always include $! if its value is meaningful (that is, if the function that fails sets it).

    I always provide the implicit line number (although I almost always ignore it when I read the error message). The exception is some status messages that are not errors.

    I don't capitalize the error message.

    I sometimes give details about the error, sometimes not. I add the details in the die statement if the error actually happens and I don't understand why.

    If you want to know more about my style, here is the complete (ok, I could have missed some) list of error statements in two of my programs.

Re: Style guide for error messages?
by danb (Friar) on Sep 21, 2005 at 22:21 UTC

    If you use something like Log::Log4Perl, then it can do some of the things you mentioned automatically (prefixes, line numbers).

    Where appropriate, I think that hints should be included in the error messages. For example, Error: Incorrect password.  Hint: Are you sure your Caps Lock is turned off?

    --Dan

Re: Style guide for error messages?
by rir (Vicar) on Sep 21, 2005 at 22:22 UTC
    Perl Best Practices covers this issue well. Objects are the thing to throw.

    I disagree with some of your ideas:

    • Warnings should be meaningful. A warning that needs no action can be ignored, then will be ignored, then just becomes useless verbiage. Make it an error or silence it.
    • "Fatal errors" are a self defining group. You don't need to tag them so. Update: I wish I hadn't said that. It is true, but if you know your exception should be fatal you should capture that information in the exception. One just needs to be careful of unwarranted assumptions about the calling environment.
    • Always provide the line number of the error and/or the caller. If some part of the code doesn't handle input correctly, you will need to know which code that is.

    Like Best Practices I would suggest using objects, in your first pass use the object type and the long output of a bare croak, in your message clean up phase use another method to output the exceptions' error strings. Since these methods or the table they use doesn't exist the brokenness forces the fix; examining your type tree gives you a list of all your errors and their meanings then you just need express them. This implies handler objects or another way of consolidating your error handling code.

    You may complain that I've only moved your problem to the naming of exception packages. I won't argue.

    Be well,
    rir

      I agree on objects being the thing the throw. (c.f. Exception::Class and my own Exception::Class::TryCatch)

      However, even the object carries with it some message that may wind up in front of the user. But if I understand your argument, you're suggesting never bothering to add any error string at the time of the croak -- just the default system output -- and using the description defined in the Exception::Class object as the message instead. That does just push the problem upstream, as it were, but at least it's done only once, and at a time when more thought can be devoted to crafting the message (and it can be parameterized with fields as necessary). That's a good idea. ++

      On the other hand, I think it requires overriding the as_string function to stringify as the description plus the right field(s) in case it isn't caught. Lots of up front work, but on a large project, definitely less headache in the long run. E.g., a very brief, quick example:

      use Exception::Class ( 'FileOpenError' => { isa => 'Exception::Class::Base', description => "Error: couldn't open the file called ", fields => [ 'filename' ], alias => 'throw_file_open_error', }, ); BEGIN { # minimal stringification *FileOpenError::as_string = sub { my $self = shift; return $self->description() . $self->filename(); } } # later... open my $fh, "<", $filename or throw_file_open_error( filename => $filename );

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      I seldomly include the line number in my error messages. The vast majority of my usage of 'die' is after a system command:
      open(...) or die(...); chmod(...) or die(...);
      If I can't open a file for some reason (it doesn't exist, I don't have permission, whatever), all I care about is which file did the program try to open, and why can't it open the file. It doesn't matter whether it tried to open the file on line 23, or on line 389 - dies not being caught by eval will report to the user, and should contain only information that's relevant to the user. Line numbers aren't.

      The second largest group of die usage are meant as exceptions. Again, line numbers are useless - the die is supposed to be catched (or escalated to a real die), and I need an informative message or datastructure. That's what I'll inspect in $@ - not a line number.

      I might want to know line-numbers is during debugging. But croak() already gives a stack-trace, and that is often already enough.

      The compiler emitting line-number on syntax error, now that's a useful use of line numbers.

      And I disagree with Perl Best Practices.

      The advice in PBP is designed to be one fairly good way to do things. It is not the only possible one, and it is not necessarily the best for all circumstances.

      I agree that if you attempt to use exceptions as a flow of control, then it is much better to throw objects than strings. But I'd prefer that exceptions generally be fatal, and would like to discourage trying to use the exception system for complex flow of control. Which means that all of TheDamian's arguments for exception objects are arguments for how to better do what I'd like people not to do.

      This is not to say that PBP is wrong. It is correct that it provides an internally consistent set of best practices. But it is not the only possible one, and it is not my preferred one.

        This is not to say that PBP is wrong. It is correct that it provides an internally consistent set of best practices. But it is not the only possible one, and it is not my preferred one.
        So, naturally, enquiring minds now want to know what you prefer instead! :-)
        ... I'd prefer that exceptions generally be fatal, and would like to discourage trying to use the exception system for complex flow of control. Which means that all of TheDamian's arguments for exception objects are arguments for how to better do what I'd like people not to do.

        The emotion behind this evokes some fellow feeling but I don't find much logic in it. Exceptions are always a flow of control mechanism. Exceptions usually are fatal. Exceptions should be used to clarify flow of control. And if you must do something, distaste is not a reason to do it badly.

        Exceptions are all about passing control to unknown code.

        The type or value of an exception should rarely be needed by the handler; that is the start of a brittle, tangled mess. I use the type to carry meaning without being locked into a representation (for those rare occasions). I use the object to carry a bunch of diagnostic data.

        I use exceptions like a more robust and efficient version of the LIBNAME_STATUS variables common to old C libraries. Rarely is there any fine discrimination by the handler, usually just FAILURE vs. SUCCESS.

        Be well,
        rir

Re: Style guide for error messages?
by pg (Canon) on Sep 21, 2005 at 23:21 UTC

    Cool! Just add one thing: for something you expect to go production and someone else will (evetually) take over and support. Add some brief description to the error message that might help them to determine the solution. This is not always possible, but try your best to help whever possible.

Re: Style guide for error messages?
by jplindstrom (Monsignor) on Sep 22, 2005 at 11:17 UTC
    In addition to your stuff:
    • Always provide necessary variables that may be interesting in the context of the error.
      die("Could not open file $file: $!\n");
    • Always put delimiters around the variables, so that you don't get tricked by whitespace, weird chars, line feeds, etc.
      die("Could not open file ($file): ($!)\n");

    /J

Re: Style guide for error messages?
by wazoox (Prior) on Mar 09, 2006 at 17:18 UTC

    I currently use a logging object I wrote to manage messages. When something's going wrong, my rule of the thumb is to write the message to the log object, then to return undef. something like :

    use strict; use warnings; package bozo; use Message; sub failing { my $log = Message->new ; $log->set_error("this always fails"); return } package main; use Message; my $log = Message->new ; # I sometimes do this eval { `touch /toto` }; if ($@) { $log->set_error("it failed! : $@") ; # or this if ( not 0 ) { $log->set_error("it failed too!") } # now calling the failing sub up there: if ( not bozo::failing() ) { print $log->get_message(); }

    Whatever I do with the error string, the Message object also spits out errors and messages to the log, this way (PID, date and time, the the message):

    [25435] 2006-02-21 14:27:20 : ERROR : DBD::Pg::st execute failed: exec +ute on disconnected handle at ../lib/Archiver/Job.pm line 96.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://493945]
Approved by cazz
Front-paged by cazz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (7)
As of 2014-10-22 22:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (122 votes), past polls