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

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

try
{
    // do main work
}
catch(Exception ex)
{
    // log exception info
}

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

Я удалил этот верхний уровень try / catch, по крайней мере, пока я все еще кодировал и тестировал. Но теперь моя задача выполнена, и я должен решить, вставлять ли ее обратно для релиза или нет. Я думаю, что мне следует это сделать, потому что это делает сервис более стабильным, и весь смысл в том, что он работает в фоновом режиме без какого-либо надзора. С другой стороны, я читал, что нужно вызывать только определенные исключения (как в IoException), а не в общем Exception.

Что вы посоветуете по этому поводу?

Кстати, проект написан на C #, но меня также интересуют ответы для языков, отличных от .NET.


person Treb    schedule 05.03.2009    source источник
comment
См. Также stackoverflow.com/questions/576532/   -  person Brian    schedule 06.03.2009


Ответы (5)


Положить его обратно.

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

Ведение журнала в порядке.

Вы можете дополнительно использовать символ DEBUG, определенный Visual Studio для отладочных сборок, в качестве флага.

    ...
    } catch( Exception e ) { 
#if DEBUG
          throw;
#else
          log as usual 
#endif
    }

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

person OscarRyz    schedule 05.03.2009
comment
Разве ему не следует использовать «бросить», а не «бросить е»? В противном случае трассировка стека очищается (или что-то в этом роде - не совсем уверен). - person strager; 06.03.2009
comment
@strager: Ага! В приведенном выше примере будет замаскировано исходное исключение. Просто используйте "бросить". - person Theo Lenndorff; 06.03.2009
comment
@strager & RunningMonkey: вот так? - person OscarRyz; 06.03.2009
comment
Почему переменная с именем debug, а не директива #ifdef DEBUG? Последний автоматически определяется для отладочных сборок в Visual Studio. - person Sergio Acosta; 06.03.2009
comment
Я имею в виду, что при использовании #ifdef DEBUG вам не нужно изменять какой-либо код (например, устанавливать для переменной отладки значение true), чтобы отключить выдачу исключения, просто скомпилируйте сборку Release. - person Sergio Acosta; 06.03.2009
comment
@ Оскар: хорошо. Я внес изменения. - person Sergio Acosta; 06.03.2009
comment
@Sergio. Спасибо. Думаю, это больше Си-Шарпи. ;) - person OscarRyz; 06.03.2009

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

Thread.setDefaultUncaughtExceptionHandler( ... );

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

В дополнение к этому, у большинства моих потоков есть попытка / улов, где я поймаю RunnableException (но не Error) и записываю его ... некоторые потоки умрут, когда это произойдет, другие будут регистрировать и игнорировать, другие будут отображать жалоба пользователю и позволить пользователю решать, в зависимости от потребностей Приложения. Эта попытка / уловка находится в самом корне потока, в методе Runnable.run() или эквивалентном. Поскольку try / catch находится в корне потока, нет необходимости иногда отключать этот улов.

Когда я пишу на C #, я кодирую аналогично. Но все зависит от необходимости приложения. Исключение, которое повредит данные? Что ж, не поймите и не игнорируйте это. ВСЕГДА регистрируйте это, но затем дайте приложению умереть. Однако большинство исключений не относятся к этому типу.

person Eddie    schedule 05.03.2009

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

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

person Kenneth Cochran    schedule 05.03.2009

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

person Mohit Chakraborty    schedule 05.03.2009
comment
Я понимаю, откуда вы пришли, но есть отличный класс исключений, когда ваша программа может работать в деградированном состоянии, но все же работать, где вы действительно хотите улавливать и регистрировать исключения. Стабильность имеет значение. Вы просто должны знать, где вы НЕ МОЖЕТЕ этого сделать. - person Eddie; 06.03.2009
comment
@Eddie - Правда, эта область всегда очень сильно зависит от приложений и ожиданий пользователей. Мы все хотели бы создавать надежные и производительные приложения, которые никогда не выходят из строя, но затем требует реальности :). Ничто не сравнится с обширным предварительным тестированием, охватом кода и бета-версиями с реальными клиентами, чтобы получить уверенность. - person Mohit Chakraborty; 06.03.2009

Если вы хотите видеть исключение, когда оно возникает, в Visual Studio вы можете перейти в меню ОТЛАДКА, выбрать ИСКЛЮЧЕНИЯ, и вы можете указать отладчику, чтобы он прерывался, как только возникает исключение. Вы даже можете выбрать тип исключений. :)

person Chris Woodruff    schedule 14.03.2013
comment
И как это работает, когда рассматриваемая программа работает как СЕРВИС? - person Treb; 15.03.2013
comment
Если вы загрузите сервис в память, а затем подключите к нему, все это работает, как вы привыкли. Если он удален (например, служба находится где-то на сервере), вам нужно установить что-то на сервере, чтобы разрешить удаленную отладку. Если вам нужно сделать это при ЗАПУСКЕ, вы можете добавить фиктивную службу в свой исполняемый файл, запустить ТОТ (который загружает EXE в память), подключиться к процессу, а затем запустить другую службу (с полными возможностями отладки). Он работает ОТЛИЧНО. - person Chris Woodruff; 13.06.2013