
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")) ?>