.NET - каков наилучший способ реализовать “обработчик всех исключений”
-
03-07-2019 - |
Вопрос
Мне интересно, как лучше всего сделать так, чтобы "если все остальное не сработает, поймай это".
Я имею в виду, что вы обрабатываете как можно больше исключений в своем приложении, но все равно там обязательно будут ошибки, поэтому мне нужно иметь что-то, что улавливает все необработанные исключения, чтобы я мог собирать информацию и хранить храните их в базе данных или отправляйте в веб-службу.
Захватывает ли событие AppDomain.CurrentDomain.UnhandledException все?Даже если приложение многопоточное?
Боковое примечание:Windows Vista предоставляет встроенные функции API, которые позволяют любому приложению восстанавливать себя после сбоя...сейчас не могу вспомнить это название...но я бы предпочел не используйте его, как многие наши пользователи по-прежнему используют Windows ХР.
Решение
Я только что поиграл с поведением необработанного исключения AppDomain, (это последний этап, на котором регистрируется необработанное исключение)
Да, после обработки обработчиками событий ваше приложение будет закрыто и the nasty "...показано диалоговое окно "программа перестала работать".
:) Ты все еще можно избежать этого.
Проверьте:
class Program
{
void Run()
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Console.WriteLine("Press enter to exit.");
do
{
(new Thread(delegate()
{
throw new ArgumentException("ha-ha");
})).Start();
} while (Console.ReadLine().Trim().ToLowerInvariant() == "x");
Console.WriteLine("last good-bye");
}
int r = 0;
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Interlocked.Increment(ref r);
Console.WriteLine("handled. {0}", r);
Console.WriteLine("Terminating " + e.IsTerminating.ToString());
Thread.CurrentThread.IsBackground = true;
Thread.CurrentThread.Name = "Dead thread";
while (true)
Thread.Sleep(TimeSpan.FromHours(1));
//Process.GetCurrentProcess().Kill();
}
static void Main(string[] args)
{
Console.WriteLine("...");
(new Program()).Run();
}
}
P.S. Обработайте необработанное для приложения.Исключение ThreadException (WinForms) или DispatcherUnhandledException (WPF) на более высоком уровне.
Другие советы
В ASP.NET вы используете функцию Application_Error
в файле Global.asax
.
В WinForms вы используете MyApplication_UnhandledException
в файле ApplicationEvents
Обе эти функции вызываются, если в вашем коде возникает необработанное исключение. Вы можете зарегистрировать исключение и представить пользователю хорошее сообщение с помощью этих функций.
Для приложений Winform, в дополнение к AppDomain.CurrentDomain.UnhandledException, я также использую Application.ThreadException и Application.SetUnhandledExceptionMode (w / UnhandledExceptionMode.CatchException). Эта комбинация, кажется, ловит все.
В основном потоке у вас есть следующие опции:
- Консольное или сервисное приложение:
AppDomain.CurrentDomain.UnhandledException
- Приложение WinForms:
Application.ThreadException
- Веб-приложение:Глобальный.asax's
Application_Error
Для других потоков:
- Вторичные потоки не имеют необработанных исключений;использование Безопасная резьба
- Рабочие потоки:(таймер, пул потоков) здесь вообще нет никакой системы безопасности!
Имейте в виду, что эти события не ручка исключения, они просто Сообщить их в приложение - часто тогда, когда уже слишком поздно делать с ними что-либо полезное / разумное
Протоколирование исключений - это хорошо, но мониторинг приложений лучше ;-)
Предостережение:Я являюсь автором книги Безопасная резьба Статья.
Для WinForms не забудьте также присоединить к событию необработанного исключения текущего потока (особенно если вы используете многопоточность).
Некоторые ссылки на лучшие практики здесь и здесь и здесь (вероятно, лучшая статья по обработке исключений для .net)
Есть еще классная штука под названием ЭЛЬМА это будет регистрировать любые ASP.NET ошибки, возникающие в веб-приложении.Я знаю, что вы спрашиваете о решении для приложений Winform, но я почувствовал, что это может быть полезно всем, кому нужны подобные вещи в веб-приложении.Мы используем его там, где я работаю, и это было очень полезно при отладке (особенно на производственных серверах!).
Вот некоторые функции, которые у него есть (взяты прямо со страницы):
- Протоколирование почти всех необработанных исключений.
- Веб-страница для удаленного просмотра всего журнала перекодированных исключений.
- Веб-страница для удаленного просмотра полной информации о любом зарегистрированном исключении.
- Во многих случаях вы можете просмотреть исходный желтый экран смерти, который ASP.NET сгенерирован для данного исключения, даже при отключенном режиме customErrors .
- Уведомление по электронной почте о каждой ошибке в момент ее возникновения.
- RSS-канал с последними 15 ошибками из журнала.
- Ряд резервных реализаций хранилища для журнала, включая in-memory, Microsoft SQL Server и несколько, внесенных сообществом.
Вы можете отслеживать большинство исключений в этом обработчике даже в многопоточных приложениях, но .NET (начиная с 2.0) не позволит вам отменить необработанные исключения, если вы не включите режим совместимости 1.1. Когда это произойдет, AppDomain будет закрыт, несмотря ни на что. Лучшее, что вы можете сделать, - запустить приложение в другом домене приложений, чтобы вы могли обработать это исключение и создать новый домен приложений для перезапуска приложения. Р>
Я использую следующий подход, который работает и значительно сокращает объем кода (однако я не уверен, есть ли лучший способ или какие в нем могут быть подводные камни.Всякий раз, когда ты звонишь:Я полагаю, что вопросы, ставящие минусы, были бы достаточно вежливы, чтобы разъяснить свои действия ;)
try
{
CallTheCodeThatMightThrowException()
}
catch (Exception ex)
{
System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace ();
Utils.ErrorHandler.Trap ( ref objUser, st, ex );
} //eof catch
А вот код обработчика ошибок :Просто чтобы внести ясность- :objUser - это объект, моделирующий пользователей приложения (вы можете получить такую информацию, как доменное имя, отдел, регион и т.д.для целей ведения журнала ILog logger - это объект ведения журнала, напримертот, который выполняет операции протоколирования StackTrace st - объект StackTrace, предоставляющий вам информацию об отладке вашего приложения
using System;
using log4net; //or another logging platform
namespace GenApp.Utils
{
public class ErrorHandler
{
public static void Trap ( Bo.User objUser, ILog logger, System.Diagnostics.StackTrace st, Exception ex )
{
if (ex is NullReferenceException)
{
//do stuff for this ex type
} //eof if
if (ex is System.InvalidOperationException)
{
//do stuff for this ex type
} //eof if
if (ex is System.IndexOutOfRangeException)
{
//do stuff for this ex type
} //eof if
if (ex is System.Data.SqlClient.SqlException)
{
//do stuff for this ex type
} //eof if
if (ex is System.FormatException)
{
//do stuff for this ex type
} //eof if
if (ex is Exception)
{
//do stuff for this ex type
} //eof catch
} //eof method
}//eof class
} //eof namesp
В управляемом приложении GUI по умолчанию исключения, возникающие в потоке GUI, обрабатываются тем, что назначено для Application.ThreadException.
Исключения, возникающие в других потоках, обрабатываются AppDomain.CurrentDomain.UnhandledException.
Если вы хотите, чтобы исключения вашего потока GUI работали так же, как и исключения, не относящиеся к GUI, чтобы они обрабатывались AppDomain.CurrentDomain.UnhandledException, вы можете сделать это:
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
Преимущество перехвата исключений потока в графическом интерфейсе с помощью ThreadException заключается в том, что вы можете использовать опции, позволяющие приложению продолжать работу. Чтобы убедиться, что ни один из файлов конфигурации не переопределяет поведение по умолчанию, вы можете вызвать:
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Вы по-прежнему уязвимы к исключениям из плохо управляемых родных библиотек. Если нативный dll устанавливает свой собственный обработчик, используя Win32 SetUnhandledExceptionFilter, он должен сохранить указатель на предыдущий фильтр и вызвать его тоже. Если этого не произойдет, ваш обработчик не будет вызван.