PHP Debugging Techniques – Part II
2. Error Logging
Despite out best efforts, errors can (and do) occur in production environments. When these hiccups do arise we have to ensure that they are dealt with quickly, otherwise users (or even, gasp, clients) get angry.
A logging system can be very useful in tracking down bugs, especially when they happen in a production environment. Such a system can also be useful in debugging during development but lt is found much easier to use an IDE to debug development environment.
Some types of errors we cannot do anything about (think parse errors) and we just have to ensure that we have a close eye on our error logs so we notice when they occur. Fortunately, it is these types of bugs that are normally caught very quickly during development and testing.
As for all the other errors, we need to make sure that they are caught and dealt with properly. Make sure the user is shown a nice error page (with a suitably cute ‘oops-back-soon’ picture) and then log, log
everything in sight! following are strong recommendations:
- The stack current trace (see debug_backtrace() and debug_print_backtrace()).
- The output of get_defined_vars(). However, this is only useful if you call it at the point the error occurs, not at the point the error is logged. This includes global variables.
- Any and all information about the remote user (IP address, user agent, session data)
- All global variable data (which includes the contents of $_COOKIES, $_SERVER etc.)
- Any other status data which is specific to your application
This information will be invaluable when you come to tracking down errors in production code.
PHP’s Error Logging Facility
# Inside PHP.ini
error_log = /home/vhost/logs/php_error_log
display_errors = Off
log_errors_max_len = 0
- Since the errors are being logged there is no need to display them on screen.
- Do not limit the error length so the error message is never cutoff.
Some errors/bugs are more critical then others and may require immidiate attention. Or you may want to have all your error messages (php, system, etc.) in one place for ease of monitoring.
In these cases instead of using error_log or custom error_handler you may want to use a system logger, syslog.
How you choose to actually capture your errors is your own choice. You can use trigger_error and a custom error handler.
3. Use Manual Redirects When Debugging
Many developers (including myself) will make use of redirects when developing web applications. To refresh your memory, here is how you do a redirect in PHP:
This technique can be very useful for sending the user to the correct page, but it can also be very problematic for debugging. For example, do you keep getting sent off to a bizarre area of your application? Do you know if it is just one redirect sending you there or many? What do you do when you get trapped in an infinite redirect loop?
One answer to these problems is to introduce the concept of manual redirects which would only be used for debugging. Rather than sending a header to the client, a link would be send to the target page as well as a stack trace. This would allow you to monitor the redirects that were happening in my application and clearly see what was happening if the application went wrong.
The code used looks something like this:
function redirect($url, $debug = false)
//If manual page redirects have been enabled then print some html
echo “Redirect: You are being redirected to: <a href=’$url’> $url </a>\n”;
echo “Backtrace: \n”;
header ( “Location: $url” );
You may find it useful to pull the $debug value from your configuration system of choice rather than having to pass it for each function call, but it works in this example.
4. Avoid Side Effects
A side effect can be described as a non-obvious effect that was caused by performing an action . For example.
You can see that we start with a $radius and $area variable which we use to show the area of the circle. We then want to display the area of a square with the same dimensions, so we call getSquareArea. Although this function does what its name implies, it also alters the $radius variable (intentionally or not). This is defiantly non-obvious in the rest of our code and can cause severe headaches when it comes to debugging, especially in more complex applications. Of course, you should also avoid global variables for similar reasons.
This also applies to modifying function parameters (which were passed by reference). If you find yourself doing this then you should probably refactor your code. To do this you can either return the parameter rather than modifying it, or you can split the function into several smaller functions. Also, don’t forget that objects are always passed by reference in PHP 5.