Разумнее ли регистрировать исключения в универсальном или базовом классе исключений?

15

Я нахожусь в процессе рефакторинга довольно большого веб-приложения. Одна из главных проблем - непоследовательная обработка ошибок, и я пытаюсь придумать разумную стратегию. Я создал собственный обработчик ошибок с помощью set_error_handler, который по существу превращает ошибки PHP в ErrorExceptions и пользовательский базовый класс исключений, который напрямую наследуется от Exception .

На производстве я использую универсальную ловушку исключений через set_exception_handler , и я собираюсь добавить регистрацию исключений * в смесь. Моя дилемма заключается в том, где вести реальную регистрацию, в базовом классе исключений или во всеобъемлющем.

Я подумал о нескольких причинах, чтобы войти в это в ловушку:

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

и одна из причин входа в базовый класс исключений:

  • В настоящее время универсальное оборудование используется только на производстве. Было бы легко представить его в других наших средах (разработка, тестирование), но для этого потребовалось бы внести несколько корректировок, поскольку ошибки обрабатываются по-разному для каждой среды, так как на производстве они переводятся на страницы ошибок 404/503.

Есть ли приемлемая практика для регистрации исключений?

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


Некоторые пояснения, побуждаемые @ unholysampler в ответ :

Я сталкиваюсь с 2 * 10 ^ 6 sloc кодовой базой, со множеством сторонних материалов, которые я не могу контролировать, и часть кода, который у меня есть, контролирует исключения предшествующих дат в PHP. И есть еще какой-то дерьмовый недавний код, мы выздоравливаем после длительного периода интенсивного давления, когда нам практически пришлось перестать думать и просто взломать.

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

Основной мотивацией для ведения журнала является получение электронного письма на мой телефон всякий раз, когда на производстве происходит что-то плохое. Мне все равно, будут ли огромные дампы данных, если они будут, у меня будет работа cron, которая время от времени удаляет старые.

Яннис
источник

Ответы:

11

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

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

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

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

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

unholysampler
источник
Я добавил некоторые пояснения по вопросу, вызванному вашим ответом. Из того, что я понял, с практической стороны вопроса, который вы предлагаете, чтобы войти в ловушку?
Яннис
1
@YannisRizos: Да, вы должны реализовать универсальный подход в качестве первого шага. То, что я сказал о всеобъемлющем, было сделано для того, чтобы вы не использовали его как нормальную часть вашего потока кода. Реализация обработчика необработанных исключений важна, потому что он позволяет вам получать много информации каждый раз, когда ваш код делает что-то плохое.
unholysampler
Нет ли каркасов ведения журналов, которые могли бы удобно обрабатывать концепцию «Вот куча вещей, которые должны регистрироваться, если они не будут заменены»? Каждый слой, который видит исключение, может заменить данные предыдущего, за исключением того, что, если новое исключение возникнет в процессе разматывания стека, последний отчет журнала не будет заменен и, следовательно, будет записан. Разве фреймворки не поддерживают такой шаблон?
суперкат
3

Если ваш язык / среда выполнения не позволяют вам легко определить источник исключения, есть случай, чтобы записать его в очередь. C ++ и некоторые движки JS не предоставляют файл + строка или стек вызовов исключения к тому времени, когда вы его поймали / но эта информация доступна на момент создания исключения.

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

JBRWilkinson
источник
+1 Это определенно хороший случай для регистрации на выходе ... PHP действительно обеспечивает полную трассировку стека, хотя, вероятно, я пойду другим путем ...
yannis