В настоящее время я занимаюсь рефакторингом большой подсистемы с многоуровневой архитектурой и изо всех сил стараюсь разработать эффективную стратегию регистрации и обработки ошибок.
Допустим, моя архитектура состоит из следующих трех слоев:
- Открытый интерфейс (IE контроллер MVC)
- Уровень домена
- Уровень доступа к данным
Мой источник путаницы - то, где я должен реализовать регистрацию ошибок \ обработку:
Самым простым решением было бы реализовать ведение журнала на верхнем уровне (IE Public Interface \ MVC Controller). Однако это кажется неправильным, потому что это означает всплытие исключения через разные слои, а затем запись его; вместо того, чтобы регистрировать исключение в его источнике.
Регистрация исключения в его источнике, очевидно, является лучшим решением, потому что у меня больше информации. Моя проблема в том, что я не могу перехватить каждое исключение в источнике без перехвата ВСЕХ исключений, а на уровне домена / общедоступного интерфейса это приведет к отлову исключений, которые уже были перехвачены, зарегистрированы и повторно выброшены нижним уровнем ,
Другой возможной стратегией является сочетание № 1 и № 2; в результате чего я ловлю определенные исключения на уровне, они, скорее всего, будут выброшены (IE Catching, logging and re-throwing
SqlExceptions
в уровне доступа к данным), а затем записываю любые дальнейшие необработанные исключения на верхнем уровне. Однако это также потребовало бы от меня перехватывать и регистрировать каждое исключение на верхнем уровне, потому что я не могу отличить ошибки, которые уже были зарегистрированы / обработаны, от ошибок, которые не были.
Очевидно, что это проблема большинства программных приложений, поэтому должно существовать стандартное решение этой проблемы, которое приводит к тому, что исключения регистрируются у источника и регистрируются один раз; однако я просто не вижу, как это сделать сам.
Обратите внимание, что заголовок этого вопроса очень похож на « Регистрация исключений в многоуровневом приложении» , однако в ответах на этот пост не хватает подробностей, и их недостаточно для ответа на мой вопрос.
источник
The easiest solution would be to implement the logging at the top level
- сделай это. Регистрация исключений в их источнике не является хорошей идеей, и каждое приложение, с которым я столкнулся, выполняло это для отладки PITA. Ответственность за обработку исключений должна лежать на вызывающей стороне.try{ ... } catch(Exception ex) { Log(ex); }
приведет к тому, что одно и то же исключение будет зарегистрировано на каждом слое. (Также кажется довольно плохой практикойОтветы:
На ваши вопросы:
Наличие пузыря исключения до верхнего уровня является абсолютно правильным и правдоподобным подходом. Ни один из методов более высокого уровня не пытается продолжить какой-либо процесс после сбоя, который обычно не может быть успешным. И хорошо оборудованное исключение содержит всю информацию, необходимую для регистрации. И ничего не делая с исключениями, вы сохраняете свой код в чистоте и сосредотачиваетесь на основной задаче, а не на сбоях.
Это наполовину правильно. Да, самая полезная информация доступна там. Но я бы порекомендовал поместить все это в объект исключения (если его там еще нет) вместо немедленной регистрации. Если вы регистрируетесь на низком уровне, вам все равно нужно выдать исключение, чтобы сообщить звонящим, что вы не выполнили свою работу. Это заканчивается в нескольких журналах одного и того же события.
Исключения
Моя основная рекомендация - ловить и регистрировать исключения только на верхнем уровне. И все уровни ниже должны убедиться, что вся необходимая информация о сбоях перенесена на верхний уровень. В однопроцессном приложении, например в Java, это в основном означает не пытаться / ловить или регистрировать вообще вне верхнего уровня.
Иногда требуется, чтобы некоторая контекстная информация была включена в журнал исключений, который недоступен в исходном исключении, например, оператор SQL и параметры, которые были выполнены при возникновении исключения. Затем вы можете перехватить исходное исключение и повторно выдать новое, содержащее исходное и контекст.
Конечно, реальная жизнь иногда мешает
В Java иногда приходится перехватывать исключение и заключать его в другой тип исключения только для того, чтобы подчиняться некоторым сигнатурам фиксированных методов. Но если вы повторно выбросите исключение, убедитесь, что оно содержит всю информацию, необходимую для последующей регистрации.
Если вы пересекаете границу между процессами, часто технически вы не можете передать полный объект исключения, включая трассировку стека. И, конечно, связь может быть потеряна. Итак, вот момент, когда служба должна регистрировать исключения, а затем стараться передавать как можно больше информации о сбоях через линию своему клиенту. Служба должна удостовериться, что клиент получает уведомление об ошибке, либо получая ответ об ошибке, либо запуская таймаут в случае разрыва соединения. Обычно это приводит к тому, что один и тот же сбой регистрируется дважды, один раз внутри службы (с более подробной информацией) и один раз на верхнем уровне клиента.
логирование
Я добавляю несколько предложений о ведении журнала в целом, а не только об исключении.
Помимо исключительных ситуаций, вы хотите, чтобы важные действия вашего приложения также регистрировались в журнале. Итак, используйте каркас журналирования.
Будьте осторожны с уровнями журналов (чтение журналов, в которых отладочная информация и серьезные ошибки не помечены как разные, соответственно, является проблемой!). Типичные уровни журнала:
В производстве установите уровень журнала на INFO. Результаты должны быть полезны для системного администратора, чтобы он знал, что происходит. Ожидайте, что он вызовет вас для помощи или исправления ошибки для каждой ОШИБКИ в журнале и половине ПРЕДУПРЕЖДЕНИЙ.
Включайте уровень DEBUG только во время реальных сеансов отладки.
Сгруппируйте записи журнала в соответствующие категории (например, по полному имени класса кода, который генерирует запись), что позволяет вам включать журналы отладки для определенных частей вашей программы.
источник
Я готовлюсь к отрицательным голосам, но я собираюсь выйти на конечность и сказать, что я не уверен, что могу согласиться с этим.
Выделение исключений, а тем более их повторная регистрация - это дополнительные усилия с небольшой выгодой. Поймать исключение в источнике (да, самое простое), зарегистрировать его, но затем не выбрасывать исключение, просто сообщить об ошибке «вызывающему». «-1», ноль, пустая строка, некоторое перечисление, что угодно. Звонящий должен только знать, что вызов не удался, почти никогда не бывает ужасных деталей. И это будет в вашем журнале, верно? В том редком случае, когда вызывающему абоненту нужны подробности, действуйте и всплывают, но не в качестве автоматического бездумного значения по умолчанию.
источник
null
пустая строка? Это -1 или любое отрицательное число? 2. Если вызывающий абонент не заботится (то есть не проверяет), это приводит к ошибкам отслеживания, не связанным с первоначальной причиной, например, aNullPointerException
. Или хуже: расчет продолжается с неправильными значениями. 3. Если вызывающий объект захочет, но программист не считает, что этот метод не работает, компилятор не напоминает ему. Исключения не имеют этой проблемы, либо вы ловите, либо отбрасываете.