Improving Error Reporting

One of the most important things in good website engineering is ensuring that when things fail, it's handled gracefully with some kind of reasonable error message returned to the user, and that the event is logged properly in the system error logs. There are basically four ways in which a WACS application is likely to fail - authentication, failure to parse the configuration files, and failure to connect to the database, and failure to find the content.

The authentication failure is pretty conclusively covered by the core WACS check_auth function and it's partners. The parser is rather more tricky to cope with, and the XML parse routines tend to just abort - it's also very all or nothing; the file parses or it doesn't. Additionally once a configuration file is in place, it's unlikely to become corrupted; if it's merely disappeared the defaults will be used and the system will most likely have problems at the next stage of connecting to the database. The third is connecting to the database, which we'll deal with in a moment. The fourth, failure to find content, doesn't result in completely blank screens and should get reported to you quite quickly. Additionally there are so many places it could be (raid parition, lvm volume, remote fileserver) that we can't really do much in a general way.

Where we can get some traction is with decent reporting of database connection problems, and this where the dberror function comes into play. Previously, if we failed to connect to the database we did the following in php:

if( DB::iserror($dbhandle))
{
        die("Can't connect to database\nReason:".
              $dbhandle->getMessage()."\n");
}

and the similar steps in perl were:

$dbhandle=DBI->connect( conf_get_attr("database","dbiconnect"),
                        conf_get_attr("database","dbuser"),
                        conf_get_attr("database","dbpass") ) ||
die("Can't connect to database\nReason given was $DBI::errstr\n");

To improve this, we're going to change this (called mysimple6 in the example code) to use the dberror function instead. This is a routine that uses named parameters, a technique we'll see a lot more of later as we use the WacsUI programming library. Basically we pass it up to five arguments or parameters, but we tell it what each one is, thus the order doesn't matter and if any of them are missing, it doesn't affect the values of the others. The dberror routine expects parameters called: header, message, error, dbuser and dbhost.

The header is to tell the routine how early in the proceedings we are and whether we still need to start the HTML of the web page. Setting header to y says we do want a header added, setting it to n says we don't. The next one, message is the message that the end user will see. The next three are the error message returned by the database routines, the username it was trying to use, and the database connect string it was trying to use. Here is the code for doing this in PHP5:

Example 3.6. Calling dberror for better error reporting

if( DB::iserror($dbhandle))
{
        $wacs->dberror( array( 
		 "header"=>"y",
		 "message"=>"MySimple6: Can't connect to database",
		 "error"=>$dbhandle->getMessage(),
		 "dbuser"=>$wacs->conf_get_attr("database","dbuser"),
		 "dbhost"=>$wacs->conf_get_attr("database","phpdbconnect")
		));
}

while the same basic code in perl looks a little simpler because the parameter names don't need to be packaged up into an array before they're passed:

$dbhandle=DBI->connect( conf_get_attr("database","dbiconnect"),
                        conf_get_attr("database","dbuser"),
                        conf_get_attr("database","dbpass") ) ||
	dberror( header=>'n',
		 message=>"Can't connect to database",
		 error=>$DBI::errstr,
		 dbuser=>conf_get_attr("database","dbuser"),
		 dbhost=>conf_get_attr("database","dbiconnect") );

With the error reporting improved, we'll move on to other things. We'll continue to use the short form version of the error message for brevity in the later examples, but you'll know that you probably want to actually use dberror in most cases. Next up, we'll take a look at displaying set details rather than those of models....