Debugging Perl/CGI Scripts

Hosting servers are configured to store error logs for CGI/Perl scripts, encountered during the course of operation. By default, error messages are sent to STDERR.

Most HTTPD servers direct STDERR to the server's error log, which stores information for all the websites hosted on a shared hosting environment. As this, this file cannot be accessed by individual website owners. You may wish to keep private error logs, distinct from the server's error log, or may wish to direct error messages to to a web browser.

This can be accomplished by coding your scripts accordingly during the development phase.

Sending error messages to a private log file

The carpout() function can be used to achieve this. Since carpout() is not exported by default, you must import it explicitly as -

use CGI::Carp qw(carpout);

The carpout() function requires one argument, which should be a reference to an open filehandle for writing errors. It should be called in a BEGIN block at the top of the CGI application so that compiler errors will be caught.

Example:

BEGIN {<br>use CGI::Carp qw(carpout);<br>open(LOG, "&gt;&gt;/domains/domain.com/logs/cgi-error.log") or<br>die("Unable to open cgi-error.log: $!\n");<br>carpout(LOG);<br>}

carpout() does not handle file locking on the log for you at this point.

Sending error messages to a Web Browser

Fatal (die, confess) errors can be sent to a web browser by importing the special "fatalsToBrowser" subroutine:

use CGI::Carp qw(fatalsToBrowser);<br>die "Couldn't open log file";
<blockcode>
<p>Fatal errors will now be echoed to the browser as well as to the log file. CGI::Carp arranges to send a minimal HTTP header to the browser so that even errors that occur in the early compile phase will be seen. Nonfatal errors will still be directed to the log file only (unless redirected with carpout).</p>
<p>&nbsp;</p>
<blockcode language="perl">
<b>Sample Script</b><p>
#!/usr/bin/perl -wT</p>
<p># In the first line above, T causes Perl to check<br>
# for "tainted" data, that is, data from outside the<br># script (i.e. user input) that is going to be used<br># to affect something else outside the script<br># (i.e. writing to a log file)</p>
<p># You can untaint data by parsing it for unwanted<br># characters then saving it to another variable.</p>
<p># If you are having trouble with a script, try removing<br>
# the T switch to see if that is the problem.</p>
<p># The w switch in the first line causes warnings about<br># script syntax to be printed, if there are any.</p>
<p># This script does 2 things:<br>#<br>#1. It directs fatal errors to the browser,<br># so when the script is invoked via the Web,<br># a meaningful error message is returned.<br># This is useful when developing a script;<br># but should be disabled when the script<br># is made publicly available.<br>#<br># 2. It will direct any error message the script<br># generates to an error log that resides in<br># the user's home directory. The file must<br># already exist and be "other" writeable.</p>
<p># Notice that the following is enclosed in
        a BEGIN { }<br># block that causes it to execute before the rest of<br># the script is read.</p>
<p># This block should be placed in the main script,<br># as near the top as practical. Do not place it in<br># subroutines or libraries. Always test<br># subroutines thoroughly before placing them<br># in libraries.</p>
<p>BEGIN {</p>
<p># define an error log in YOUR home directory<br># this is an example where the<br># home directory is /domains/domain.com/</p>
                        <p>my $error_log = "/domains/domain.com/logs/cgi-error.log";</p>
                        <p># "my" in the line above makes the variable
        $error_log<br># local so it only has meaning inside this block.<br># See your text for more on variable scope.</p>
                        <p># load the CGI::Carp module;<br># fatalsToBrowser directs fatal errors to the browser<br># carpout is for directing errors to the error log</p>
                        <p>use CGI::Carp qw(fatalsToBrowser carpout);</p>
                        <p>open (LOG,"&gt;&gt;$error_log") ||<br>die "couldn't open log file: $!";<br>carpout(LOG);</p>
                        <p># open(...) is used to open a file.<br># &gt;&gt; means the new input will be appended what's<br># already in the file.</p>
                        <p># LOG is a nickname (properly called a
        "file handle")<br># that is given to the file so it is easy to refer to<br># it later, i.e. carpout(LOG) sends the error<br># message to the file with the nickname LOG</p>
                        <p># || means "or" (as in do this or that)<br># die means stop executing the program. You can add<br># a message in quotes after the die command.</p>
                        <p># $! is a special variable that contains
        the current error info</p>
                        <p>}</p>
                        <p># The following line will cause an error.<br># It is a call to a sub-routine that does not exist.<br># After you have tried this script and received the error message,<br># comment out the following line so no error occurs<br># and "Hello world!" is printed.</p>
                        <p>&amp;non_existent_subroutine();</p>
                        <p>print &lt;&lt;EOT;<br>Content-type: text/html\n\n</p>
                        <p>&lt;HTML&gt;<br>&lt;BODY&gt;<br>Hello world!<br>EOT</p>
                        <p>print "&lt;/BODY&gt;&lt;/HTML&gt;";
                        </p>