Pull to refresh

Доработка стандартного механизма обработки ошибок в CodeIgniter

Reading time5 min
Views3.6K
CodeIgniter предоставляет неплохие возможности по обработке ошибок, но мне они показались недостаточными по следующим причинам:
  • отсутствует возможность обработки исключений
  • отсутствует возможность уведомлять администратора сайта о возникающих ошибках через почту
  • довольно скудная информацию о возникающих ошибках (в фреймворке с которым я работал раньше текст ошибки дополняется дампом глобальных переменных, что очень сильно облегчает процесс отладки, хотелось бы подобную схему увидеть и в CodeIgniter)

Будем это исправлять =)

Обработка исключений (см. ниже вариант без правки файлов ядра)


Указываем обработчик исключений в файле /system/codeigniter/CodeIgniter.php
set_exception_handler('_exception_handler2');

рядом со строчкой
set_error_handler('_exception_handler');

теперь при возникновении исключения будет вызываться метод _exception_handler2 из файла /system/codeigniter/Common.php.

А вот собственно и сам метод:
/**
* Обработчик исключений
*
*/
function _exception_handler2($errstr)
{
	$error =& load_class('Exceptions');
	
	// Выводим текст об исключении на экран
   echo $error->show_error('error', nl2br($errstr));

	// Should we log the error?  No?  We're done...
	$config =& get_config();
	if ($config['log_threshold'] == 0)
	{
		return;
	}
	
	// Пишем сообщение в лог
	$error->log_exception('Exception', $errstr, '', '');
	
	exit;
}


Уведомление по почте и дополнительная информация по ошибкам


Для того чтобы админстратор получал уведомление о ошибках на сайте на почтовый ящик, необходимо в файле /system/application/config/config.php указать почтовый адрес администратора:
/*
|--------------------------------------------------------------------------
| Уведомление администратора об ошибках на сайте
|--------------------------------------------------------------------------
|
| Если вы хотите чтобы администратору приходили письма о возникающих ошибках и исключениях
| Укажите у следующего параметра значением адрес почтового ящика администратора
|
*/
$config['log_to_email'] = 'admin@site.ru';

Если вы не хотите чтобы сообщения отсылались — не указывайте этот параметр или оставьте его пустым.

Расширяем базовый класс Exceptions — в директории /system/application/libraries/
создаем файл MY_Exceptions.php со следующим содержимым:
class MY_Exceptions extends CI_Exceptions
{
	/**
	 * Exception Logger
	 *
	 * Метод заменяет базовый добавляя возможности
	 * отсылать сообщению администратору сайта о ошибках
	 * и выводить дополнительную информацию в лог
	 *
	 * @access	private
	 * @param	string	the error severity
	 * @param	string	the error string
	 * @param	string	the error filepath
	 * @param	string	the error line number
	 * @return	string
	 */
	function log_exception($severity, $message, $filepath, $line)
	{
		// Отсылаем сообщение администратору сайта
		$this->adminmail($message);
		
		$severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];
		
		// Дополняем текст сообщения дампам переменных окружения
		$message .= "\n" . $this->GetGlobalVariables() . $filepath.' '.$line . "\n\n";
		
