Описание проблемы
Представим, что из-за какой-то проблемы в приложении случилось необработанное исключение.
Что делать?
Большинство приложений делают следующее:
Почему это плохо:
Решение
Необходимо научить приложение передавать сведения о необработанных исключениях в систему мониторинга. Система мониторинга автоматически переведёт соответствующий компонент приложения в статус 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: