http://qs321.pair.com?node_id=130621

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

How do you handle errors in a complex CGI-program? Especially if you use lots of own modules. I usually have a subroutine called "error" in each CGI-program, but I also thought about an Error.pm. It's aim is to generate an error-page. But what is with no so important errors - the script could continue. But how do I handle this?

Thank you,
Uwe

Replies are listed 'Best First'.
Re: CGI Error Handling
by davorg (Chancellor) on Dec 10, 2001 at 15:17 UTC

    I think it's important to differentiate between the different types of errors that you're handling. For errors that are caused by invalid user input, it's a good idea to create an error page that tells them what to correct in their input - probably including the current input you've got and pointing out what needs to be fixed.

    For anything that is outside of the user's control (missing files on the server, invalid permissions, that kind of thing) I think that it's a waste of time creating clever error reporting pages. You're probably not going to give the user any information that will mean anything to him than the default web server error page and you run the risk of giving a passing cracker too much information.

    The "vanilla" 500 Error page is fine for most purposes. Of course, there's nothing to stop you replacing it with another that better reflects the look and feel of your site.

    --
    <http://www.dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

      I like eval blocks
      eval { #any code in here yourSub(); } if ($@){ # $@ contains err message if error occurs warn " continue with script but there has been an erro $@ \n"; #run error handling routine ErrorHandle(); }

      The average user finds a standard Apache error page v scary so I try and make an error doc for every error possible.

      ErrorDocument 404 /missing.html ErrorDocument 401 /auth_error.html

      Update amended to consider below
        Two important pieces of advice.

        The first is to pick an indentation style indenting between 2-4 spaces, and then start using it. Now. Running your code through perltidy will show you what it looks like.

        The second is to call functions with explicit parameters. That means using parens. As documented in perlsub, if you use just the plain & and in a function you call another function, that other function will get the parameters to the first as arguments.

        Believe me. You may think these are picky details. But they are not. The first strongly impacts how easy it is to pick out your logic at a glance. The second is causing a subtle set of side-effects that may or may not be intended which a maintainance programmer may or may not know enough to notice.

Re: CGI Error Handling
by giulienk (Curate) on Dec 10, 2001 at 14:57 UTC
    I don't know your exact needs but i think you should check out CGI::Carp module.
    use CGI::Carp qw(fatalsToBrowser); die "Error! $error" if ($error);
    This output the error to the browser in a pretty way.

    gkinueliileunikg

      Whilst this is great to use when you're developing a script, I wouldn't advise using it in production code. This is because the error messages that it gives can be a source of useful information for anyone trying to crack your site. In some cases, just knowing that you've got Perl installed on you server can give crackers a head start.

      Error messages like this belong in the server error log where the webmaster can read them. There's a good reason why Apache gives a plain 500 error page by default.

      --
      <http://www.dave.org.uk>

      "The first rule of Perl club is you do not talk about Perl club."
      -- Chip Salzenberg

        It almost sounds like you're warning people away from using CGI::Carp entirely. (You probably aren't, but I wanted to post this for the benefit of other readers that might also be confused.) It's mainly the fatalsToBrowser and warningsToBrowser bits that we don't want to see in production code. CGI::Carp's primary purpose is to format out nice, debug-friendly error messages in your server's error logs, so I definitely recommend its use even in production code.
      Thanks for the hint, but CGI::Carp is out of the question. I need something that produces an errorpage in the same style as the application ist.
        CGI::Carp can easily be told to use your own template or function for generating the error message to the user. Check out the documentation for specifics.
Re: CGI Error Handling
by Lucky (Scribe) on Dec 10, 2001 at 17:13 UTC
    The simplest way to handle errors is eval block. It acts like java try{}catch{} statement. For example:
    eval{ ...code...}; if ($@) { handle error }
    For complex error handling Error.pm module exists. It's very useful for scripts with an object-oriented structure. Example:
    use Error; try{ do something... }catch Error::Simple with{ handling... }finally{ ... }
    Looks like Java code :-)
      Looks like Java code

      You say that like it's a good thing :)

      --
      <http://www.dave.org.uk>

      "The first rule of Perl club is you do not talk about Perl club."
      -- Chip Salzenberg

Re: CGI Error Handling
by Lucky (Scribe) on Dec 10, 2001 at 16:48 UTC
    There is one useful package in Perl standard library - CGI::Carp.
    It will help you to redirect ALL errors and warnings to your own log file.
    BEGIN{ use CGI::Carp qw(carpout); open LOG, $logfile or die "Couldn't open log file: $!"; carpout(LOG); }
    see also 'man CGI::Carp';
Re: CGI Error Handling
by bmccoy (Beadle) on Dec 10, 2001 at 16:56 UTC
    A lot depends on the error -- an application level error (bad user input, unavailable resource, etc) I usually handle with my own exception handling routines. Fatal exceptions I usually dump into the web server using somehting like CGI::Carp. It also lets you print error messages to the browser screen, but this shouldn't be done for a production, just as a debugging tool.

    -- Brett

    Go not to the Elves for counsel, for they will say both no and yes

      I mean application level error. How does your own exception handling routine look like?
      I usually write a database-abstraction-layer (and plan to write a general modul for this purpose). I'm not sure how I should handle errors that occur in this layer. Should the script specify an error template and the database-layers shows this page if an error occurs? Or should the database-layer only return a false value and stores the errortext/-code in a datastructure? The script then can retrieve this errortext and prints the errortemplate.
Re: CGI Error Handling
by edebill (Scribe) on Dec 10, 2001 at 22:34 UTC

    One thing to keep in mind is the difference between development/debugging error messages, and the user friendly error messages that should be displayed to actual users. Most users don't know what to do with a stack trace or a DBI error message - and as someone else already mentioned, that can be a security risk. Useability folks will scream if you mention anything technical in an error message.

    On the other hand, carp, die() and company are your friends during dev.

    I like to wrap most everything in eval{} blocks so that major errors can be wrapped with user friendly text, and uses UNIX::Syslog to log error messages. This gives me the chance to grab the $DBI::errst which typically has some great debug info in it for db errors.

    This works great for production, but we tend to leave the eval{}'s out while first coding something - verbose error messages are a big help while debugging. Unfortunately, doing anything as a two pass operation means sometimes the eval{}'s don't get added before it goes to production.

      I like the idea of putting code in eval-blocks, but is this also efficient/state-of-the-art/perlish?
      eval { open(FH, $file) or die "NO_OPEN"; $text = join('', <FH>) or die "NO_READ"; close(FH) or die "NO_CLOSE"; } if ($@) { ... }
      instead of
      open(FH, $file) or &error("NO_OPEN", $file, $!); my $text = join('', <FH>) or &error("NO_READ", $file, $!); close(FH) or &error("NO_CLOSE", $file, $!);
      What approach do you find better?
        You can (as in "have the potential to be able to") recover in the eval block. Can you recover in your error sub? I find the block more flexible that way, if that's the sort of thing that's necessary.

        (Feel free to drop the leading ampersand. It doesn't add anything in this case, and is often better removed.)

        In your example, the second option is actually more readable. But if the function call on the left is longer, and you start wrapping things will get pretty ugly. What's more, if you do something like $sth->execute and the transaction fails (e.g. from bad SQL or an RI constraint) $sth->execute will call die() for you - and an eval block is the only way to catch it.

        I prefer to use the same method for something every time - so I go for nothing but eval blocks. Using two different constructs to do the same thing in the same bit of software just makes things harder to maintain. YMMV.