Meglévő minimalista hibakezelést „turbóztam fel” emailküldéssel. Nekem ez a verzió jobban bejön, egyébként meg ízlés dolga. Van egy „bootloader” területünk (az az include, amit mindig, minden körülmények között meg KELL hívni), ebbe helyeztem el egy require_once("ErrorHandler.php"); , ezzel el is intéztem az egészet.
A kódnak két (három) feladata van:
- Error esetén emailt küldeni stackTrace-szel, változók értékével, hibaüzenettel.
- Exception esetén ugyanezt megcsinálni.
- Meglévő error.log file-t továbbra is úgy írni, mintha semmi se történt volna.
A harmadik ponttal úgy voltam, hogy a lehető legkevesebbet akartam azon a kódon változtatni, ezért egy kicsit öszvér a végeredmény, ezért is neveztem el 0.9-es verziónak. Ha időm engedi, akkor tisztességgel integrálom a „printDebugBacktrace3” metódust is.
Ha van valami jó ötleted, hogy amitől még ütősebb lehetne, akkor azt sok szeretettel látom a kommentben (és beépítem a köv. verzióba).
Disclaimer: a printDebugBacktrace3 metódust nem én írtam, ezért van németül kommentelve és ezért érhető tetten, hogy teljesen más a hibaüzenet formázása, valamint az, hogy milyen értékekre kíváncsi.
'Error'
,E_WARNING => 'Warning'
,E_PARSE => 'Parsing 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'
,E_STRICT => 'Runtime Notice'
,E_RECOVERABLE_ERROR => 'Catchable Fatal Error'
,E_DEPRECATED => "Deprecated"
,E_USER_DEPRECATED => "User deprecated"
);
/**
* Set of errors for which a var trace will be saved.
* @var Array $userErrors
*/
private static $userErrors = array( E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE );
/**
* Set of errors for which an e-mail will be sent.
* @var Array $mailErrors
*/
private static $mailErrors = array( E_ERROR, E_WARNING, E_PARSE );
/**
* Formating the exception message and calls "sendMail".
*
* @param Exception e thrown Exception
*/
public static function handleException($e) {
$emsg = $e->getMessage();
$err = "EXCEPTION TRACE as string".PHP_EOL;
$err .= $e->getTraceAsString().PHP_EOL;
$err .= "PRINT_R EXCEPTION".PHP_EOL;
$err .= print_r($e, true).PHP_EOL;
self::sendMail($emsg, $err);
}
/**
* Formating the error message for PHPLOG and for email. Calls "sendMail".
*
* @param int errno error number, refering to variable errorTypers
* @param string errmsg error message
* @param string filename the error occured in the $filename
* @param int linenum the error occured at the $linenum line
* @param mixed vars variables
*/
public static function handleError( $errno, $errmsg, $filename, $linenum, $vars ) {
if(!(error_reporting() & $errno)) {
return;
}
$dbgTrace = debug_backtrace();
printDebugBacktrace3($errno, $errmsg, $filename, $linenum, $dbgTrace);
// SPAM Prevention: Keine Warnungen wegen NOWAIT Locks aus dem AccessProtectionSystem
// and because we have an old PHP
if(strpos( $errmsg, 'deprecated') !== false) {
return;
}
/* Der '@'-Operator in PHP wird verwendet, um Warnungen zu unterdrücken.
* Eigene Error Handler werden jedoch trotzdem aufgerufen, allerdings wird error_reporting
* temporär auf 0 gesetzt.
* Uns interessieren diese Meldungen hier nicht, da der Entwickler sich ja explitzit dazu
* entschlossen hat, diese Warnungen zu unterdrücken.
*/
if(error_reporting() === 0) {
return;
}
$err = self::$errorType[$errno]."($errno), ".date( "Y-m-d H:i:s (T)" ).":\n";
if( in_array( $errno, self::$userErrors ) ) {
$err .= "\t" . wddx_serialize_value( $vars, "Variables" ) . " \n";
}
foreach( $dbgTrace as $l_idx => $l_trace ) {
if( $l_idx == 0 ) // Der letzte Aufruf ist diese Handler-Funktion: Hier also nur den Fehler ausgeben!
{
$err .= '#'.$l_idx.' '.$filename.'('.$linenum.'): '.$errmsg."\n";
}
else
{
if ( !isset( $l_trace['file'] ) )
continue;
$err .= '#'.$l_idx.' '.$l_trace['file'].'('.$l_trace['line'].'): ';
$args = array();
if( isset( $l_trace['args'] ) && is_array( $l_trace['args'] ) )
foreach( $l_trace['args'] as $ll_arg )
{
if( is_object( $ll_arg ) )
{
$args[] = 'Object('.get_class( $ll_arg ).')';
}
elseif( is_resource( $ll_arg ) )
{
$args[] = get_resource_type( $ll_arg );
}
elseif( is_array( $ll_arg ) )
{
$args[] = 'array('.count($ll_arg).')';
}
else
{
$ll_s = (string)$ll_arg;
if( strlen( $ll_s )>30 )
{
$ll_s = substr( $ll_s, 0, 15 ).'...'.substr( $ll_s, -15 );
}
$args[] = is_string($ll_arg) ? '"'.$ll_s.'"' : $ll_s;
}
}
if( isset( $l_trace['object'] ) )
{
$err .= get_class($l_trace['object']).'->';
}
$err .= $l_trace['function'].'( '.implode(', ', $args).' )'."\n";
}
} // foreach stacktrace
// only critical errors
if( in_array( $errno, self::$mailErrors ) )
{
self::sendMail(self::$errorType[$errno].' - '.$errmsg, $err);
}
} // handleError()
/**
* Creates and send an email including the error message (created by the methods handleError and handleException).
* Also includes session variables and defined variables.
*
* @param string emsg Error message for the header
* @param string err Long error text including callStack.
*/
static function sendMail ($emsg, $err) {
$mail = new Mail();
$mail->setSubject('Error in CoConUt: '.$emsg);
$arguments = php_sapi_name() == 'cli' ? $_SERVER['argv'] : $_REQUEST;
$msg = $err.PHP_EOL;
$msg .= PHP_EOL."SESSION VARIABLES: ".PHP_EOL;
$msg .= print_r( $_SESSION, true).PHP_EOL;
$msg .= PHP_EOL."DEFINED VARIABLES: ".PHP_EOL;
$msg .= print_r( get_defined_vars (), true).PHP_EOL;
$msg .= PHP_EOL."ARGUMENTS: ".PHP_EOL;
$msg .= print_r( $arguments, true).PHP_EOL;
$mail->setBodyText( $msg );
try {
$mail->send();
}
catch( Exception $e )
{
print( 'Error when sending exception e-mail: '.$e->getMessage().PHP_EOL.$e->getTraceAsString().PHP_EOL );
}
}
}
/**
* Class Mail is a simple class to send emails. It uses the PHP method mail().
* The sender will be "Apache" if the error occured during a web session or
* the name assigned to the shell script user if the error occured during the execution of a shell script.
*
*/
class Mail {
/**
* @var string subject the subject of the email
*/
var $subject = "Coconut Error ";
/**
* @var string body the email body
*/
var $body;
/**
* Sets the subject of the email
*
* @param string s subject
*/
function setSubject($s) {
$this->subject = $s;
}
/**
* Sets the body of the email
*
* @param string s body
*/
function setBodyText($s) {
$this->body = $s;
}
/**
* Executes the mail() method of PHP with the correct parameters.
*/
function send() {
mail (EMAILTO, $this->subject, $this->body);
/ }
} // end of class Mail
/**
* Dieses Include-File stellt eine Fehlermeldungs- und Logging-Funktion
* per set_error_handler() ein.
* Dadurch wird bei Fehlern der Call-Stack in das error_log geschrieben
*
*/
if(!function_exists("printDebugBacktrace3")) {
/**
* Das Haupt-Konfigurations-Include-File.
*/
// @include_once "config.inc.php";
/** Funktion, die von PHP selbst aufgerufen wird, wenn ein Fehler
* auftritt (da sie mit set_error_handler() als Error-Handler
* eingestellt wird).
*/
function printDebugBacktrace3($errno, $errstr, $errfile, $errline, $dbgTrace) {
// Bei manchen Funktionen möchte ich die Args nicht mitloggen.
// Da möchte ich dann auch bei den aufrufenden Funktionen keine Args mitloggen.
$dont_log_args_array = array( // alles hier klein schreiben!
"ocilogon","oci_connect",
"ocinlogon","oci_new_connect",
"ociplogon","oci_pconnect",
"ftp_login",
"ssh2_auth_password",
"ssh2_auth_pubkey_file",
"cocouser::authenticate");
// Bei manchen Funktionen sollen Arrays komplett ausgegeben werden.
$expand_arrays_array = array( // alles hier klein schreiben!
"db::parsebindexecute",
);
if(php_sapi_name() == "cli")
$NL = PHP_EOL;
else
$NL = "
\n";
$dbgTrace = debug_backtrace();
$msg = ErrorHandler::$errorType[$errno].": ".$errstr." in ".$errfile." on line ".$errline.".";
$short_msg = $msg . $NL;
$NL = PHP_EOL; // nur $short_msg wird angezeigt, der Rest geht ins error_log,
// da möchte ich kein
sehen
$msg .= $NL." calling stack:".$NL;
foreach($dbgTrace as $dbgIndex => $dbgInfo) {
if ($dbgInfo["file"]==__FILE__) continue;
if($dbgInfo["function"] == 'handleError') continue;
if(isset($dbgInfo["class"]))
$class_info = $dbgInfo["class"] . "::";
else
$class_info = "";
$dbgInfo["function"] = $class_info . $dbgInfo["function"];
if(!isset($dont_log_args) || !$dont_log_args)
$dont_log_args = in_array(strtolower($dbgInfo["function"]),
$dont_log_args_array);
$args = '';
if(is_array($dbgInfo["args"]) && !$dont_log_args ) {
foreach($dbgInfo["args"] as $arg) {
if(is_object($arg))
$args .= ', ' . '(object)';
elseif(is_array($arg) &&
in_array(strtolower($dbgInfo["function"]), $expand_arrays_array))
$args .= ', ' . print_r($arg, true);
elseif(is_array($arg))
$args .= ', (array)';
else
$args .= ', ' . $arg;
}
$args = substr($args,1);
if(strlen($args) > 1000)
$args = "(".$args."...) [truncated, too long]";
else
$args = "(".$args.")";
}
$msg .= " ".$dbgInfo["file"].", line ".$dbgInfo["line"].", ".
$dbgInfo["function"].$args.$NL;
}
if(ini_get("log_errors")) {
$msg = ( php_sapi_name() == "cli"?
$_SERVER["SCRIPT_FILENAME"] : $_SERVER["REQUEST_URI"]
) .
(
isset($_SERVER["HTTP_REFERER"])?
(" referer: ".$_SERVER["HTTP_REFERER"]) : ""
) . $NL . $msg;
error_log($msg, 0);
}
if(ini_get("display_errors")) {
if(php_sapi_name() == "cli")
echo "\n";
else
if(!count(ob_list_handlers())) // nicht, wenn ich nach ob_start() aufgerufen wurde
echo "\n";
echo ini_get("error_prepend_string");
echo $short_msg;
echo " The calling stack can be seen in the log file ",
ini_get("error_log")," .",$NL;
echo ini_get("error_append_string");
if((php_sapi_name() != "cli") && !count(ob_list_handlers()))
echo "\n";
}
}
} // if(!function_exists("printDebugBacktrace"))
?>

