Подтвердить что ты не робот

Должны ли я перехватывать исключения только для их регистрации?

Следует ли исключать исключения для ведения журнала?

public foo(..)
{
   try
   {
     ...
   } catch (Exception ex) {
     Logger.Error(ex);
     throw;
   }
}

Если у меня есть это место на каждом из моих слоев (DataAccess, Business и WebService), это означает, что исключение регистрируется несколько раз.

Имеет ли смысл делать это, если мои слои находятся в отдельных проектах, и только публичные интерфейсы имеют в них try/catch? Зачем? Почему нет? Есть ли другой подход, который я мог бы использовать?

4b9b3361

Ответ 1

Определенно нет. Вы должны найти правильное место для дескриптора исключения (на самом деле что-то сделать, например, catch-and-not-rethrow), а затем зарегистрировать его. Конечно, вы можете и должны включать в себя всю трассировку стека, но после вашего предложения будет зависеть код от блоков try-catch.

Ответ 2

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

Моя лучшая практика:

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

Ответ 3

Общее правило заключается в том, что вы получаете только исключение, если вы действительно можете что-то сделать. Поэтому на уровне "Бизнес" или "Данные" вы поймаете исключение только в следующей ситуации:

            try
            {
                this.Persist(trans);
            }
            catch(Exception ex)
            {
                trans.Rollback();
                throw ex;
            }

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

На уровне пользовательского интерфейса вы можете реализовать общий обработчик исключений:

Application.ThreadException + = новый ThreadExceptionEventHandler (Application_ThreadException);

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

    static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        LogException(e.Exception);
    }
    static void LogException(Exception ex)
    {
        YYYExceptionHandling.HandleException(ex,
            YYYExceptionHandling.ExceptionPolicyType.YYY_Policy,
            YYYExceptionHandling.ExceptionPriority.Medium,
            "An error has occurred, please contact Administrator");
    } 

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

Кроме того, как напоминание, всегда пытайтесь обрабатывать ошибки - например, делить на 0 - вместо того, чтобы генерировать исключение.

Ответ 4

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

public void connect() throws ConnectionException {
   try {
       File conf = new File("blabla");
       ...
   } catch (FileNotFoundException ex) {
       LOGGER.error("log message", ex);
       throw new ConnectionException("The configuration file was not found", ex);
   }
}

Ответ 5

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

Ответ 6

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

в вашем примере вы ничего не делаете, кроме как поймать исключение, зарегистрировать его и снова выбросить.. почему бы просто не поймать его на самом высоком уровне с помощью одной попытки /catch, а не внутри каждого метода, если все, что вы делаете, - это вести журнал это?

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

Ответ 7

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

Например (псевдокод Java):

public void methodWithDynamicallyGeneratedSQL() throws SQLException {
    String sql = ...; // Generate some SQL
    try {
        ... // Try running the query
    }
    catch (SQLException ex) {
        // Don't bother to log the stack trace, that will
        // be printed when the exception is handled for real
        logger.error(ex.toString()+"For SQL: '"+sql+"'");
        throw ex;  // Handle the exception long after the SQL is gone
    }
}

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

Если это единственное, что он делает, я думаю, что лучше удалить try/catch из этих классов и позволить исключению быть поднятым классу, ответственному за их обработку. Таким образом, вы получаете только один журнал за исключение, предоставляя вам более четкие журналы и даже вы можете записывать стек, чтобы вы не пропустили, откуда возникло исключение.

Ответ 11

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

Ответ 12

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

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

public foo(..)
{
   try
   {
     ...
   }
   catch (NullReferenceException ex) {
     DoSmth(e);
   }
   catch (ArgumentExcetion ex) {
     DoSmth(e);
   }
   catch (Exception ex) {
     DoSmth(e);
   }
}

Ответ 13

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

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

Я использую этот шаблон для бизнес-уровней приложений, использующих веб-службы Remoting или ASMX. С WCF вы можете перехватывать и регистрировать исключение, используя IErrorHandler, прикрепленный к вашему ChannelDispatcher (другой предмет целиком), поэтому вам не нужен шаблон try/catch/throw.

Ответ 14

Вам нужно разработать стратегию обработки исключений. Я не рекомендую улов и ретрол. В дополнение к избыточным записям журнала это делает код более трудным для чтения. Рассмотрим запись в журнал конструктора для исключения. Это резервирует try/catch для исключений, из которых вы хотите восстановить; что делает код более удобным для чтения. Чтобы справиться с неожиданными или невосстановимыми исключениями, вам может потребоваться попытка/улов рядом с самым внешним слоем программы для записи диагностической информации.

Кстати, если это С++, ваш блок catch создает копию объекта исключения, который может быть потенциальным источником дополнительных проблем. Попробуйте поймать ссылку на тип исключения:

  catch (const Exception& ex) { ... }

Ответ 15

This Software Rising podcast - очень хорошая справочная информация о лучших методах обработки ошибок. На самом деле есть 2 лекции.