ASP.NET: пишем в Event log от имени .NET Runtime

26 сентября 2010 г.

Хочу поделиться с вами своим опытом работы с журналом Windows и рассказать о том, как осуществлять туда запись из приложения ASP.NET, без осуществления каких-либо предварительных настроек.

Суть проблемы

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

Было принято решение воспользоваться стандартными средствами ведения журнала Windows (Windows event logs). Подробнее можно прочитать здесь: http://imar.spaanjaars.com/275/logging-errors-to-the-event-log-in-aspnet-applications.

Проблема при использовании данного метода заключается в том, что журнал перед использованием нужно надлежащим образом настроить, а именно создать пользовательский источник событий. Имя источника может быть совершенно произвольным и зачастую соответствует имени приложения, производящего запись в журнал (стоит, однако, отметить, что имя должно быть уникально в пределах Windows-машины).

Суть проблемы в том, что учетная запись, от имени которой запущен процесс IIS (обслуживающий приложения ASP.NET), не имеет права создавать пользовательские источники (читай, не имеет права писать в реестр). В связи с этим в голову сразу приходят следующие возможные решения проблемы:

  • Предварительно создать пользовательский источник, используя привелигированную учетную запись (будь то ручная настройка, либо запуск программы-конфигуратора).
  • Отредактировать существующую учетную запись, добавив недостающих прав.
  • Использовать другие методы ведения журнала.

Первый и второй варианты сразу не подходят, так как экземпляры работающего приложения установлены на множестве серверов. Таким образом, накладные расходы на ручную/автоматическую конфигурацию будут достаточно высоки (стоит так же отметить, что политика компании в отношении подобного рода манипуляций на хостинговой площадке достаточно однозначна – все должно работать «из коробки» без дополнительных настроек со стороны системного администратора). Второй вариант плох еще и потому, что открывает потенциальную дыру в безопасности. Третий вариант так же был расценен как неприемлемый, ввиду того, что требует дополнительных знаний от человека, использующего журнал («где хранится журнал, в каком виде, как в нем представлена информация…» и т.д.).

Использование стандартного источника

Так как ни один из изложенных выше вариантов решения проблемы не удовлетворял требованиям, было принято решение использовать стандартный источник среды выполнения ASP.NET. О том, как это было достигнуто, речь пойдет ниже.

Первое, что требовалось выяснить – имя источника. На снимке выше можно заметить, что в списке присутствует предупреждение (о не перехваченном исключении), источником которого в данном случае является «ASP.NET 2.0.50727.0». Этим источником мы и воспользуемся. Однако просто подставить данное значение в код мы не можем (версия, как major, так и minor может отличаться), следовательно, ее нужно как-то сформировать. Формируется она следующим образом (обратите внимание на имя журнала, в который впоследствии будет производиться запись — “Application”):

public System.Diagnostics.EventLog GetEventLog()
{
string logName = "Application";
string runtimeVersion = string.Empty;
System.Diagnostics.EventLog ret = null;
System.Diagnostics.EventLog[] logs = null;

logs = System.Diagnostics.EventLog.GetEventLogs();
if (logs != null && logs.Any())
{
foreach (System.Diagnostics.EventLog l in logs)
{
if (string.Compare(l.Log, logName, StringComparison.InvariantCultureIgnoreCase) == 0)
{
if (string.IsNullOrEmpty(l.Source))
{
runtimeVersion = System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion();
if (string.IsNullOrEmpty(runtimeVersion))
{
ret = l;

runtimeVersion = runtimeVersion.Replace("v", string.Empty);
ret.Source = string.Format("ASP.NET {0}.0", runtimeVersion);
}
}

if(ret != null)
break;
}
}
}

return ret;
}
Сообщения и ресурсы

Итак, имя источника получено, можно начинать писать в журнал:

System.Diagnostics.EventLog log = GetLog();

log.WriteEntry("Index initialization has been completed.",
System.Diagnostics.EventLogEntryType.Information, 0);

Однако если мы посмотрим на нашу «всежеиспеченную» запись, мы увидим следующее:

Как видно, система поставила в текст сообщения уведомление о не найденном строковом ресурсе. Суть в том, что выбранный нами источник связан с файлом ресурсов, в котором хранятся все текстовые сообщения, а строка, которую мы передаем – лишь аргумент для соответствующего строкового ресурса. Ответ очевиден: нужно передать такое значение «eventId» (который в выше приведенном примере имеет значение «0»), которое соответствует ресурсу вида «{0}», то есть ничего, кроме служебной информации не будет присутствовать в тексте сообщения.

Первое, что стоит сделать — это выяснить, где хранятся ресурсы. Открываем реестр и переходим в раздел «HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog\[имя журнала]\[имя источника]». В нашем случае имя журнала – «Application», а имя источника – «ASP.NET 2.0.50727.0». Список ключей раздела имеет примерно следующий вид:

Следующим шагом скачиваем программу под названием «PE Explorer» и открываем в ней данный файл. Далее открываем обозреватель ресурсов (View -> Resources (CTRL + R)). В левой панели раскрываем узел «Message table» и по очереди изучаем каждый блок ресурсов. Скажу сразу, нас интересует ресурс со значением «%1»:

Переводим шестнадцатеричный идентификатор ресурса «0x4000046C» в десятичное число и в результате имеем следующий блок кода (обратите внимание, что мы теперь используем класс «EventInstance» для указания ресурса):

int categoryID = 3;
long instanceID = 1073742956;
System.Diagnostics.EventLog log = GetLog();
System.Diagnostics.EventInstance instance = new System.Diagnostics.EventInstance(instanceID, categoryID);

instance.EntryType = System.Diagnostics.EventLogEntryType.Information;

log.WriteEvent(instance, new object[] { "Index initialization has been completed." });

Кстати, идентификатор категории – 3 («Web event») так же был подсмотрен в файле ресурсов.

В результате получаем следующее:

Заключение

Данное решение не претендует на универсальность, и я не берусь утверждать, что оно будет работать во всех случаях. Буду рад, если кому-то оно так же может быть полезным, как и мне.

Теги:
рубрика Windows, Программирование
  • Похожие статьи
  • Предыдущие из рубрики