Zidium

Исходные коды примеров находятся в архиве API

Описание проблемы

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

Что делать?

Большинство приложений делают следующее:

  • ошибка записывется в лог;
  • пользователю показывается страница ошибки "Упс, произошла ошибка... Приносим извинения".

Почему это плохо:

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

Решение

Необходимо научить приложение передавать сведения о необработанных исключениях в систему мониторинга. Система мониторинга автоматически переведёт соответствующий компонент приложения в статус Alarm. Техническая поддержка получит уведомление и быстро исправит ошибку, так как в системе мониторинга будут подробные сведения об проблеме (стек, url запроса, логин пользователя и т.д.).

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

Реализация

Для обработки исключений в приложениях ASP.NET MVC используется метод Application_Error в Global.asax. Реализуем отправку ошибок в систему мониторинга в этом методе:

protected void Application_Error(object sender, EventArgs e)
{
	var exception = Server.GetLastError();
	// обработка ошибки 404 - страница не найдена
	// источником ошибок 404 часто бывают боты, которые что-то ищут на сайте
	// такие ошибки не будем считать ошибками нашего приложения
	var httpException = exception as HttpException;
	if (httpException != null)
	{
		if (httpException.GetHttpCode() == 404)
		{
			ShowErrorPage("Error404.cshtml", exception);
			return;                    
		}
	}
	// остальные ошибки отправим в систему мониторинга Zidium
	var component = ZidiumHelper.GetWebSiteComponent();
	var error = component.CreateApplicationError(exception);
	error.Add(); // асинхронная отправка          
	exception.HelpLink = error.TypeCode; // запомним код ошибки
	ShowErrorPage("Error.cshtml", exception, error.TypeCode);
} 

Если Ваше приложение доступно в сети Интернет, то разные боты будут переодически создавать ошибки 404. Чтобы ошибки 404 не отвлекали, код выше их игнорирует (не отправляет в систему мониторинга).

Чтобы было проще разобраться в причине ошибки, будем передавать параметры HTTP-запроса:

  • Login
  • IP
  • Url
  • UserAgent
  • UrlReferrer

Заполнением свойств всех событий будет заниматься класс 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 использует ZidiumHelper, когда создается Client:

public static IClient GetClient()
{
	if (_client == null)
	{
		_client = new Client();
		// EventPreparer заполняет свойства всех событий
		_client.EventPreparer = new EventPreparer(); 
	}
	return _client;
}

Проверим

Чтобы посмотреть, как работает обработка ошибок, напишем 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)
		{
			var component = ZidiumHelper.GetWebSiteComponent();
			component.AddApplicationError(exception);
		}
		return View();         
	}
}

Запустите приложение и выполните /Errors/Sample1 или /Errors/Sample2. Приложение отобразит страницу ошибки:

Проверим, что в системе мониторинга компонент имеет статус Alarm (красный цвет).

В дереве компонентов под компонентом "WebSite" есть красная ссылка "События", кликнем на неё, чтобы посмотреть события (ошибки).

В таблице событий можно кликнуть на сообщение события и увидеть дополнительные свойства:

Событие ошибки перевело компонент "WebSite" в статус Alarm, а Zidium автоматически отправил Вам email-уведомление. Проверьте, что Вы получили email-уведомление: