Custom error handling
Posted: Sat Feb 10, 2007 2:39 pm
This is a crude custom error handling system I scrapped up quickly from some other project, but there is a lot of room for improvement so I'm posting it here to see all the flames I can get 
Criticism please!
*note: I modified some stuff to make it easier to look at out of context
Here we have the custom error handler
Here is the error object
This is the error logging class
Finally errors are stored in the queue
Criticism please!
*note: I modified some stuff to make it easier to look at out of context
Here we have the custom error handler
Code: Select all
/**
* Global error handler.
*
* This should not be called by anything - only for uncaught errors.
*/
public function globalErrorHandler($errorNumber, $errorMsg, $file, $line, $context)
{
$error = new SpotSec_Error($errorNumber, $errorMsg, $file, $line, $context, SpotSec_Error::TYPE_ERROR, false);
// Log error
SpotSec_Error_Logger::log($error);
// Queue error for those implementations that need it (e.g. web-interface, SOAP)
SpotSec_Error_Queue::getInstance()->push($error);
// Show error on standard out if running from command line
if (preg_match('/cli/', php_sapi_name()))
echo SpotSec_Error::getErrorType($errorNumber) . ": " . $errorMsg . " - $file ($line)\n";
// TODO: Add "when to die" policy for uncaught errors
}Code: Select all
class SpotSec_Error
{
protected $_code;
protected $_message;
protected $_tag;
protected $_line;
protected $_context;
protected $_caught;
protected $_trace;
protected $_type;
const TYPE_EXCEPTION = 'exception';
const TYPE_ERROR = 'error';
/**
* Global Error types
*
* Define global error message mapping.
* Handled errors and exceptions use COMMON_xxx constants.
* Unhandled errors and exceptions use built-in E_xxx constants.
*/
static $globalErrorTypes = array(
SpotSec_Exception::FATAL => 'fatal',
SpotSec_Exception::ERROR => 'error',
SpotSec_Exception::WARNING => 'warning',
SpotSec_Exception::INFO => 'info',
SpotSec_Exception::VALIDATION => 'validation',
SpotSec_Exception::NOTICE => 'notice',
SpotSec_Exception::DEBUG => 'debug',
E_STRICT => 'strict',
E_ERROR => 'error',
E_WARNING => 'warning',
E_PARSE => 'parse error',
E_NOTICE => 'notice',
E_CORE_ERROR => 'core error',
E_CORE_WARNING => 'core warning',
E_COMPILE_ERROR => 'compile error',
E_COMPILE_WARNING => 'compile warning',
E_USER_ERROR => 'user error',
E_USER_WARNING => 'user warning',
E_USER_NOTICE => 'user notice'
);
/**
* Error constructor.
*
* @param integer $code error code
* @param string $message error message
* @param string $tag a method name or some other nickname
* @param integer $line line number
* @param array $context error context
* @param integer $type type of error - exception or error
* @param boolean $caught true if error was caught by application
* @param array $trace error back trace
* @returns void
*/
function __construct($code, $message, $tag, $line, $context = null, $type, $caught = true, $trace = null)
{
$this->_code = $code;
$this->_message = $message;
$this->_tag = $tag;
$this->_line = $line;
$this->_context = $context;
$this->_type = $type;
$this->_caught = $caught;
$this->_trace = $trace;
}
/**
* Returns error code.
*
* @returns integer error code
*/
function getCode()
{
return $this->_code;
}
/**
* Returns error message.
*
* @returns string error message
*/
function getMessage()
{
return $this->_message;
}
/**
* Returns error tag.
*
* @returns string error tag
*/
function getTag()
{
return $this->_tag;
}
/**
* Returns line number where error occurred.
*
* @returns integer line number
*/
function getLine()
{
return $this->_line;
}
/**
* Returns error context.
*
* @returns array error context
*/
function getContext()
{
return $this->_context;
}
/**
* Returns flag on state of the error.
*
* @returns boolean true if error was caught by application.
*/
function isCaught()
{
return $this->_caught;
}
/**
* Returns error type: error or exception.
*
* @returns string error type
*/
function getType()
{
return $this->_type;
}
/**
* Returns error trace.
*
* @returns array error trace.
*/
function getTrace()
{
if ($this->_trace) {
return $this->_trace;
} else {
return array();
}
}
/**
* Returns the error level string from the error code
*
* @todo move this somewhere else
* @param integer $code
* @return string
*/
public static function getErrorType($code)
{
$typemap = self::$globalErrorTypes;
if (in_array($code, $typemap, true)) {
return $typemap[$code];
} else {
return null;
}
}
}Code: Select all
class SpotSec_Error_Logger
{
/**
* Constructor
*
*/
private function __construct()
{}
/**
* Logs errors
*
* @todo look at todo comments and fix COMMON_DEBUG_MODE
*
* @param SpotSec_Error $error
*/
public static function log(SpotSec_Error $error)
{
static $basetime = 0;
// Set the log variables
$errorNumber = $error->getCode();
$errorMsg = $error->getMessage();
$file = $error->getTag();
$line = $error->getLine();
$context = $error->getContext();
$caught = $error->isCaught();
$type = $error->getType();
// TODO: discuss what policy we want here. Current policy is...
// In debug mode, all errors are logged. In production mode, the following errors are logged:
// - all uncaught errors
// - COMMON_ERROR
// - COMMON_FATAL
/*if ((!COMMON_DEBUG_MODE) && ($errorNumber >= SpotSec_Exception::WARNING)) {
return;
}*/
// Set prefix for log line
if ($type == SpotSec_Error::TYPE_ERROR) {
$prefix = 'error';
} elseif ($type == SpotSec_Error::TYPE_EXCEPTION) {
$prefix = 'exception';
} else {
$prefix = 'unknown';
}
if (!$caught) {
$prefix .= ' uncaught';
}
// Specify log line format
$logline = sprintf("$prefix: %s: %s (%d): %s", SpotSec_Error::getErrorType($errorNumber), preg_replace('/.*\//', '', $file), $line, $errorMsg);
// Perform extra goodness in debug mode
if (COMMON_DEBUG_MODE) {
// Append timestamp to log line
if ($basetime == 0) {
$basetime = microtime(true);
$timestamp = 0;
} else {
$currenttime = microtime(true);
$timestamp = microtime(true) - $basetime;
}
$logline = sprintf("%.4f: %s", round($timestamp, 4), $logline);
// Log messages to standard out when in command-line mode
if (ini_get('display_errors') && preg_match('/cli/', php_sapi_name())) {
echo "$logline\n";
}
// Log messages to custom log file (if set) and standard out on
if (ini_get('error_log')) {
date_default_timezone_set('CST');
$timestamp = date("M j G:i:s T Y");
error_log("{$timestamp}: $logline\n", 3, ini_get('error_log'));
foreach ($error->getTrace() as $traceinfo) {
// Backtrace log format
$logline = sprintf("$prefix: debug backtrace: %s (%d): %s",
preg_replace("/.*\//", "", $traceinfo['file']),
$traceinfo['line'],
$traceinfo['function']);
error_log("{$timestamp}: $logline\n", 3, ini_get('error_log'));
}
}
} else {
$logName = 'SpotSec';
// Log errors to syslog
try {
Zend_Log::registerLogger(new SpotSec_Log_Adapter_Syslog($logName, LOG_NDELAY | LOG_PID, LOG_LOCAL6, false), $logName);
Zend_Log::log($logline, Zend_Log::LEVEL_INFO, $logName);
} catch (Exception $e) {
//TODO: do backup plan
}
// Log backtrace
foreach ($error->getTrace() as $traceinfo) {
// Backtrace log format
$logline = sprintf("$prefix: debug backtrace: %s (%d): %s",
preg_replace('/.*\//', '', $traceinfo['file']),
$traceinfo['line'],
$traceinfo['function']);
try {
Zend_Log::log($logline, Zend_Log::LEVEL_INFO, $logName);
} catch (Exception $e) {
//TODO: do backup plan
}
}
}
}
/**
* Logs an exception to the log.
*
* @static
* @global array global error message mapping
* @return void
*/
public static function logException(Exception $exception, $iscaught)
{
self::log(
new SpotSec_Error(
$exception->getCode(),
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
'',
SpotSec_Error::TYPE_EXCEPTION,
$iscaught,
$exception->getTrace()
)
);
}
}Code: Select all
class SpotSec_Error_Queue
{
/**
* @var SpotSec_ErrorQueue instance
*/
static private $_instance = null;
/**
* @var array error queue
*/
static private $_errors = array();
/**
* SpotSec_ErrorQueue constructor.
*
* @access private
*/
private function __construct()
{}
/**
* Do not allow attempts to clone our singleton.
*
* @access private
*/
private function __clone()
{}
/**
* Returns an ErrorQueue instance.
*
* @static
* @return ErrorQueue current instance
*/
static public function getInstance()
{
if (self::$_instance == null) {
self::$_instance = new self;
}
return self::$_instance;
}
/**
* Add error to error queue.
*
* @param SpotSec_Error $error error object
* @return void
*/
public function push(SpotSec_Error $error)
{
self::$_errors[] = $error;
}
/**
* Return list of queued Error objects.
*
* @param boolean $purge if true, the queue will be purged
*
* @return array list of Errors
*/
public function getAll($purge = true)
{
$errors = self::$_errors;
if ($purge){
self::$_errors = array();
}
return $errors;
}
/**
* Return list of queued error messages.
*
* @param boolean $purge if true, the queue will be purged
*
* @return array list of Errors
*/
public function getAllMessages($purge = true)
{
$messages = array();
foreach (self::$_errors as $error) {
if ($error->isCaught()) {
$messages[] = $error->getMessage();
} else {
if (ini_get('display_errors')) {
$errorNumber = $error->getCode();
if(($errorNumber & error_reporting()) == $errorNumber) {
$tag = preg_replace("/.*\//", "", $error->getTag());
$line = $error->getLine();
$errmsg = $error->getMessage();
$messages[] = sprintf("%s: %s (%d): %s", SpotSec_Error::getErrorType($errorNumber), $tag, $line, $errmsg);
}
}
}
}
if ($purge){
self::$_errors = array();
}
return $messages;
}
/**
* @access private
*/
function __destruct()
{}
}