Описание проблемы
Представим, что из-за какой-то проблемы в приложении случилось необработанное исключение.
Что делать?
Большинство приложений делают следующее:
Почему это плохо:
Решение
Необходимо научить приложение передавать сведения о необработанных исключениях в систему мониторинга. Система мониторинга автоматически переведёт соответствующий компонент приложения в статус Warning. Технической поддержке будет легче исправить ошибку, так как в системе мониторинга будут подробные сведения об проблеме (стек, url запроса, логин пользователя и т.д.).
На странице ошибки нужно показывать номер ошибки, чтобы в случае обращения в техническую поддержку было точно понятно, о какой проблеме сообщил пользователь.
Реализация
Для обработки исключений в приложениях ASP.NET MVC используется метод Application_Error в Global.asax. Реализуем отправку ошибок в систему мониторинга в этом методе:
protected void Application_Error(object sender, EventArgs e) { var exception = Server.GetLastError(); if (exception == null) return; // обработка ошибки 404 - страница не найдена // источником ошибок 404 часто бывают боты, которые что то ищут на сайте // такие ошибки не будем считать ошибками нашего приложения var httpException = exception as HttpException; if (httpException != null) { if (httpException.GetHttpCode() == 404) { ShowErrorPage("Error404.cshtml", exception); return; } } // остальные ошибки залогируем LogManager.GetCurrentClassLogger().Error(exception); // получим уникальный номер ошибки в Zidium, чтобы показать его пользователю var errorNumber = Client.Instance.ExceptionRender.GetExceptionTypeCode(exception); // покажем страницу с текстом ошибки и номером ShowErrorPage("Error.cshtml", exception, errorNumber); }
Если Ваше приложение доступно в сети Интернет, то разные боты будут переодически создавать ошибки 404. Чтобы ошибки 404 не отвлекали, код выше их игнорирует (не отправляет в систему мониторинга).
Ошибки записываются в NLog, а подключенный адаптер ошибок (Zidium.Errors) создаёт в Zidium событие-ошибку.
Чтобы было проще разобраться в причине ошибки, будем передавать параметры HTTP-запроса:
Заполнением свойств всех событий будет заниматься класс EventPreparer. Данный класс нужен, чтобы в каждом месте кода, которое отправляет ошибки (события), не писать одинаковый код по заполнению свойств.
/// <summary> /// Заполняет свойства событий (ошибок) параметрами HTTP-запроса /// </summary> public class EventPreparer : IEventPreparer { public void Prepare(SendEventBase eventObj) { var httpContext = HttpContext.Current; if (httpContext == null) { return; } if (httpContext.Handler == null) { // чтобы не было исключения при старте приложения, // когда httpContext.Request пустой. return; } var request = httpContext.Request; eventObj.Properties.Set("IP", request.UserHostAddress); eventObj.Properties.Set("UserAgent", request.UserAgent); eventObj.Properties.Set("Url", request.Url.AbsoluteUri); eventObj.Properties.Set("UrlReferrer", request.UrlReferrer); eventObj.Properties.Set("Login", httpContext.User.Identity.Name); } }
EventPreparer подключается при старте приложения:
protected void Application_Start() { try { // подключим заполнение свойств всех событий-ошибок Client.Instance.EventPreparer = new EventPreparer(); AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); // сообщает, что запуск приложения успешно выполнен LogManager.GetCurrentClassLogger().Info("Приложение запущено"); } catch (Exception exception) { // отправим ошибку запуска приложения в мониторинг LogManager.GetCurrentClassLogger().Fatal(exception); throw; // приложение не должно работать, если есть ошибка инициализации } }
Проверим
Чтобы посмотреть, как работает обработка ошибок, напишем ErrorsController, который будет создавать исключения:
public class ErrorsController : ControllerBase { public ActionResult Sample1() { throw new Exception("Ой, случилась ошибка (вариант 1)"); } public ActionResult Sample2() { throw new Exception("Опять ошибка (вариант 2)"); } public ActionResult Sample3() { try { throw new Exception("Ошибка в try-catch (вариант 3)"); } catch (Exception exception) { LogManager.GetCurrentClassLogger().Error(exception); } return View(); } }
Запустите приложение и выполните /Errors/Sample1 или /Errors/Sample2. Приложение отобразит страницу ошибки:
Проверим, что в системе мониторинга компонент имеет статус Warning (жёлтый цвет).
В дереве компонентов под компонентом "WebSite" есть жёлтая ссылка "События", кликнем на неё, чтобы посмотреть события (ошибки).
В таблице событий можно кликнуть на сообщение события, на открывшейся странице увидеть подробности:
Все ошибки за нужный интервал времени можно увидеть на странице "Ошибки":
Здесь по ошибкам можно создать дефекты и далее работать с ними во встроенном баг-трекере.
Уровни лога Warning и Error создают ошибку жёлтого цвета. По умолчанию уведомления по email о жёлтом цвете не включены. Уровень лога Fatal создаёт ошибку красного цвета. О такой ошибке вы получите уведомление на email: