Show Contents Previous Page Next Page
Chapter 4 - Content Handlers Handling Errors In this section... Introduction Show Contents Go to Top Previous Page Next Page
Errors in Apache modules do occur, and tracking them down is significantly trickier than in standalone Perl or C programs. Some errors are due to bugs in your code, while others are due to the unavoidable hazards of running in a networked environment. The remote user might cancel a form submission before it is entirely done, the connection might drop while you're updating a database, or a file that you're trying to access might not exist.
A virtuous Apache module must let at least two people know when a problem has occurred: you, the module's author, and the remote user. You can communicate errors and other exception conditions to yourself by writing out entries to the server log. For alerting the user when a problem has occurred, you can take advantage of the simple but flexible Apache ErrorDocument system, use CGI::Carp, or roll your own error handler.
Error Logging Show Contents Go to Top Previous Page Next Page We talked about tracking down code bugs in Chapter 2
and will talk more about C-language specific debugging in Chapter 10.
This section focuses on defensive coding techniques for intercepting and handling
other types of runtime errors.
The most important rule is to log everything. Log anything unexpected, whether it is a fatal error or a condition that you can work around. Log expected but unusual conditions too, and generate routine logging messages that can help you trace the execution of your module under normal conditions.
Apache versions 1.3 and higher offer syslog -like log levels ranging in severity from debug, for low-priority messages, through warn, for noncritical errors, to emerg, for fatal errors that make the module unusable. By setting the LogLevel directive in the server configuration file, you can adjust the level of messages that are written to the server error log. For example, by setting LogLevel to warn, messages with a priority level of warn and higher are displayed in the log; lower-priority messages are ignored.
To use this adjustable logging API, you must load the standard Apache::Log module. This adds a log() method to the Apache request object, which will return an Apache::Log object. You can then invoke this object's methods in order to write nicely formatted log entries to the server's error log at the priority level you desire. Here's a short example:
use Apache::Log ();
my $log = $r->log;
$log->debug("Trying to lock guestbook file now");
unless (lock($GUESTBOOKFILE,1)) {
$log->emerg("Can't get lock!");
return SERVER_ERROR;
}
$log->debug("Got lock");
In this example, we first obtain a log object by calling the request object's log() method. We call the log object's debug() method to send a debug message to the error log and then try to perform a locking operation. If the operation fails, we log an error message at the emerg priority level using the log object's emerg() method and exit. Otherwise, we log another debugging message.
You'll find the full list of method calls made available by Apache::Log
in Chapter 9, in the subsection "Logging
Methods" under "The Apache Request Object."
In addition, the Apache Perl API offers three simpler methods for entering messages
into the log file. You don't have to import the Apache::Log module
to use these methods, and they're appropriate for smaller projects (such as
most of the examples in this book). -
$r->log_error($message)
- log_error() writes out a time-stamped message into the server error log using a facility of
error. Use it for critical errors that make further normal execution of the module impossible.
This method predates the 1.3 LogLevel API but still exists for backward compatibility
and as a shortcut to $r->log->error.
-
$r->warn($message)
- warn() will log an error message with a severity level of warn. You can use this for noncritical
errors or unexpected conditions that you can work around. This method predates
the 1.3 LogLevel API but still exists for backward compatibility and as a shortcut
to $r->log->warn.
-
$r->log_reason($message,$file)
-
This is a special-purpose log message used for errors that occur when a content handler
tries to process a file. It results in a message that looks something like this:
access to /usr/local/apache/htdocs/index.html failed for ppp12.yahoo.com,
reason: user phyllis not authorized
You might also choose to include a $DEBUG global in your modules, either hard-coding it directly into the source, or by pulling its value out of the configuration file with Apache::dir_config(). Your module can then check this global every time it does something significant. If set to a true value, your script should send verbose informational messages to the Apache error log (or to an alternative log file of your choice).
The ErrorDocument System Show Contents Go to Top Previous Page Next Page
Apache provides a handy ErrorDocument directive that can be used to display a custom page when a handler returns a non-OK status code. The custom page can be any URI, including a remote web page, a local static page, a local server-side include document, or a CGI script or module. In the last three cases, the server generates an internal redirect, making the redirection very efficient.
For example, the configuration file for Lincoln's laboratory site contains this directive:
ErrorDocument 404 /perl/missing.cgi
When the server encounters a 404 "Not Found" status code, whether generated by a custom module or by the default content handler, it will generate an internal redirect to a mod_perl script named missing.cgi. Before calling the script, Apache sets some useful environment variables including the following:
REDIRECT_URL -
The URL of the document that the user was originally trying to fetch.
REDIRECT_STATUS -
The status code that caused the redirection to occur.
REDIRECT_REQUEST_METHOD -
The method (GET or POST) that caused the redirection.
REDIRECT_QUERY_STRING -
The original query string, if any.
REDIRECT_ERROR_NOTES -
The logged error message, if any.
A slightly simplified version of missing.cgi that works with Apache::Registry
(as well as a standalone CGI script) is shown in Example 4-16.
For a screenshot of what the user gets when requesting a nonexistent URI, see
Figure 4-9.
Figure 4-9. The missing.cgi script generates
a custom page to display when a URI is not found. Example 4-16. A Simple Apache::Registry
ErrorDocument Handler
#!/usr/local/bin/perl
# file: missing.cgi
use CGI qw(:standard);
use strict;
print header,
start_html(-title => 'Missing Document', -bgcolor => 'white'),
h1(img({-src => '/icons/unknown.gif'}),
'Document Not Found'),
p("I'm sorry, but the document you requested,",
strong($ENV{REDIRECT_URL}),
"is not available. Please try the",
a({-href => "/search.html"}, "search page"),
"for help locating the document."),
hr,
address(a({-href => "mailto:$ENV{SERVER_ADMIN}"}, 'webmaster')),
end_html;
If you want to implement the ErrorDocument handler as a vanilla Apache Perl API script, the various REDIRECT_ environment variables will not be available to you. However, you can get the same information by calling the request object's prev() method. This returns the request object from the original request. You can then query this object to recover the requested URI, the request method, and so forth.
Example 4-17 shows a rewritten version of
missing.cgi that uses prev() to recover the URI of the missing
document. The feature to note in this code is the call to $r->prev
on the fifth line of the handler() subroutine. If the handler was invoked
as the result of an internal redirection, this call will return the original
request object, which we then query for the requested document by calling its
uri() method. If the handler was invoked directly (perhaps by the user
requesting its URI), the original request will be undefined and we use an empty
string for the document URI.
Example 4-17. An ErrorDocument Handler
Using the Vanilla Apache API
package Apache::Missing;
# File: Apache/Missing.pm
use strict;
use Apache::Constants qw(:common);
use CGI qw(:html);
sub handler {
my $r = shift;
$r->content_type('text/html');
$r->send_http_header;
return OK if $r->header_only;
my $original_request = $r->prev;
my $original_uri = $original_request ? $original_request->uri : '';
my $admin = $r->server->server_admin;
$r->print(
start_html(-title => 'Missing Document',
-bgcolor => 'white'),
h1(img({-src => '/icons/unknown.gif'}),
'Document Not Found'),
p(
"I'm sorry, but the document you requested,",
strong($original_uri),
", is not available. Please try the",
a({-href => "/search.html"}, "search page"),
"for help locating the document."
),
hr,
address(a({-href => "mailto:$admin"}, 'webmaster')),
end_html
);
return OK;
}
1;
__END__
Here's an example using Apache::Missing in the configuration file:
<Location /Missing>
SetHandler perl-script
PerlHandler Apache::Missing
</Location>
If the static nature of the Apache ErrorDocument directive is inadequate for your needs, you can set the error document dynamically from within a handler by calling the request object's custom_response() method. This method takes two arguments: the status code of the response you want to handle and the URI of the document or module that you want to pass control to. This error document setting will persist for the lifetime of the current request only. After the handler exits, the setting returns to its default.
For example, the following code snippet sets up a custom error handler for the SERVER_ERROR error code (a generic error that covers a variety of sins). If the things_are_ok() subroutine (not implemented here) returns a true value, we do our work and return an OK status. Otherwise, we set the error document to point to a URI named /Carp and return a SERVER_ERROR status.
package Apache::GoFish;
# file: Apache/GoFish.pm
use strict;
use Apache::Constants qw(:common);
sub handler {
my $r = shift;
if (things_are_ok($r)) {
do_something();
return OK;
}
$r->custom_response(SERVER_ERROR, "/Carp");
return SERVER_ERROR;
}
1;
__END__
Show Contents Go to Top Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |