Регистрация ошибок, плавно

Я читал, в частности, «журнал ошибок». И я придумал функцию «error_log», которая, как представляется, является хорошим инструментом для обработки журнала ошибок. Но как самый гладкий и лучший способ его использовать?

Если у меня есть

try { //try a database connection... } catch (PDOException $e) { error_log($e->getMessage(), 3, "/var/tmp/my-errors.log"); } 

Это приведет к регистрации ошибки в файле my-errors.log. Но что, если мне когда-нибудь понадобится изменить положение файла, новую папку или что-то еще. Если у меня есть тонны файлов, мне нужно их все изменить.

Теперь я начал думать о том, чтобы использовать переменную для установки пути к журналу ошибок. Конечно, это может сработать, но что, если я хочу использовать error_log в методе функции или класса? Тогда мне нужно будет установить переменную как глобальную, но это считается плохой практикой! Но что, если я не буду использовать функцию глубоко в классе, разве это не будет считаться плохой практикой? Какое здесь хорошее решение?

 <?php function legit() { try { if (1 == 1) { throw new Exception('There was an error here'); } } catch (Exception $e) { throw new Exception('throw the error to the try-catch outside the function...'); } } try { legit(); } catch (Exception $e) { echo 'error here' . $e->getMessage(); //log it } 

Это пример того, о чем я говорил выше (не имея регистрации в классе / функции … Это хороший способ?)

Далее:

Я не совсем уверен, как использовать Исключения вообще. Предположим, я хочу сделать INSERT для базы данных с SQL внутри метода, я бы использовал try / catch, а затем перепрограммировал исключение, если он не прошел? Это считается хорошей практикой? Примеры пожалуйста.

Related of "Регистрация ошибок, плавно"

Во-первых, я хотел бы поблагодарить вас за поиск стандартных методов ошибок в PHP. К сожалению, error_log имеет некоторые ограничения, как вы узнали.

Это длинный ответ, который можно прочитать, чтобы узнать о:

  1. ошибки
    • Регистрация ошибки напрямую с помощью trigger_error и set_error_handler
    • Где хорошие ошибки идут плохо – Fatal Errors.
  2. Исключения
    • SPL
    • Что с ними делать?
  3. Код
    • Настроить
    • Применение

TL; DR Используйте trigger_error для повышения ошибок и set_error_handler для их регистрации.

1. Ошибки

Когда в вашей программе все не так, как ожидалось, вы часто захотите поднять ошибку, чтобы кто-то или что-то было уведомлено. Ошибка в ситуации, когда программа может продолжаться, но произошло что-то примечательное, возможно, опасное или ошибочное. На этом этапе многие люди хотят немедленно регистрировать ошибку с помощью своего пакета ведения журнала. Я считаю, что это не то, что нужно делать. Я рекомендую использовать trigger_error для повышения ошибки, чтобы ее можно было обработать с помощью обратного вызова, заданного set_error_handler . Давайте сравним эти параметры:

Регистрация ошибки напрямую

Итак, вы выбрали свой регистрационный пакет. Теперь вы готовы распространять вызовы на ваш регистратор везде, где возникает ошибка в коде. Давайте посмотрим на один вызов, который вы можете сделать (я буду использовать аналогичный регистратор для ответа в ответ Джека):

Logger::getLogger('standard')->error('Ouch, this hurts');

Что вам нужно для запуска этого кода?

     Класс : Logger
     Метод : getLogger
     Возврат : объект с методом 'error'

Это зависимости, которые необходимы для использования этого кода. Каждый, кто хочет повторно использовать этот код, должен будет предоставить эти зависимости. Это означает, что стандартной конфигурации PHP больше не будет достаточно для повторного использования вашего кода. В лучшем случае, используя Dependency Injection, вам по-прежнему требуется, чтобы объект логгера передавался во весь ваш код, который может испускать ошибку.

Кроме того, в дополнение к тому, за что отвечает код, он также несет ответственность за регистрацию ошибки. Это противоречит принципу единой ответственности .

Мы видим, что регистрация ошибки напрямую плоха .

trigger_error для спасения

PHP имеет функцию trigger_error которая может использоваться для повышения ошибки, как это делают стандартные функции. Уровни ошибок, которые вы используете с ним, определяются в константах уровня ошибок . В качестве пользователя вы должны использовать одну из пользовательских ошибок: E_USER_ERROR , E_USER_WARNING или значение по умолчанию E_USER_NOTICE (другие уровни ошибок зарезервированы для стандартных функций и т. Д.). Использование стандартной функции PHP для повышения ошибки позволяет повторить использование кода с любой стандартной установкой PHP! Наш код больше не отвечает за регистрацию ошибки (только убедитесь, что она поднята).

Используя trigger_error мы выполняем только половину процесса регистрации ошибок (повышение ошибки) и сохраняем ответственность за ответ на ошибку для обработчика ошибок, который будет рассмотрен далее.

Обработчик ошибок

Мы устанавливаем собственный обработчик ошибок с set_error_handler функции set_error_handler (см. Настройку кода). Этот настраиваемый обработчик ошибок заменяет стандартный обработчик ошибок PHP, который обычно регистрирует сообщения в журнале ошибок веб-сервера в зависимости от настроек конфигурации PHP. Мы все еще можем использовать этот стандартный обработчик ошибок, возвращая false в наш собственный обработчик ошибок.

У пользовательского обработчика ошибок есть одна ответственность: реагировать на ошибку (включая любые записи, которые вы хотите сделать). Внутри настраиваемого обработчика ошибок у вас есть полный доступ к системе и вы можете запускать любой вид ведения журнала, который вы хотите. Практически любой регистратор, использующий шаблон проектирования Observer, будет в порядке (я не буду вдаваться в это, поскольку я считаю, что он имеет второстепенное значение). Это должно позволить вам подключаться к новым наблюдателям журнала для отправки вывода туда, где вам это нужно.

У вас есть полный контроль над тем, что вам нравится, с ошибками в одной поддерживаемой части вашего кода. Теперь журнал ошибок можно быстро и легко изменить из проекта в проект или в рамках одного проекта со страницы на страницу. Интересно, что даже @ подавленные ошибки делают это для пользовательского обработчика ошибок с errno 0, которая, если уважаемая маска error_reporting не должна сообщаться.

Когда хорошие ошибки идут плохо – фатальные ошибки

Невозможно продолжить некоторые ошибки. Следующие уровни ошибок не могут быть обработаны с помощью специального обработчика ошибок: E_ERROR , E_PARSE , E_CORE_WARNING , E_COMPILE_ERROR , E_COMPILE_WARNING , E_COMPILE_WARNING . Когда эти виды ошибок запускаются при вызове стандартной функции, пользовательский обработчик ошибок пропускается, и система отключается. Это может быть вызвано:

call_this_function_that_obviously_does_not_exist_or_was_misspelt();

Это серьезная ошибка! Из-за этого невозможно восстановить, и система вот-вот отключится. Наш единственный выбор – иметь дело register_shutdown_function с отключением. Однако эта функция выполняется всякий раз, когда скрипт завершается (успешный, а также неудачный). Используя эту и error_get_last можно зарегистрировать некоторую базовую информацию (система почти завершена в этот момент), когда последняя ошибка была фатальной ошибкой. Также может быть полезно отправить правильный код состояния и отобразить страницу типа ошибки внутреннего сервера по вашему выбору.

2. Исключения

Исключения могут быть рассмотрены очень похожими на основные ошибки. Вместо trigger_error исключение будет вызываться вашим кодом (вручную с throw new Exception или со стандартным вызовом функции). Используйте set_exception_handler чтобы определить обратный вызов, который вы хотите использовать для обработки исключения.

SPL

Стандартная библиотека PHP (SPL) предоставляет исключения . Это мой предпочтительный способ создания исключений, потому что, подобно trigger_error они являются стандартной частью PHP, которая не вводит дополнительные зависимости к вашему коду.

Что с ними делать?

Когда выбрано исключение, можно сделать три варианта:

  1. Поймайте его и исправьте (код продолжает, как будто ничего плохого не произошло).
  2. Поймайте его, добавьте полезную информацию и повторите бросок.
  3. Пусть это пузырь до более высокого уровня.

На каждом уровне стека эти варианты сделаны. В конце концов, когда он set_exception_handler самого высокого уровня, будет выполнен обратный вызов, заданный с помощью set_exception_handler . Здесь ваш код регистрации принадлежит (по тем же причинам, что и обработка ошибок), а не распространяется по всем операторам выписок в вашем коде.

3. Код

Настроить

Обработчик ошибок

 function errorHandler($errno , $errstr, $errfile, $errline, $errcontext) { // Perform your error handling here, respecting error_reporting() and // $errno. This is where you can log the errors. The choice of logger // that you use is based on your preference. So long as it implements // the observer pattern you will be able to easily add logging for any // type of output you desire. } $previousErrorHandler = set_error_handler('errorHandler'); 

Обработчик исключений

 function exceptionHandler($e) { // Perform your exception handling here. } $previousExceptionHandler = set_exception_handler('exceptionHandler'); 

Функция выключения

 function shutdownFunction() { $err = error_get_last(); if (!isset($err)) { return; } $handledErrorTypes = array( E_USER_ERROR => 'USER ERROR', E_ERROR => 'ERROR', E_PARSE => 'PARSE', E_CORE_ERROR => 'CORE_ERROR', E_CORE_WARNING => 'CORE_WARNING', E_COMPILE_ERROR => 'COMPILE_ERROR', E_COMPILE_WARNING => 'COMPILE_WARNING'); // If our last error wasn't fatal then this must be a normal shutdown. if (!isset($handledErrorTypes[$err['type']])) { return; } if (!headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } // Perform simple logging here. } register_shutdown_function('shutdownFunction'); 

Применение

ошибки

 // Notices. trigger_error('Disk space is below 20%.', E_USER_NOTICE); trigger_error('Disk space is below 20%.'); // Defaults to E_USER_NOTICE // Warnings. fopen('BAD_ARGS'); // E_WARNING fopen() expects at least 2 parameters, 1 given trigger_error('Warning, this mode could be dangerous', E_USER_WARNING); // Fatal Errors. // This function has not been defined and so a fatal error is generated that // does not reach the custom error handler. this_function_has_not_been_defined(); // Execution does not reach this point. // The following will be received by the custom error handler but is fatal. trigger_error('Error in the code, cannot continue.', E_USER_ERROR); // Execution does not reach this point. 

Исключения

Каждый из трех вариантов из предыдущего перечислены здесь в общем виде, исправляют, присоединяют к нему и позволяют ему пузыриться.

1 Fixable:

 try { $value = code_that_can_generate_exception(); } catch (Exception $e) { // We decide to emit a notice here (a warning could also be used). trigger_error('We had to use the default value instead of ' . 'code_that_can_generate_exception\'s', E_USER_NOTICE); // Fix the exception. $value = DEFAULT_VALUE; } // Code continues executing happily here. 

2 Добавить:

Ниже следует, как code_that_can_generate_exception() не знает о $context . Блок catch на этом уровне имеет больше информации, которую он может добавить к исключению, если он полезен путем его повторного создания.

 try { $context = 'foo'; $value = code_that_can_generate_exception(); } catch (Exception $e) { // Raise another exception, with extra information and the existing // exception set as the previous exception. throw new Exception('Context: ' . $context, 0, $e); } 

3 Пусть это пузырится вверх:

 // Don't catch it. 

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

преамбула

Обработка ошибок обычно не является первым, о чем вам следует подумать при написании приложения; как косвенный результат, он возникает при необходимости. Тем не менее, не нужно много стоить, чтобы использовать существующие механизмы в PHP.

Это довольно длинная статья, поэтому я разбил ее на логические наборы текста.

Ошибки запуска

В PHP существует два разных способа получения ошибок:

  1. Ошибки самого PHP (например, использование неопределенных переменных) или внутренних функций (например, imagecreatefromjpeg не может открыть файл),
  2. Ошибки, вызванные кодом пользователя с помощью trigger_error ,

Они обычно печатаются на вашей странице (если display_errors не отключен, или error_reporting равно нулю), который должен быть стандартным для производственных машин, если вы не напишете идеальный код, например, я … движется дальше); эти ошибки также могут быть захвачены, что дает вам представление о любых ошибках в коде, используя set_error_handler объясненный set_error_handler .

Бросание исключений

Исключения отличаются от ошибок тремя основными способами:

  1. Код, который их обрабатывает, может быть удален от места, из которого они выбраны. Состояние переменной в начале координат должно быть явно передано в конструктор Exception, иначе у вас будет только трассировка стека.
  2. Код между исключением и catch полностью пропущен, тогда как после возникновения ошибки (и она не была фатальной) код все равно продолжается.
  3. Они могут быть расширены из основного класса Exception ; это позволяет вам поймать и обработать определенные исключения, но пусть другие пузырят стек, пока они не поймаются другим кодом. См. Также: http://www.php.net/manual/en/language.exceptions.php

Пример исключения исключений приведен ниже.

Обработка ошибок

Захват и обработка ошибок довольно просто, зарегистрировав обработчик ошибок, например:

 function my_error_handler($errno, $errstr, $errfile = 'unknown', $errline = 0, array $errcontext = array()) { // $errcontext is very powerful, it gives you the variable state at the point of error; this can be a pretty big variable in certain cases, but it may be extremely valuable for debugging // if error_reporting() returns 0, it means the error control operator was used (@) printf("%s [%d] occurred in %s:%d\n%s\n", $errstr, $errno, $errfile, $errline, print_r($errcontext, true)); // if necessary, you can retrieve the stack trace that led up to the error by calling debug_backtrace() // if you return false here, the standard PHP error reporting is performed } set_error_handler('my_error_handler'); 

Для ударов вы можете превратить все ошибки в ErrorException , зарегистрировав следующий обработчик ошибок (PHP> = 5.1):

 function exception_error_handler($errno, $errstr, $errfile, $errline) { throw new ErrorException($errstr, $errno, 0, $errfile, $errline); } set_error_handler("exception_error_handler"); 

Обработка исключений

В большинстве случаев вы обрабатываете исключения как можно ближе к коду, который заставлял его разрешать планы резервного копирования. Например, вы пытаетесь вставить запись базы данных, а исключение ограничения первичного ключа выбрано; вы можете восстановить, обновив запись вместо этого (надуманный, поскольку большинство баз данных могут справиться с этим сами). Некоторые исключения просто не могут обрабатываться локально, поэтому вы хотите, чтобы они каскадировались. Пример:

 function insertRecord($user, $name) { try { if (true) { throw new Exception('This exception should not be handled here'); } // this code is not executed $this->db->insert('users', array('uid' => $user, 'name' => $name)); } catch (PDOException $e) { // attempt to fix; an exception thrown here will cascade down throw $e; // rethrow exception // since PHP 5.3.0 you can also nest exceptions throw new Exception("Could not insert '$name'", -1, $e); } catch (WhatEverException $e) { // guess what, we can handle whatever too } } 

Скользкое исключение

Итак, что происходит, когда вы ничего не поймаете? Вы можете поймать это, используя set_exception_handler .

 function my_exception_handler(Exception $exception) { // do your stuff here, just don't throw another exception here } set_exception_handler('my_exception_handler'); 

Это не рекомендуется, если у вас нет значимого способа обработки исключения в любом месте вашего кода.

Регистрация ошибки / исключения

Теперь, когда вы обрабатываете ошибку, вы должны ее зарегистрировать где-нибудь. Для моего примера я использую проект, который Apache портировал с Java на PHP, называемый LOG4PHP . Есть и другие, но это иллюстрирует важность гибкого механизма ведения журнала.

Он использует следующие понятия:

  1. Регистраторы – именованные объекты, которые выполняют регистрацию от вашего имени; они могут быть конкретными для класса в вашем проекте или использоваться как общий регистратор,
  2. Appenders – каждый запрос журнала может быть отправлен одному или нескольким адресатам (электронной почте, базе данных, текстовому файлу) на основе предопределенных условий (таких как уровень журнала)
  3. Уровни – журналы классифицируются по сообщениям отладки на фатальные ошибки.

Основное использование для иллюстрации различных уровней сообщений:

 Logger::getLogger('main')->info('We have lift off'); Logger::getLogger('main')->warn('Rocket is a bit hot'); Logger::getLogger('main')->error('Houston, we have a problem'); 

Используя эти концепции, вы можете смоделировать довольно мощное средство ведения журнала; например, без изменения кода, вы можете реализовать следующую настройку:

  1. Собирайте все отладочные сообщения в базе данных для разработчиков, чтобы посмотреть; вы можете отключить это на производственном сервере,
  2. Собирайте предупреждения в ежедневный файл, который вы можете отправить по электронной почте в конце дня,
  3. Имейте немедленные электронные письма, отправленные по фатальным ошибкам.

Определите его, затем используйте его 🙂

 define('ERRORLOG_PATH', '/var/tmp/my-errors.log'); error_log($e->getMessage(), 3, ERRORLOG_PATH); 

В качестве альтернативы просто добавьте третий параметр error_log по error_log , по умолчанию.

Если вам по-прежнему нужен собственный способ обработки журналов (т. Е. Вы не хотите использовать стандартный trigger_error ()), я бы рекомендовал посмотреть Zend_Log (http://framework.zend.com/manual/en/zend.log .overview.html) по следующим причинам:

  1. это может быть использовано как отдельный компонент, ZF не является полнотекстовой структурой. Вы можете копировать только пространства имен Zend_Loader и Zend_Log, создавать экземпляр Zend_Loader и использовать его. Смотри ниже:

     require_once('Zend/Loader/Autoloader.php'); $loader = Zend_Loader_Autoloader::getInstance(); $logger = new Zend_Log(); $writer = new Zend_Log_Writer_Stream('php://output'); $logger->addWriter($writer); $logger->log('Informational message', Zend_Log::INFO); 
  2. Вам предложили много журнальных библиотек, но я считаю, что команда Zend (основатели PHP lang) знают, что они делают

  3. Вы можете использовать любые авторы (база данных, STDOUT – см. Выше, файл, независимо от того, вы можете настроить его, чтобы написать свой собственный, чтобы отправлять сообщения журнала на веб-службу даже)
  4. уровни журналов
  5. может изменить формат журнала (но тот, который из коробки отлично подходит моему разуму). Вышеприведенный пример со стандартным форматированием приведет к следующим:

    2012-05-07T23: 57: 23 + 03: 00 INFO (6): Информационное сообщение

  6. просто прочитайте ссылку, он может быть настроен на обнаружение ошибок php

В качестве дополнения, для регистрации ошибок (и, фактически, для всего журнала) я использовал бы диспетчер событий, как это делает инфраструктура symfony.

Взгляните на этот sf-компонент (его очень легкая зависимость, целая структура не требуется, возможно, 3 соответствующих php-класса и 2 интерфейса)

https://github.com/symfony/EventDispatcher

таким образом вы можете создать диспетчер где-нибудь в загрузочном буфере приложения:

 use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\Event; $dispatcher = new EventDispatcher(); //register listeners $dispatcher->addListener('application.log', function (Event $event) { //do anything you want }); 

Затем вы можете поднять мероприятие в любом месте своего кода с помощью чего-то вроде

 $dispatcher->dispatch(new GenericEvent('application.log', array('message' => 'some log', 'priority' => 'high')); 

Конечно, вы можете подклассифицировать класс событий своими собственными событиями:

 class LogEvent extends GenericEvent { public function __construct($message, $priority = 'INFO') { parent::__construct('application.log', array('message'=>$message,'priority'=>$priority)); } public function getMessage() { return $this->getArgument('message'); } public function getPriority() { return $this->getArgument('priority'); } } // now raising LogEvent is much cleaner: $dispatcher->dispatch(new LogEvent('some log')); 

Это также позволит вам создавать более настраиваемые события, такие как ExceptionEvent

  class ExceptionEvent extends GenericEvent { public function __construct(Exception $cause) { parent::__construct('exception.event', array('cause' => $cause)); } } 

И обрабатывайте их соответственно.

преимущества

  • вы отделяете логику регистрации от приложения
  • вы можете легко добавлять и удалять регистраторы во время выполнения
  • вы можете легко зарегистрировать столько логистов, которые вы хотите (то есть DebugLogger, который записывает все в текстовый файл, ErrorLogger, который регистрирует только ошибки в error_log, CriticalLogger, который регистрирует только критические ошибки в рабочей среде и отправляет их по электронной почте администратору и т. д.),
  • вы можете использовать диспетчер событий для большего количества вещей, чем просто ведение журнала (фактически для каждого задания, для которого шаблон наблюдателя подходит)
  • фактический регистратор становится не чем иным, как «деталью реализации» – так легко заменить, что неважно, куда идут ваши журналы – вы сможете в любой момент заменить место назначения журнала без необходимости рефакторинга имен ваших методов или изменения чего-либо в коде.
  • будет легко реализовать сложную логическую логику маршрутизации или глобально изменить формат журнала (путем настройки регистраторов)
  • все становится еще более гибким, если вы используете инъекцию зависимостей как для слушателей (регистраторов), так и для диспетчера (в классы, которые уведомляют событие журнала)

Фактическая регистрация

Как уже сказал кто-то, я бы посоветовал пойти с готовой библиотекой, например, упомянутым Monolog, Zend_Log или log4php. Вероятно, нет причин закодировать эти вещи вручную (и последнее, что вы хотите, это сломанный логгер ошибок !)

PS: Рассматривайте фрагменты кода как псевдокод, я их не тестировал. Подробности можно найти в документах упомянутых библиотек.

Если PHP-метод обработки ошибок недостаточно для вас (например, иногда вы хотите регистрироваться в базе данных, иногда в файл, иногда что-то еще), вам нужно использовать / создать собственную среду ведения журнала PHP.

Вы можете просмотреть обсуждение в https://stackoverflow.com/questions/341154/php-logging-framework или просто пойти и дать лучший выбор, KLogger , попробовать. Я не уверен, однако, если он поддерживает настраиваемые адресаты для ведения журнала. Но, по крайней мере, это небольшой и удобный для чтения класс, и вы должны расширить его для своих нужд.

Я бы пошел с журнальным решением Tom vand der Woerdt, самым простым и эффективным для ваших требований.

Что касается другого вопроса:

Вам не нужно улавливать / отменять исключение внутри функции, если не существует определенного исключения, для которого у вас есть решение.

Несколько упрощенный пример:

 define('ERRORLOG_PATH', '/var/tmp/my-errors.log'); function do_something($in) { if (is_good($in)) { try { return get_data($in); } catch (NoDataException $e) { // Since it's not too big a deal that nothing // was found, we just return false. return false; } } else { throw new InvalidArguementException('$in is not good'); } } function get_data($data) { if (!is_int($data)) { InvalidArguementException('No'); } $get = //do some getting. if (!$get) { throw new NoDataException('No data was found.'); } else { return $get; } } try { do_something('value'); } catch (Exception $e) { error_log($e->getMessage(), 3, ERRORLOG_PATH); die ('Something went wrong :('); } 

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

Очевидно, что если вы снова выбросите Exception (вне первоначальной try {} или в верхнем catch {} ), ваш скрипт выйдет с ошибкой Uncaught Exception, и регистрация ошибок будет потеряна.

Если вы хотите пройти весь путь, вы также можете реализовать пользовательскую функцию обработки ошибок с помощью set_error_handler() и поместить туда свою регистрацию.

Есть две проблемы для встречи. Первое заключается в гибкости при регистрации в разных каналах. В этом случае вы должны взглянуть, например, на Монолога .

Вторая задача состоит в том, чтобы сплести при входе в ваше приложение. Imho лучшим случаем является не использование журналирования явно. Здесь, к примеру, удобна аспектная ориентация . Хорошим образцом является поток3 .

Но это более птичий взгляд на проблему …

Я использую свою собственную функцию, которая позволяет мне записывать несколько типов файлов журнала, задавая или изменяя второй параметр.

Я прохожу мимо концептуальных вопросов, которые вы задаете «для чего это правильно», включив функцию журнала в библиотеку функций, которые я считаю «родными» для моих проектов разработки. Таким образом, я могу рассматривать эти функции как часть «MY» php core, например date() или time()

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

 function dlog($message,$type="php-dlog") { if(!is_array($message) ) $message=trim($message); error_log(date("m/d/Y h:i:s").":".print_r($message,true)."\n",3, "/data/web/logs/$_SERVER[HTTP_HOST]-$type.log"); } 

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

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

Во время разработки, когда установлены display_errors, они ничего не регистрируют, поскольку все ошибки возникают в браузере.

И в качестве побочного примечания к этому: не делайте свой собственный обработчик ошибок, исключая исключения! Это действительно плохая идея. Это может вызвать ошибки в обработчике буфера и в некоторых расширениях. Кроме того, некоторые основные функции PHP, такие как fopen (), приводят к предупреждению или уведомлению о сбое, они должны рассматриваться соответствующим образом и не должны останавливать применение исключения.

Упоминание о том, что обработчик ошибок, бросающий исключения в документации PHP, является ошибкой заметок.

Как заявляет KNL, что является совершенно правильным, но, к сожалению, пока еще не документировано, ошибки, бросающие исключения, не являются чем-то рекомендованным разработчиками PHP, и кто-то ошибся в документации. Это может привести к ошибкам со многими расширениями, поэтому не делайте этого.

Это уже обсуждалось на #PHP на irc.

«Однако ошибки могут быть просто переведены на исключения с ErrorException». на http://php.net/manual/en/language.exceptions.php будет удален.