Введение в журнал событий Windows и ETW — как использовать стандартные механизмы ОС для логов бизнес-приложения
· Го Комура · CSharp, .NET, Журнал событий, ETW, EventSource, Проектирование логов, Исследование ошибок, Разработка Windows
При разработке бизнес-приложений или служб Windows на C# обычно уже ведутся повседневные записи самописным файловым логгером или библиотекой Serilog/NLog. Значит ли это, что журнал событий Windows и ETW (Event Tracing for Windows) больше не нужны? Нет. Это не «замена файловому логу», а записи другого уровня, которые видны операционному персоналу и стандартным средствам ОС.
Ниже рассмотрим, когда использовать файловый лог, журнал событий и ETW, как реализовать каждый из них в .NET, необходимый минимум ETW, практику сбора и анализа и типичные полевые ловушки.
1. Вывод сначала
- Три механизма не заменяют друг друга, а разделяют работу. Файловый лог — запись для разработчиков, чтобы позже проследить детали; журнал событий — запись, по которой операционный персонал и стандартные средства ОС, такие как Event Viewer и мониторинг, замечают проблему; ETW — высокочастотная трасса, включаемая по требованию для исследования производительности или трудно воспроизводимых ошибок. Обычно применяйте все три, меняя объём информации в каждом.
- В журнал событий пишите только «старт, остановку и фатальные ошибки». Если направить туда каждую строку лога, аномалии, которые должны видеть операторы, утонут. Файловый лог оставляйте подробным, а журнал событий сужайте.
- Регистрация источника событий требует прав администратора. Начиная с Windows Vista
EventLog.CreateEventSourceнельзя вызвать без прав администратора. Не проектируйте регистрацию источника при первом доступе во время работы: зарегистрируйте его заранее в установщике. 1 - ETW не означает «постоянный сбор». Даже если события определены через
EventSource, ничего не записывается, пока их не слушает подписчик — dotnet-trace, PerfView или инструмент на основе ETW. Базовый сценарий — указать нужный provider и собирать только при анализе производительности или конкретной ошибки. 2 - Пишите сообщения шаблонами, а не конкатенацией или интерполяцией. Запись
logger.LogInformation("Заказ {OrderId} завершился ошибкой", orderId)позволяет искать в структурированном логе по имени заполнителя OrderId. Конкатенация и интерполяция теряют эту структурную информацию. 3 - Проблема разрастания журнала часто начинается с малого значения по умолчанию. Предельный размер журнала событий по умолчанию — всего 512 КБ. Если бизнес-приложение регулярно использует журнал событий, явно увеличьте размер и заранее решите, что делать при заполнении: перезаписывать старые записи или отбрасывать новые. 4
Ниже — таблица выбора.
| Аспект | Файловый лог | Журнал событий | ETW |
|---|---|---|---|
| Основной читатель | Разработчики и сопровождающие | Операционный персонал, средства мониторинга, стандартный Event Viewer | Исследователи производительности, инженеры поддержки |
| Примерный объём | Подробный: Trace/Debug допустимы | Небольшой: только старт, остановка и фатальные ошибки | Большой только при включении; по умолчанию ничего не записывается |
| Время жизни / хранение | Легко хранить долго в зависимости от ротации | Старые записи перезаписываются или новые отбрасываются после достижения лимита | Ограничено сессией; после сбора сохраняется как .etl/.nettrace |
| Интеграция со стандартными средствами ОС | Нет, нужен собственный просмотрщик | Просмотр в Event Viewer, wevtutil и Get-WinEvent |
Просмотр через dotnet-trace, PerfView, WPR/WPA |
| Права | Только право записи у пользователя, под которым работает приложение | Для регистрации источника нужны права администратора; после регистрации писать может стандартный пользователь | Для запуска сбора часто требуются права администратора |
2. Основы журнала событий Windows
В Windows по умолчанию есть три журнала: Application, System и Security; Security доступен только на чтение. Бизнес-приложения и службы пишут в Application или в собственный журнал (канал). По соглашению драйверы устройств пишут в System. 5
Единица записи — «источник событий». Источник — имя, идентифицирующее приложение; его заранее регистрируют через EventLog.CreateEventSource. Начиная с Windows Vista регистрация источника требует прав администратора. Проверка уникальности имени требует поиска по всем журналам, включая Security, к которому стандартный пользователь доступа не имеет. 1
using System.Diagnostics;
// Выполняйте это из установщика или процесса настройки, пока доступны права администратора
const string SourceName = "KomuraSoft.OrderService";
const string LogName = "Application";
if (!EventLog.SourceExists(SourceName))
{
EventLog.CreateEventSource(SourceName, LogName);
}
Если пытаться создавать источник при первом доступе уже в работающем приложении, этот вызов в production завершится неудачей всякий раз, когда программа работает от обычного пользователя. Общий вопрос, когда действительно нужны права администратора, разобран в статье «Когда Windows действительно требует прав администратора?»; регистрация источника — именно такой случай. Безопасная схема — зарегистрировать источник установщиком, а приложению оставить только запись в уже зарегистрированный источник.
У каждого события есть две идентифицирующие части: «уровень» и «идентификатор события». Уровень — это EventLogEntryType (Information, Warning, Error, SuccessAudit, FailureAudit); он определяет значок в Event Viewer и стандартные фильтры. Идентификатор события — целое число, определяемое приложением, которое позднее служит ключом поиска и агрегации однотипных случаев.
3. Запись в журнал событий из .NET
Из .NET есть два основных пути записи в журнал событий.
3.1 Прямое использование System.Diagnostics.EventLog
Это низкоуровневый API для случаев, где нужен детальный контроль: категории, присоединение двоичных данных и т. п. В .NET, в отличие от .NET Framework, нужно сослаться на NuGet-пакет System.Diagnostics.EventLog.
using System.Diagnostics;
public sealed class OrderServiceEventLog
{
private const string SourceName = "KomuraSoft.OrderService";
public void WriteServiceStarted()
{
EventLog.WriteEntry(SourceName, "OrderService запущен.",
EventLogEntryType.Information, eventID: 1000);
}
public void WriteFatalError(Exception ex)
{
EventLog.WriteEntry(SourceName,
$"Произошла фатальная ошибка, и обработка не может быть продолжена. Подробности смотрите в файловом логе. {ex.GetType().Name}: {ex.Message}",
EventLogEntryType.Error, eventID: 1999);
}
}
3.2 Использование ILogger + AddEventLog
Если структурированное логирование уже построено вокруг ILogger, AddEventLog из пакета Microsoft.Extensions.Logging.EventLog естественно встраивается в существующий конвейер. В ASP.NET Core и Worker Service на основе Generic Host provider EventLog включён по умолчанию при работе в Windows. 6
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddEventLog(settings =>
{
settings.SourceName = "KomuraSoft.OrderService";
settings.LogName = "Application";
// Помимо стандартных фильтров категорий направляйте в журнал событий
// только Warning и более высокие уровни (подробности оставляйте в файловом логе)
settings.Filter = (_, level) => level >= LogLevel.Warning;
});
using IHost host = builder.Build();
await host.RunAsync();
Если не указывать EventLogSettings, LogName по умолчанию равен "Application", а SourceName — ".NET Runtime". 7 Оставляя общее имя .NET Runtime, в Event Viewer трудно понять, относится ли запись к вашему или к чужому .NET-приложению, поэтому для собственной программы всегда задавайте SourceName явно.
Для пакетной обработки в службе Windows или Task Scheduler практичный баланс — писать в журнал событий только «старт», «остановку» и «фатальную ошибку», оставляя всё остальное файловому логу. Построение и эксплуатация службы разобраны в «Создание и эксплуатация служб Windows», а исследование Task Scheduler — в «Когда задача планировщика не запускается или завершается с 0x1». В обоих случаях журнал событий часто становится первой точкой проверки операционного персонала, а наличие там ключевых фактов определяет скорость первичного исследования.
4. Что такое ETW: основа трассировки во всей ОС
Event Tracing for Windows (ETW) — механизм трассировки, на общей основе охватывающий всё от ядра до приложений пользовательского режима. Его главное достоинство — возможность записывать и анализировать события ядра, например дисковый I/O и создание процессов, вместе с собственными событиями приложения на одной временной шкале. 8
В .NET собственный ETW-provider определяется классом, наследующим System.Diagnostics.Tracing.EventSource. ETW использует модель publisher/subscriber: без подписчика события не пишутся. То есть одно создание EventSource ничего не делает — записи появятся только при явном включении сбором. 2
using System.Diagnostics.Tracing;
[EventSource(Name = "KomuraSoft-OrderService")]
internal sealed class OrderServiceEventSource : EventSource
{
public static readonly OrderServiceEventSource Log = new();
[Event(1, Level = EventLevel.Informational, Message = "Начата обработка заказа. OrderId={0}")]
public void OrderProcessingStart(string orderId)
{
if (IsEnabled())
{
WriteEvent(1, orderId);
}
}
[Event(2, Level = EventLevel.Informational, Message = "Обработка заказа завершена. OrderId={0}")]
public void OrderProcessingStop(string orderId)
{
if (IsEnabled())
{
WriteEvent(2, orderId);
}
}
}
Вызывающая сторона использует его так. Проверка IsEnabled() до записи события позволяет избежать ненужных накладных расходов, когда никто не собирает данные.
OrderServiceEventSource.Log.OrderProcessingStart(order.Id);
try
{
ProcessOrder(order);
}
finally
{
OrderServiceEventSource.Log.OrderProcessingStop(order.Id);
}
Самый простой способ собрать эти события — кроссплатформенный инструмент dotnet-trace. 9
dotnet-trace collect --providers KomuraSoft-OrderService -- OrderService.exe
Собранный файл .nettrace открывают в Visual Studio или PerfView. Для более серьёзного расследования производительности, например с событиями ядра, используется Windows Performance Recorder (WPR) из Windows Assessment and Deployment Kit (ADK), а результат просматривается в Windows Performance Analyzer (WPA). 10 Но рамки этой статьи заканчиваются на вопросах «что умеет ETW и когда его применять», без детальной процедуры анализа в PerfView/WPA. Для разработки и сопровождения бизнес-приложения достаточно уметь определить provider через EventSource и собирать его dotnet-trace.
5. Практика структурированного логирования
Шаблон сообщения ILogger — фиксированная строка с именованными заполнителями вроде {PlaceHolder}. Это не только стиль: передача аргументов при неизменном шаблоне позволяет фреймворку сохранить связь между именами заполнителей и значениями как структурированные данные. 11
// Рекомендуется: сохраняйте шаблон неизменным и передавайте аргументы отдельно
logger.LogWarning("Не удалось зарезервировать товарный запас для заказа {OrderId}. Склад={WarehouseId}", orderId, warehouseId);
// Не рекомендуется: конкатенация или интерполяция строк нарушает соответствие между шаблоном и его значениями
logger.LogWarning("Заказ " + orderId + ": не удалось зарезервировать товарный запас. Склад=" + warehouseId);
logger.LogWarning($"Не удалось зарезервировать товарный запас для заказа {orderId}. Склад={warehouseId}");
Правило анализатора CA2254 обнаруживает такой нежелательный вариант, где шаблон меняется при каждом вызове. 3 На горячих путях с частыми вызовами можно пойти дальше и использовать source generation через LoggerMessageAttribute, чтобы избежать стоимости boxing и разбора шаблона. 12
using Microsoft.Extensions.Logging;
internal static partial class Log
{
[LoggerMessage(
EventId = 2001,
Level = LogLevel.Warning,
Message = "Не удалось зарезервировать товарный запас для заказа {OrderId}. Склад={WarehouseId}")]
public static partial void StockReservationFailed(
this ILogger logger, string orderId, string warehouseId);
}
// Вызывающий код
logger.StockReservationFailed(orderId, warehouseId);
При такой схеме один вызов лога можно направлять в разные места: детали — в файловый лог через sink Serilog/NLog, сокращённую версию — в журнал событий, дешёвую трассу — в ETW через EventSourceLoggerProvider. Базовый паттерн — зарегистрировать несколько ILoggerProvider и с помощью Filter назначить разный уровень для каждого. Минимальные требования к самописному логгеру собраны в статье о минимальных требованиях и интеграционном чек-листе; связывание логов и дампов при падении — в статье о сохранении логов и дампов при падении Windows-приложения.
6. Сбор и исследование: как читать написанные логи
В Event Viewer через «Filter» или «Custom Views» из списка журнала можно выделить только определённый источник, уровень или идентификатор события. Сохранение часто используемых условий как custom view ускоряет последующие расследования. Custom view определяется XPath-запросом; например, запрос, выбирающий события конкретного источника, выглядит так. 13
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">
*[System[Provider[@Name='KomuraSoft.OrderService'] and (Level=2 or Level=3)]]
</Select>
</Query>
</QueryList>
В командной строке wevtutil экспортирует журналы и меняет настройки. Во время расследования сбоя им можно экспортировать журнал событий вокруг момента инцидента и передать его в поддержку. 14
:: Экспортировать весь журнал Application в файл evtx
wevtutil epl Application C:\logs\application_20260707.evtx
:: Показать только события указанного источника (10 самых последних)
wevtutil qe Application /q:"*[System[Provider[@Name='KomuraSoft.OrderService']]]" /c:10 /rd:true /f:text
При исследовании падения основная практика — сопоставить по времени три вещи: журнал событий, файловый лог и crash dump. Начиная с времени «фатальной ошибки» из журнала событий, сравнивают детали файлового лога того же момента и дамп, снятый WER/ProcDump. Как собирать дампы, разобрано в «Введение в сбор Windows crash dump: WER/ProcDump/WinDbg», а процедура анализа снятого дампа — в «Чтение crash dump с WinDbg + SOS».
7. Ловушки
- Попытка записи с незарегистрированным источником. Если выполнить аналог
RegisterEventSourceс незарегистрированным именем источника, само событие попадает в Application, но из-за отсутствия DLL ресурсов сообщений Event Viewer не может показать описание и показывает ошибку. 15 А схема с автоматической регистрацией черезCreateEventSourceпри запуске процесса обычным пользователем обычно падает исключением уже при первом старте. Всегда завершайте регистрацию источника в установщике. - Заблуждение «можно писать под чужим именем источника». При наличии права записи журнал событий позволяет писать под именем источника, которого ваше приложение не регистрировало. Имена источников должны быть уникальны на компьютере, но ОС не применяет это правило — дисциплина полностью остаётся на стороне приложений. 5 Слишком общее короткое имя без компании или продукта часто приводит к столкновению с приложением другого поставщика или непреднамеренному использованию его источника.
- Разрастание журнала и политика хранения. Предельный размер журнала по умолчанию — всего 512 КБ. 4 При регулярном использовании бизнес-приложением явно увеличьте размер через
wevtutil slили установщик и осознанно выберите: «перезаписывать самые старые записи» или «отбрасывать новые». Помните также, чтоOverwriteOlderпомечен устаревшим: его указание может фактически дать режим «не перезаписывать». 16 - Отображение сообщений в многоязычной среде. Если настроены локализованные ресурсы сообщений (
MessageResourceFile), просмотр лога в среде без нужной DLL или с другой версией показывает «The description for Event ID XX cannot be found». Это возникает, например, при просмотре перенаправленного журнала на другом сервере или когда после удаления приложения остаются только события. Запись строк напрямую черезWriteEntry, без файла ресурсов, не вводит эту проблему изначально. - Случаи, когда запись невозможна из-за прав. Часто неверно считают, что администратор нужен всегда. На деле права администратора требуются для регистрации источника, но запись в уже зарегистрированный источник обычному пользователю доступна. Не запускайте всё приложение elevated, увидев «без администратора не работает»: сначала отделите операцию, которой эти права действительно нужны.
Связанные статьи
- Когда Windows действительно требует прав администратора? — UAC, защищённые области и проектное определение
- Создание и эксплуатация служб Windows — от выбора между Task Scheduler до превращения BackgroundService в службу
- Когда задача планировщика не запускается или завершается с 0x1: изоляция причины и надёжная эксплуатация
- Минимальные требования к самописному логгеру и чек-лист интеграционного теста
- Как проектировать Windows-приложения, сохраняющие логи и дампы при падении
- Введение в сбор Windows crash dump: WER/ProcDump/WinDbg
- Чтение crash dump с WinDbg + SOS: практическое введение в анализ после сбора
Связанные области консультаций
KomuraSoft LLC проводит технические консультации по проектированию основы логирования, журнала событий и ETW для Windows-бизнес-приложений, а также по определению точек наблюдаемости с учётом исследования сбоев.
- Разработка Windows-приложений
- Исследование ошибок и анализ первопричин
- Техническая консультация и ревью дизайна
- Контакты
Справочные ссылки
-
Microsoft Learn, EventLog.CreateEventSource Method. О том, почему с Windows Vista создание источника событий требует прав администратора: нужен поиск по всем журналам, включая Security. ↩ ↩2
-
Microsoft Learn, Getting Started with EventSource. О модели publish-subscribe EventSource, при которой без ETW/EventPipe-подписчика события не записываются, и о сборе через dotnet-trace collect. ↩ ↩2
-
Microsoft Learn, CA2254: Template should be a static expression. О том, что конкатенация и интерполяция в шаблоне лог-сообщения теряют связь между именами заполнителей и значениями. ↩ ↩2
-
Microsoft Learn, EventLog.MaximumKilobytes Property. О предельном размере журнала событий по умолчанию 512 КБ. ↩ ↩2
-
Microsoft Learn, EventLog Class. О трёх журналах по умолчанию (Application/System/Security), требовании уникальности источника на компьютере и механизме, по которому писать под любым зарегистрированным именем источника можно при наличии права записи. ↩ ↩2
-
Microsoft Learn, Logging providers in .NET. О том, что стандартные providers Generic Host включают Console, Debug, EventSource и EventLog (только Windows). ↩
-
Microsoft Learn, EventLogSettings Class. О значениях по умолчанию AddEventLog:
LogName—"Application",SourceName—".NET Runtime". ↩ -
Microsoft Learn, Event Tracing. О ETW как механизме запуска, остановки, инструментирования и потребления событий трассировки приложений. ↩
-
Microsoft Learn, dotnet-trace performance analysis utility. О dotnet-trace — кроссплатформенном инструменте сбора трасс на базе EventPipe — и его команде collect. ↩
-
Microsoft Learn, Introduction to WPR. О Windows Performance Recorder как инструменте, расширяющем ETW для подробной записи системы и приложений. ↩
-
Microsoft Learn, Logging in C# and .NET. О синтаксисе
{PlaceHolder}в шаблонах и механизме, в котором ключи шаблона становятся именами свойств лога. ↩ -
Microsoft Learn, High-performance logging in .NET. О том, что source-generated logging через LoggerMessageAttribute устраняет стоимость boxing и разбора шаблона. ↩
-
Microsoft Learn, Comparison of ETW and EventLog logger functionality. О создании custom view Event Viewer XPath-запросом с фильтрацией по имени события и другим критериям. ↩
-
Microsoft Learn, wevtutil. О командах работы с журналом событий, включая экспорт (epl), запрос (qe) и смену настройки (sl). ↩
-
Microsoft Learn, Event Sources. О том, что запись под незарегистрированным именем попадает в Application, но без файла сообщений Event Viewer не может показать описание и сообщает ошибку. ↩
-
Microsoft Learn, OverflowAction Enum. О поведении при достижении размера журнала — перезаписи или отбрасывании — и об устаревании OverwriteOlder. ↩
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Как точно найти причину «медленной работы» с PerfView и dotnet-trace — практическое введение в анализ производительности .NET
Когда бизнес-приложение «работает медленно», «загружает ЦП под завязку» или «иногда зависает», какой инструмент выбрать и на что смотреть...
Не только appsettings.json — практическое руководство по управлению конфигурацией Windows-бизнес-приложений (настройки окружений, секреты и место записи)
Практический разбор управления конфигурацией Windows-бизнес-приложений: слои appsettings.json, выбор между IConfiguration и семейством IO...
Печать и вывод PDF в Windows-бизнес-приложениях — выбор между System.Drawing.Printing, WPF и библиотеками отчётов
В виде таблицы выбора по требованиям собраны печать WinForms через PrintDocument, печать WPF через FlowDocument/FixedDocument и варианты ...
Google Ads для BtoB с небольшим бюджетом — как получить результат при нескольких десятках тысяч иен в месяц
Практическое руководство для BtoB-компаний, которые начинают Google Ads с бюджетом в несколько десятков тысяч иен в месяц. Разбираются ср...
Как сделать сайт заметным по запросам с названием района — практическое руководство по локальному SEO для малого и среднего бизнеса
Для компаний, которые не появляются по запросу «название района + отрасль», собран порядок исправлений для локального SEO: подготовка Goo...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Расследование ошибок и долгие сбои
Периодические сбои, диагностика связи, сбои после длительной работы и проверка путей отказа.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Проектирование основы логирования, журнала событий и ETW входит в область консультаций по разработке Windows-приложений.
Расследование ошибок и причин
Проектирование логов с учётом расследования сбоев напрямую связано с консультациями по поиску ошибок и анализу первопричин.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Если уже есть файловые логи, нужен ли журнал событий?
- Для подробного исследования одних файловых логов часто достаточно. Но если операционный персонал должен замечать проблемы приложения через стандартные средства ОС — Event Viewer или системы мониторинга, уведомления о сбоях Task Scheduler, SCOM и т. п. — практично выводить в журнал событий хотя бы ключевые точки. Это не замена одного другим, а записи на разных уровнях для разных читателей.
- Как выбрать правила нумерации идентификаторов событий?
- Единственно правильного ответа нет, но три практических правила уменьшают ошибки: делите диапазоны по функциональным областям — например, 1000-е для запуска, 2000-е для связи; после выпуска не меняйте значение идентификатора и не используйте его повторно; оставляйте пробелы в нумерации. EventId в ILogger также служит ключом поиска структурированных логов, поэтому удобная для документации схема окупается при сопровождении.
- Я использую Serilog или NLog — как это соотносится со статьёй?
- Serilog и NLog — реализации под ILogger, которые направляют те же записи в несколько приёмников: файлы, журнал событий, ETW, внешние SaaS и т. д. Принципы статьи — оставлять в журнале событий только главное и не разрушать шаблон сообщения — одинаково применимы при прямом ILogger и при Serilog/NLog. Выбор между EventLog sink, например Serilog.Sinks.EventLog, и стандартным AddEventLog — вопрос реализации.
- Насколько глубоко нужно изучать ETW?
- Если главная цель — разработка и сопровождение бизнес-приложения, достаточно уметь определить собственный provider через EventSource и собирать его события с dotnet-trace. Анализ стека вызовов в PerfView и сбор событий ядра через WPR можно осваивать при специализации на расследовании производительности; эта статья намеренно оставляет их за рамками.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.
Публичные ссылки