<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
// +----------------------------------------------------------------------+
// | Eventum - Issue Tracking System |
// +----------------------------------------------------------------------+
// | Copyright (c) 2003 - 2008 MySQL AB |
// | Copyright (c) 2008 - 2009 Sun Microsystem Inc. |
// | |
// | This program is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU General Public License as published by |
// | the Free Software Foundation; either version 2 of the License, or |
// | (at your option) any later version. |
// | |
// | This program is distributed in the hope that it will be useful, |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// | GNU General Public License for more details. |
// | |
// | You should have received a copy of the GNU General Public License |
// | along with this program; if not, write to: |
// | |
// | Free Software Foundation, Inc. |
// | 59 Temple Place - Suite 330 |
// | Boston, MA 02111-1307, USA. |
// +----------------------------------------------------------------------+
// | Authors: João Prado Maia <hide@address.com> |
// +----------------------------------------------------------------------+
//
// @(#) $Id: class.error_handler.php 3797 2009-01-12 20:14:39Z balsdorf $
//
require_once(APP_INC_PATH . "class.misc.php");
require_once(APP_INC_PATH . "class.mail.php");
require_once(APP_INC_PATH . "class.setup.php");
/**
* Class to manage all tasks related to error conditions of the site, such as
* logging facilities or alert notifications to the site administrators.
*
* @version 1.0
* @author João Prado Maia <hide@address.com>
*/
class Error_Handler
{
/**
* Logs the error condition to a specific file and if asked and possible
* queue error in mail queue for reporting.
*
* @access public
* @param mixed $error_msg The error message
* @param string $script The script name where the error happened
* @param integer $line The line number where the error happened
* @param boolean $notify_error Whether error should be notified by email.
*/
function logError($error_msg = 'unknown', $script = 'unknown', $line = 'unknown', $notify_error = true)
{
$msg =& Error_Handler::_createErrorReport($error_msg, $script, $line);
$fp = @fopen(APP_ERROR_LOG, 'a');
fwrite($fp, '[' . date('D M d H:i:s Y') . '] ');
fwrite($fp, $msg);
fclose($fp);
// if there's no db_api object, then we cannot
// possibly queue up the error emails
if ($notify_error === false || is_null(@$GLOBALS['db_api'])) {
return;
}
$setup = Setup::load();
if (@$setup['email_error']['status'] == 'enabled') {
$notify_list = trim($setup['email_error']['addresses']);
if (empty($notify_list)) {
return false;
}
Error_Handler::_notify($msg, $setup['smtp']['from'], $notify_list, $script, $line);
}
}
/**
* Notifies site administrators of the error condition
*
* @access private
* @param string $notify_msg The formatted error message
* @param string $notify_from Sender of the email
* @param string $notify_list Email addresses to whom send the error report.
*/
function _notify(&$notify_msg, $notify_from, $notify_list, $script, $line)
{
$backtrace = debug_backtrace();
array_splice($backtrace, 0, 2);
for ($i = 0; $i < count($backtrace); $i++) {
if ($backtrace[$i]['class'] == 'Error_Handler') {
return;
}
}
$time = time();
$date = date('m/d/Y H:i:s', $time);
$msg = "Hello,\n\n";
$msg .= $notify_msg;
// this checks that we're not running from commandline (cron for example)
if (isset($_SERVER['REMOTE_ADDR'])) {
$msg .= "That happened on page '" . $_SERVER['SCRIPT_NAME'] . "' from IP Address '" . $_SERVER['REMOTE_ADDR'] . "' coming from the page (referrer) '" . $_SERVER['HTTP_REFERER'] . "'.\n\n";
$msg .= "The user agent given was '" . $_SERVER['HTTP_USER_AGENT'] . "'.\n\n";
}
$msg .= "-- \nSincerely yours,\nAutomated Error_Handler Class";
// query database for 'max_allowed_packet'
$stmt = "show variables like 'max_allowed_packet'";
$res =& $GLOBALS['db_api']->dbh->query($stmt);
if (PEAR::isError($res)) {
// we failed, assume 8M
$max_allowed_packet = 8387584;
} else {
$arr = $res->fetchRow(DB_FETCHMODE_ORDERED);
$max_allowed_packet = $arr[1];
$res->free();
}
// skip error details of an email notification about a query that
// was bigger than max_allowed_packet + 1024
if (strlen($msg) > $max_allowed_packet + 1024) {
return false;
}
$notify_list = str_replace(';', ',', $notify_list);
$notify_list = explode(',', $notify_list);
$subject = APP_SITE_NAME . ' - Error found! - ' . $date;
foreach ($notify_list as $notify_email) {
$mail = new Mail_API;
$mail->setTextBody($msg);
$mail->send($notify_from, $notify_email, $subject, 0, false, 'error');
}
}
/**
* Formats backtrace
*
* @access public
* @param array $backtrace The backtrace to format
* @return string A nicely formatted backtrace.
*/
function format_backtrace($backtrace = null)
{
if ($backtrace == null) {
$backtrace = debug_backtrace();
}
// we process backtrace to truncate large blobs
$cutoff = 1024;
$msg = '';
foreach ($backtrace as $e) {
// backtrace frame contains: [file] [line] [function] [class] [type] [args]
$f = $e['file'];
$f = str_replace(APP_INC_PATH, 'APP_INC_PATH/', $f);
$f = str_replace(APP_PATH, 'APP_PATH/', $f);
$fn = $e['function'];
if (isset($e['class'])) {
$fn = $e['class']. $e['type']. $fn;
}
$a = '';
if ($e['args']) {
$z = array();
foreach ($e['args'] as $x) {
if (is_string($x)) {
if (strlen($x) > $cutoff) {
$z[] = sprintf("(string )'%.{$cutoff}s'...", $x);
} else {
$z[] = sprintf("(string )'%s'", $x);
}
} elseif (is_object($x)) {
$z[] = 'Object '. get_class($x);
} elseif (is_bool($x)) {
$z[] = '(bool ) '.$x ? 'true' : 'false';
} else {
$z[] = '(' . gettype($x). ' )' . $x;
}
}
$a = join(', ', $z);
}
$msg .= sprintf("%s:%d\n %s(%s)\n", $f, $e['line'], $fn, $a);
}
return $msg;
}
/**
* Creates error report.
*
* @access private
* @param mixed $error_msg The error message
* @param string $script The script name where the error happened
* @param integer $line The line number where the error happened
*/
function &_createErrorReport(&$error_msg, $script, $line)
{
$msg = "An error was found on line '" . $line . "' of script " . "'$script'.\n\n";
$msg .= "The error message passed to us was:\n\n";
if ((is_array($error_msg)) && (count($error_msg) > 1)) {
$msg .= "'" . $error_msg[0] . "'\n\n";
$msg .= "A more detailed error message follows:\n\n";
$error_msg = $error_msg[1];
}
if (strlen($error_msg) > 1024) {
$msg .= "'" . substr($error_msg, 0, 1024) . "' ...";
// try to find native code from DB error
// [nativecode=1153 ** Got a packet bigger than 'max_allowed_packet' bytes]'
$nativecode = strstr($error_msg, '[nativecode=');
if ($nativecode) {
$msg .= ' ' . $nativecode;
}
$msg .= "\n";
} else {
$msg .= "'" . $error_msg . "'\n";
}
$msg .= "\nA backtrace is available:\n\n";
$backtrace = debug_backtrace();
// remove the two entries related to the error handling stuff itself
array_splice($backtrace, 0, 2);
$msg .= Error_Handler::format_backtrace($backtrace);
$msg .= "\n\n";
return $msg;
}
}
// benchmarking the included file (aka setup time)
if (APP_BENCHMARK) {
$GLOBALS['bench']->setMarker('Included Error_Handler Class');
}