Многоуровневая архитектура: где я должен реализовать регистрацию ошибок \ обработку?

17

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

Допустим, моя архитектура состоит из следующих трех слоев:

  • Открытый интерфейс (IE контроллер MVC)
  • Уровень домена
  • Уровень доступа к данным

Мой источник путаницы - то, где я должен реализовать регистрацию ошибок \ обработку:

  1. Самым простым решением было бы реализовать ведение журнала на верхнем уровне (IE Public Interface \ MVC Controller). Однако это кажется неправильным, потому что это означает всплытие исключения через разные слои, а затем запись его; вместо того, чтобы регистрировать исключение в его источнике.

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

  3. Другой возможной стратегией является сочетание № 1 и № 2; в результате чего я ловлю определенные исключения на уровне, они, скорее всего, будут выброшены (IE Catching, logging and re-throwing SqlExceptionsв уровне доступа к данным), а затем записываю любые дальнейшие необработанные исключения на верхнем уровне. Однако это также потребовало бы от меня перехватывать и регистрировать каждое исключение на верхнем уровне, потому что я не могу отличить ошибки, которые уже были зарегистрированы / обработаны, от ошибок, которые не были.

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

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

KidCode
источник
1
Один из подходов, который я иногда использую, заключается в создании собственного подкласса Exception (или RuntimeException) и его создании, включая исходное исключение как вложенное. Конечно, это означает, что при захвате исключений на верхних уровнях мне нужно проверить тип исключения (мои собственные исключения просто перебрасываются, другие исключения регистрируются и включаются в новые экземпляры моих исключений). Но я давно работаю соло, поэтому не могу дать «официальный» совет.
SJuan76
4
The easiest solution would be to implement the logging at the top level- сделай это. Регистрация исключений в их источнике не является хорошей идеей, и каждое приложение, с которым я столкнулся, выполняло это для отладки PITA. Ответственность за обработку исключений должна лежать на вызывающей стороне.
Джастин
Кажется, вы имеете в виду какую-то конкретную технику / язык реализации, иначе ваше утверждение «зарегистрированные исключения не отличаются от тех, которые не были» трудно понять. Можете ли вы дать больше контекста?
Врумфондель
@ Vroomfondel - Ты прав. Я использую C #, и обертывание кода в каждом слое try{ ... } catch(Exception ex) { Log(ex); }приведет к тому, что одно и то же исключение будет зарегистрировано на каждом слое. (Также кажется довольно плохой практикой
перехватывать

Ответы:

18

На ваши вопросы:

Самым простым решением было бы реализовать ведение журнала на верхнем уровне.

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

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

Это наполовину правильно. Да, самая полезная информация доступна там. Но я бы порекомендовал поместить все это в объект исключения (если его там еще нет) вместо немедленной регистрации. Если вы регистрируетесь на низком уровне, вам все равно нужно выдать исключение, чтобы сообщить звонящим, что вы не выполнили свою работу. Это заканчивается в нескольких журналах одного и того же события.

Исключения

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

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

Конечно, реальная жизнь иногда мешает

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

  • Если вы пересекаете границу между процессами, часто технически вы не можете передать полный объект исключения, включая трассировку стека. И, конечно, связь может быть потеряна. Итак, вот момент, когда служба должна регистрировать исключения, а затем стараться передавать как можно больше информации о сбоях через линию своему клиенту. Служба должна удостовериться, что клиент получает уведомление об ошибке, либо получая ответ об ошибке, либо запуская таймаут в случае разрыва соединения. Обычно это приводит к тому, что один и тот же сбой регистрируется дважды, один раз внутри службы (с более подробной информацией) и один раз на верхнем уровне клиента.

логирование

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

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

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

  • ОШИБКА: некоторые функции неисправны. Это не обязательно означает, что вся ваша программа потерпела крах, но некоторые задачи не могут быть выполнены. Как правило, у вас есть объект исключения, описывающий сбой.
  • ПРЕДУПРЕЖДЕНИЕ. Произошло что-то странное, но это не привело к сбою какой-либо задачи (обнаружена странная конфигурация, временное нарушение соединения, вызвавшее некоторые попытки и т. Д.)
  • ИНФОРМАЦИЯ: Вы хотите сообщить о значительных действиях программы локальному системному администратору (запустив какой-либо сервис с его конфигурацией и версией программного обеспечения, импортировав файлы данных в базу данных, пользователи войдя в систему, вы поймете ...).
  • DEBUG: Вещи, которые вы, как разработчик, хотите видеть, когда решаете какую-то проблему (но вы никогда не будете знать заранее, что вам действительно нужно в случае той или иной конкретной ошибки - если вы можете предвидеть это, вы исправите ошибку ). Одна вещь, которая всегда полезна, это регистрировать действия на внешних интерфейсах.

В производстве установите уровень журнала на INFO. Результаты должны быть полезны для системного администратора, чтобы он знал, что происходит. Ожидайте, что он вызовет вас для помощи или исправления ошибки для каждой ОШИБКИ в журнале и половине ПРЕДУПРЕЖДЕНИЙ.

Включайте уровень DEBUG только во время реальных сеансов отладки.

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

Ральф Клеберхофф
источник
Спасибо за такой подробный ответ, я действительно ценю часть регистрации.
KidCode
-1

Я готовлюсь к отрицательным голосам, но я собираюсь выйти на конечность и сказать, что я не уверен, что могу согласиться с этим.

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

UnconditionallyReinstateMonica
источник
3
Проблема с сообщением об ошибке по возвращаемым значениям: 1. Если вызывающий абонент заботится о сбоях, он должен проверить специальное значение. Но подождите, это nullпустая строка? Это -1 или любое отрицательное число? 2. Если вызывающий абонент не заботится (то есть не проверяет), это приводит к ошибкам отслеживания, не связанным с первоначальной причиной, например, a NullPointerException. Или хуже: расчет продолжается с неправильными значениями. 3. Если вызывающий объект захочет, но программист не считает, что этот метод не работает, компилятор не напоминает ему. Исключения не имеют этой проблемы, либо вы ловите, либо отбрасываете.
siegi
1
Извините, мне пришлось понизить это. Подход, который вы описываете (замените исключения специальными возвращаемыми значениями), был современным в 1970-х годах, когда возник язык C, и есть много веских причин, по которым современные языки имеют исключения, и для меня главная заключается в том, что Правильное использование исключений значительно упрощает написание надежного кода. И ваши «пузырящиеся исключения вверх [...] - это дополнительное усилие [...]» совершенно неверны: просто ничего не делайте с исключениями, и они сами всплывают.
Ральф Клеберхофф