		log_message('error', 'Severity: '.$severity.'  --> '.$message, TRUE);
	}
	
	
	/**
	 * 404 Page Not Found Handler
	 * 
	 * Метод дополняет базовый возможностю отсылать администратору сообщение
	 * в случае возникновения ошибки
	 *
	 * @access	private
	 * @param	string
	 * @return	string
	 */
	function show_404($page = '')
	{	
		// Отсылаем сообщение
		$message = '404 Page Not Found --> '.$page;
		$this->adminmail($message);
		
		parent::show_404($page);
	}
	
	
	/**
    * Возвращает дамп переменных окружения
    *
    * @return string дамп переменных окружения
    */    
    function GetGlobalVariables()
    {
		$content = 'REMOTE_ADDR = '.$_SERVER["REMOTE_ADDR"]."\n";
		
		if (isset($_SERVER["HTTP_REFERER"]))
		{
			$content .= 'HTTP_REFERER = '.$_SERVER["HTTP_REFERER"]."\n";
		}

		$content .= 'USER_AGENT = '.$_SERVER["HTTP_USER_AGENT"]."\n";		
		$content .= '$_SERVER[\'REQUEST_URI\'] = ';
        $content .=  var_export(@$_SERVER['REQUEST_URI'], true);
        $content .= "\n".'$_GET = ';
        $content .=  var_export(@$_GET, true);
        $content .= "\n".'$_POST = ';
        $content .=  var_export(@$_POST, true);
        $content .= "\n";
		
        return $content;
    }
	
	
	/**
	 * Метод отсылает сообщение о ошибки на почту администратору
	 * 
	 * @return 
	 * @param object $message
	 */
	function adminmail($message)
	{
		$CI =& get_instance();
		
		// Если конфиг не загружен - загружаем
		if (!isset($CI->config))
		{
			$CI->config = & load_class('Config');
		}

		// Если загрузчик не загружен - загружаем
		if (!isset($CI->load))
		{
			$CI->load = & load_class('Loader');
		}
		
		// Загружаем класс email
		if (!isset($CI->email))
		{
			$CI->email = & load_class('Email');
		}
		
		// Для почтового класса необходим класс Language
		if (!isset($CI->lang))
		{
			$CI->lang = & load_class('Language');
		}
		
		// Почтовый адрес администратора
		$email = $CI->config->item('log_to_email');
		
		// Если адрес не указан - ничего не делаем
		if (!strlen($email))
		{
			return;
		}
		
		
		
		// Отсылаем
		$CI->email->from('noreply');
		$CI->email->to($email);
		$CI->email->subject('Возникла ошибка на сайте');
		$CI->email->message($message);

		$CI->email->send();
	}
}


Не забывайте. что для включения механизма ведения логов необходимо в файле /system/application/config/config.php указать у параметра log_threshold значением '1'.

Вы можете скачать исходники (Файлы CodeIgniter.php и Common.php от CodeIgniter 1.7.2)

Код довольно простой — думаю что все ясно по исходникам.
Если все таки вопросы возникнут — с удовольствием отвечу в комментариях.

UPDATE: Добавление обработчика исключений с помощью хуков


В комментариях Mordred посоветовал использовать хуки вместо того чтобы править файлы ядра для вызова кода установки обработчика исключений и самого обработчика — спасибо за совет, я так и поступил.

Для начала надо включить механизм куков, для этого в файле /system/application/config/config.php параметр enable_hooks устанавливаем в TRUE:
$config['enable_hooks'] = TRUE;


Затем устанавливаем сам хук в файле /system/application/config/hooks.php пишем:
$hook['pre_system'] = array(
                                'class'    => '',
                                'function' => 'addExceptionHandler',
                                'filename' => 'exception_hook.php',
                                'filepath' => 'hooks'
                                );


Данный код означает что при старте системы будет вызыван метод addExceptionHandler из файла /system/application/hooks/exception_hook.php. Вот содержимое этого файла:
if (! defined('BASEPATH')) exit('No direct script access allowed');

	/**
	* Установка обработчика исключений
	*
	*/
	function addExceptionHandler()
	{
		set_exception_handler('_exception_handler2');
	}
	
	
	/**
	* Обработчик исключений
	*
	*/
	function _exception_handler2($errstr)
	{
		$error =& load_class('Exceptions');
		
		$error->adminmail('1');
		
		// Выводим текст об исключении на экран
	    echo $error->show_error('error', nl2br($errstr));
		
		// Should we log the error?  No?  We're done...
		$config =& get_config();
		if ($config['log_threshold'] == 0)
		{
			return;
		}
		
		// Пишем сообщение в лог
		$error->log_exception('Exception', $errstr, '', '');
		
		exit;
	}


В итоге файлы ядра фреймворка остаются нетронутыми.
Скачать новые исходники можно здесь.

Заодно были сделаны небольшие доработки — если ошибка возникала до загрузки контроллера — не срабатывала отсылка уведомлений админу, так как нужные классы для этого загружены не были, теперь стоит проверка и если нужный класс не загружен, то загружаем тогда его, плюс правки по мелочи.

Оригинал статьи
Tags:
Hubs:
Total votes 9: ↑7 and ↓2+5
Comments6

Articles