Журнал событий Windows и ETW: как вывести логи бизнес-приложения в стандартные механизмы ОС
· Обновлено: · Го Комура · CSharp, .NET, журнал событий, ETW, EventSource, проектирование логов, расследование сбоев, разработка под Windows
История изменений (1 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.21619988)
Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.
Го Комура (2026). Журнал событий Windows и ETW: как вывести логи бизнес-приложения в стандартные механизмы ОС. KomuraSoft LLC. https://comcomponent.com/ru/blog/windows-eventlog-etw-structured-logging/
- DOI (зарегистрированный архив)
- 10.5281/zenodo.21619988
- DOI (последняя зарегистрированная версия)
- 10.5281/zenodo.21619989
Когда на C# пишут бизнес-приложение или службу Windows, повседневные логи почти всегда уже идут в собственный файловый логгер или в библиотеку вроде Serilog и NLog. Значит ли это, что журнал событий Windows и ETW (Event Tracing for Windows) больше не нужны? Нет. Это не замена файловому логу, а отдельный слой записей, который видят специалисты по эксплуатации и стандартные средства ОС.
Ниже — как разделить роли файлового лога, журнала событий и ETW, как это реализовать в .NET, какой минимум ETW действительно нужен, как собирать и разбирать записи на практике и куда чаще всего наступают.
Целевые версии. Примеры в статье рассчитаны на .NET 8 (Windows). От .NET Framework 4.8 отличаются в основном два момента. Первый: System.Diagnostics.EventLog в .NET Framework входит в BCL, а в .NET (линейка Core) одноимённый пакет NuGet нужно подключать явно. Второй — куда доходят события EventSource. В .NET Framework это только ETW; в .NET (Core) они идут ещё и в EventPipe, встроенный механизм трассировки рантайма, поэтому инструменты вроде dotnet-trace могут собирать их без прав администратора.12
1. Сначала выводы
- Три механизма не заменяют друг друга, а делят роли. Файловый лог — запись, по которой разработчик потом разбирает детали. Журнал событий — запись, по которой специалисты по эксплуатации, Просмотр событий и средства мониторинга замечают сбой. ETW — высокочастотная трассировка, которую включают по необходимости, когда разбирают производительность или плохо воспроизводимую ошибку. Обычно используют все три и меняют объём информации в каждом.
- В журнал событий пишите только запуск, остановку и фатальные ошибки. Если туда же гнать все логи, аномалии, которые должна видеть эксплуатация, утонут. Файловый лог оставляйте подробным, журнал событий сужайте.
- Регистрация источника событий требует прав администратора. Начиная с Windows Vista
EventLog.CreateEventSourceбез прав администратора вызвать нельзя. Не регистрируйте источник при первом обращении во время работы: зарегистрируйте его заранее в установщике.3 - ETW — это не «постоянный сбор». Даже если события описаны через
EventSource, без подписчика (dotnet-trace, PerfView, инструмент на базе ETW) ничего не записывается. Базовый сценарий — указать нужный поставщик и собирать только при разборе производительности или конкретной ошибки.4 - Пишите сообщения шаблоном, а не конкатенацией или интерполяцией. Запись вида
logger.LogInformation("Order {OrderId} failed", orderId)позволяет искать в структурированном логе по имени заполнителя OrderId. Конкатенация и интерполяция эту структуру теряют.5 - Разрастание журнала часто начинается с малого значения по умолчанию. Максимальный размер журнала событий по умолчанию — всего 512 КБ. Если бизнес-приложение пользуется журналом событий регулярно, размер нужно явно увеличить и на этапе проектирования решить, что делать при заполнении: перезаписывать старое или отбрасывать новое.6
Таблица для выбора.
| Аспект | Файловый лог | Журнал событий | ETW |
|---|---|---|---|
| Основной читатель | Разработчики и сопровождение | Специалисты по эксплуатации, средства мониторинга, стандартный Просмотр событий | Те, кто разбирает производительность; инженеры поддержки |
| Ориентир по объёму | Подробный: Trace/Debug допустимы | Мало: только запуск, остановка и фатальные ошибки | Много только когда включили; по умолчанию ничего не пишется |
| Срок жизни / хранение | При настроенной ротации легко хранить долго | По достижении лимита старое перезаписывается или новое отбрасывается | В рамках сеанса; после сбора сохраняют как .etl/.nettrace |
| Связь со стандартными средствами ОС | Нет (нужен свой просмотрщик) | Смотрят в Просмотре событий, wevtutil, Get-WinEvent |
Смотрят через dotnet-trace, PerfView, WPR/WPA |
| Права | Достаточно права записи у пользователя, под которым работает приложение | Для регистрации источника нужны права администратора; писать в уже зарегистрированный источник может и обычный пользователь | Чтобы начать сбор, права администратора часто нужны |
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 18, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
2. Основы журнала событий Windows
В Windows по умолчанию три журнала: Application, System и Security. Security доступен только на чтение. Бизнес-приложения и службы пишут в Application или в собственный журнал (канал). Драйверы устройств по соглашению пишут в System.7
Единица записи — источник событий. Источник — имя, по которому опознают приложение; его заранее регистрируют через EventLog.CreateEventSource. Начиная с Windows Vista регистрация источника требует прав администратора. Чтобы проверить уникальность имени, нужно просмотреть все журналы, включая Security, а у обычного пользователя доступа к Security нет.3
using System.Diagnostics;
// Выполняйте из установщика или процедуры настройки, пока есть права администратора
const string SourceName = "KomuraSoft.OrderService";
const string LogName = "Application";
if (!EventLog.SourceExists(SourceName))
{
EventLog.CreateEventSource(SourceName, LogName);
}
Если создавать источник при первом обращении уже в работающем приложении, вызов сорвётся в любой среде, где программа идёт от обычного пользователя. Когда права администратора действительно нужны — в общем виде разобрано в статье «Когда Windows действительно требует прав администратора». Регистрация источника событий — как раз такой случай. Безопасная схема: зарегистрировать источник в установщике, а самому приложению только писать в уже зарегистрированный источник.
У события два идентификатора: уровень и идентификатор события. Уровень — это EventLogEntryType (Information, Warning, Error, SuccessAudit, FailureAudit); по нему Просмотр событий ставит значок и применяет стандартные фильтры. Идентификатор события — целое число, которое задаёт приложение; позже по нему ищут и считают однотипные случаи.
3. Как писать в журнал событий из .NET
Из .NET в журнал событий ведут два основных пути. Сначала — чем они отличаются по предпосылкам.
| Аспект | 3.1 Напрямую System.Diagnostics.EventLog |
3.2 ILogger + AddEventLog |
|---|---|---|
| Нужна ли регистрация источника | Да. Имя, которое передаёте в WriteEntry, установщик должен заранее зарегистрировать через CreateEventSource3 |
Да. Имя в SourceName так же регистрирует установщик. Если его не указать, получится общее имя .NET Runtime8 |
| Права на регистрацию | Права администратора (только на регистрацию; писать в уже зарегистрированный источник может обычный пользователь) | То же |
| Дополнительные ссылки | В .NET (линейка Core) — пакет System.Diagnostics.EventLog |
Пакет Microsoft.Extensions.Logging.EventLog (в Generic Host на Windows включён по умолчанию)9 |
| Что можно задать | Уровень и идентификатор события, плюс категория и вложение двоичных данных | Уровень, идентификатор события, шаблон сообщения. Категорию и двоичные данные задать нельзя |
| Как сузить объём | Решаете на каждом месте вызова | Через settings.Filter можно поднять уровень только для журнала событий |
| Где уместно | Небольшой сервис без своей инфраструктуры логов, перенос с Win32-подобного кода | Приложение, где структурированные логи уже идут через ILogger |
В обоих путях заранее зарегистрированное имя источника — обязательная предпосылка. Если писать под незарегистрированным именем, само событие попадёт в журнал Application, но без соответствующего ресурса сообщений Просмотр событий не сможет показать текст описания.10
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 провайдер EventLog на Windows включён по умолчанию.9
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".8 Общее имя .NET Runtime в Просмотре событий почти не отличить от логов других .NET-приложений, поэтому SourceName всегда задавайте явно под своё приложение.
Для пакетной обработки в службе Windows или в Планировщике заданий практичный баланс такой: в журнал событий — только «запуск», «остановка» и «фатальная ошибка», всё остальное — в файловый лог. Как делать службу и как её эксплуатировать — в «Как создавать службы Windows и как их эксплуатировать», сбои запуска через Планировщик заданий — в «Задача Планировщика заданий не запускается или завершается с 0x1». В обоих случаях журнал событий часто первое, куда смотрит эксплуатация; есть ли там суть, напрямую влияет на скорость первого разбора.
3.3 Как убедиться, что события записались
После того как код написан, хотя бы раз дойдите до того, что события видны в Просмотре событий. Порядок такой.
Win + R, выполнитеeventvwr.msc— откроется Просмотр событий.- В левой панели откройте Просмотр событий (локальный) > Журналы Windows > Application (в русской среде журнал может называться «Приложение»). Если сделали свой журнал, его имя будет в разделе Журналы приложений и служб.
- Если записей много и нужные тонут, справа откройте Фильтр текущего журнала и сузьте по «Источнику событий» (своё имя источника) и по нужному идентификатору в поле всех кодов событий.
Как значения из кода попадают в поля Просмотра событий — в таблице ниже. Если «писали, но не находим», по ней видно, куда смотреть.
| Что задано в коде | Где видно в Просмотре событий |
|---|---|
Первый аргумент WriteEntry / settings.SourceName |
Столбец «Источник» |
Аргумент eventID / EventId у ILogger |
Столбец «Код события» |
EventLogEntryType (Information, Warning, Error) / LogLevel |
Столбец «Уровень» (Сведения, Предупреждение, Ошибка) |
Строка, переданная в WriteEntry / сообщение после подстановки шаблона |
Текст описания внизу списка, на вкладке «Общие» |
LogName (по умолчанию Application) |
В каком журнале появится запись |
Из командной строки удобнее PowerShell и Get-WinEvent. В -FilterHashtable передайте имя журнала и имя поставщика (= имя источника) — вернутся только события этого источника.11
# Последние 10 событий своего источника: только время, идентификатор, уровень и текст
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'KomuraSoft.OrderService' } -MaxEvents 10 |
Select-Object TimeCreated, Id, LevelDisplayName, Message
Если не вернулось ни одной записи, причина почти всегда одна из трёх: опечатка в имени источника, источник не зарегистрирован, запись сорвалась, а исключение проглотили. Зарегистрирован ли источник, проверяют так: [System.Diagnostics.EventLog]::SourceExists('KomuraSoft.OrderService').
4. Что такое ETW — сквозная трассировка всей ОС
Event Tracing for Windows (ETW) — механизм трассировки, которым одинаково инструментируют и ядро, и приложения пользовательского режима. Главное свойство: события ядра (дисковый ввод-вывод, создание процесса и т. п.) и собственные события приложения можно писать и разбирать на одной шкале времени.12
В .NET собственный поставщик ETW описывают классом, который наследует System.Diagnostics.Tracing.EventSource.1 ETW работает по модели publish-subscribe: без подписчика события не записываются. То есть один только класс EventSource ничего не делает — запись начинается, только когда сбор явно включают инструментом.4
Разница из таблицы в главе 1 — «журнал событий = постоянно, для эксплуатации» против «ETW = по необходимости, для разбора» — как раз из-за наличия или отсутствия подписчика. На схеме это выглядит так.
flowchart LR
APP["Бизнес-приложение"]
APP -->|"только запуск, остановка, фатальные ошибки"| EL["Журнал событий<br/>пишется постоянно, ОС сохраняет"]
EL --> EV["Просмотр событий<br/>wevtutil / Get-WinEvent<br/>эксплуатация смотрит в любой момент"]
APP -->|"инструментирование через EventSource"| SRC["Поставщик<br/>описывает высокочастотные события"]
SRC -->|"нет подписчика"| NONE["Никуда не пишется<br/>стоимость почти нулевая"]
SRC -->|"когда сбор запущен"| SES["Сеанс<br/>включают только на время разбора"]
SES --> TOOL["Потребитель<br/>dotnet-trace / PerfView / WPA<br/>разбирают за ограниченный период"]
Рис. 1: Журнал событий пишется постоянно и сохраняется; ETW записывается, только когда есть поставщик, сеанс и потребитель.
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.13
dotnet-trace collect --providers KomuraSoft-OrderService -- OrderService.exe
Здесь важно вот что: dotnet-trace идёт не через сам ETW, а через другой путь — EventPipe. EventPipe встроен в рантайм .NET и, как и ETW, умеет собирать события EventSource. Различий три. EventPipe одинаково работает на всех платформах, которые поддерживает .NET, а ETW — только на Windows. Чтобы начать сбор ETW, часто нужны права администратора; EventPipe этого не требует, если запускать его от того же пользователя, что и целевое приложение. И EventPipe видит только управляемый код и рантайм: события ОС и ядра и нативный стек вызовов он не берёт.2 Именно из-за третьего ограничения, когда нужно дойти до ядра, берут ETW (PerfView, WPR/WPA).
Собранный файл .nettrace открывают в Visual Studio или PerfView. Для более серьёзного разбора производительности — например, вместе с событиями ядра — используют Windows Performance Recorder (WPR) из Windows Assessment and Deployment Kit (ADK) и смотрят результат в Windows Performance Analyzer (WPA).14 Но рамки этой статьи — «что умеет ETW и когда его применять»; в подробную процедуру разбора в PerfView и WPA мы не заходим. Для разработки и сопровождения бизнес-приложения достаточно уметь описать свой поставщик через EventSource и собрать его через dotnet-trace.
5. Практика структурированного логирования
Шаблон сообщения ILogger пишут как фиксированную строку с именованными заполнителями вида {PlaceHolder}. Это не вопрос внешнего вида: пока шаблон не меняется, а аргументы передаются отдельно, инфраструктура логов сохраняет соответствие имён заполнителей и значений как структурированные данные.15
// Рекомендуется: шаблон фиксированный, аргументы передаются отдельно
logger.LogWarning("Не удалось зарезервировать запас для заказа {OrderId}. Склад={WarehouseId}", orderId, warehouseId);
// Не рекомендуется: конкатенация и интерполяция ломают соответствие шаблона и значений
logger.LogWarning("Заказ " + orderId + ": не удалось зарезервировать запас. Склад=" + warehouseId);
logger.LogWarning($"Не удалось зарезервировать запас для заказа {orderId}. Склад={warehouseId}");
Правило анализатора CA2254 как раз ловит этот нежелательный вариант, когда шаблон меняется от вызова к вызову.5 На горячем пути с частыми вызовами можно пойти дальше и через LoggerMessageAttribute включить генерацию кода: тогда не платите за boxing и разбор шаблона.16
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. Сбор и разбор: сторона тех, кто читает логи
В Просмотре событий из списка журнала через «Фильтр» или «Настраиваемые представления» можно оставить только нужный источник, уровень или идентификатор события. Часто используемые условия сохраняйте как настраиваемое представление — следующий разбор пойдёт быстрее. Представление задаётся XPath-запросом; например, выбрать события конкретного источника можно так.17
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">
*[System[Provider[@Name='KomuraSoft.OrderService'] and (Level=2 or Level=3)]]
</Select>
</Query>
</QueryList>
Куда вставлять этот XML: справа в Просмотре событий «Создание настраиваемого представления» > вкладка «XML» > флажок «Изменить запрос вручную». После сохранения представление появится слева в «Настраиваемых представлениях»; дальше достаточно щелчка, и откроется список только ошибок (Level=2) и предупреждений (Level=3), которые написал KomuraSoft.OrderService. Level в схеме события — то же значение, что столбец «Уровень» в Просмотре событий; если нужны ещё сведения, добавьте в условие Level=4. Сужение, собранное в диалоге фильтра, на той же вкладке «XML» видно как XPath, поэтому короткий путь — сначала сузить в GUI, потом скопировать XML.
Из командной строки wevtutil экспортирует журналы и меняет настройки. Им же при разборе сбоя выгружают журнал вокруг момента инцидента и отправляют в поддержку.18
:: Экспортировать весь журнал 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
То, что передаётся в /q:, — содержимое элемента Select из XPath настраиваемого представления выше. /f: задаёт формат вывода: для чтения глазами берите text, для разбора скриптом — xml. В xml схема та же, что у XML настраиваемого представления, поэтому Provider и EventID можно брать по имени элемента. Экспортированный .evtx на другом компьютере открывают через «Открыть сохранённый журнал» в Просмотре событий.
При разборе сбоя базовый приём — сопоставить по времени три вещи: журнал событий, файловый лог и аварийный дамп. От метки времени «фатальной ошибки» в журнале событий смотрят подробности файлового лога в тот же момент и дамп, снятый WER/ProcDump. Как снимать дампы — в «Сбор аварийных дампов Windows: введение — WER, ProcDump, WinDbg», как разбирать уже снятый дамп — в «Как читать аварийный дамп в WinDbg + SOS».
7. Типичные ошибки
- Пишут, не зарегистрировав источник. Вызов, эквивалентный
RegisterEventSource, с незарегистрированным именем всё же кладёт событие в Application, но без DLL ресурса сообщений Просмотр событий не показывает описание и рисует ошибку.10 Если к тому же пытаться регистрировать источник черезCreateEventSourceпри запуске, процесс под обычным пользователем часто падает исключением уже при первом старте. Регистрацию источника всегда заканчивайте в установщике. - Путаница «можно писать под чужим именем источника». При наличии права записи журнал событий позволяет писать и под именем источника, которое приложение само не регистрировало. Имена источников должны быть уникальны на компьютере, но ОС это не обеспечивает — дисциплина целиком на стороне приложений.7 Слишком общее короткое имя без компании или продукта легко столкнётся с приложением другого поставщика или нечаянно займёт его источник.
- Разрастание журнала и политика хранения. Максимальный размер по умолчанию — всего 512 КБ.6 Если бизнес-приложение пользуется журналом событий регулярно, явно увеличьте размер через
wevtutil slили установщик и сознательно выберите: перезаписывать самое старое или отбрасывать новое. Учтите и то, чтоOverwriteOlderпомечен как устаревший: даже если его указать, фактически может получиться режим «не перезаписывать».19 - Отображение сообщений в многоязычной среде. Если настроены локализованные ресурсы сообщений (
MessageResourceFile), просмотр лога в среде без этой DLL или с другой версией даёт текст вроде «Не удается найти описание для события с кодом XX». Так бывает, когда перенаправленный журнал смотрят на другом сервере или когда приложение уже удалили, а события остались. Если писать строки напрямую черезWriteEntry(без файла ресурсов), эту проблему вы просто не заносите. - Случаи, когда писать нельзя из-за прав. Часто думают, что без администратора ничего не работает. На деле права администратора нужны для регистрации источника, а писать в уже зарегистрированный источник может обычный пользователь. Прежде чем поднимать всё приложение с правами администратора из-за «без администратора не работает», отделите операцию, которой эти права действительно нужны.
8. Кратко
Каркас статьи сводится к трём пунктам.
- Три механизма делят роли. Файловый лог — чтобы разработчик разбирал детали; журнал событий — чтобы эксплуатация и стандартные средства ОС замечали сбой; ETW/EventPipe — чтобы копать производительность и плохо воспроизводимые ошибки. В журнал событий пишите только запуск, остановку и фатальные ошибки, подробности оставляйте файловому логу (глава 1).
- Регистрация источника событий — работа установщика. На регистрацию нужны права администратора, писать в уже зарегистрированный источник может обычный пользователь. Попытка регистрировать источник через
CreateEventSourceво время работы как раз и даёт сбой при первом запуске под обычным пользователем (главы 2, 3 и 7). - ETW записывается, только когда есть подписчик. Один
EventSourceничего не делает. Обратная сторона: стоимость заранее заложенного инструментирования почти нулевая, и в нужный момент события можно снятьdotnet-trace(глава 4).
Если переходить к практике, разумный порядок такой.
- Просмотрите, что существующее приложение уже пишет в журнал событий, и убедитесь, что туда идут только запуск, остановка и фатальные ошибки. Если утекают и подробные логи, сузьте через
FilterуAddEventLog(глава 3). - Зафиксируйте правила нумерации идентификаторов (диапазоны по функциональным областям; выданный идентификатор не менять по смыслу) и оставьте это в документации (глава 2).
- Проверьте, есть ли в установщике регистрация источника событий и увеличен ли максимальный размер журнала относительно 512 КБ по умолчанию (глава 7).
- Если при каждом разборе сбоя вы сужаете журнал одинаково, сохраните этот XPath как настраиваемое представление (глава 6).
- В обработку, по которой могут спросить про производительность, заранее заложите через
EventSourceхотя бы события начала и конца. Пока сбор выключен, стоимость почти нулевая (глава 4).
Связанные статьи
- Когда Windows действительно требует прав администратора — UAC, защищённые области и как отличить это на этапе проектирования
- Как создавать службы Windows и как их эксплуатировать — от выбора между Планировщиком заданий до превращения BackgroundService в службу
- Задача Планировщика заданий не запускается или завершается с 0x1 — как локализовать причину и спроектировать безопасную эксплуатацию
- Минимальные требования к собственному логгеру и чек-лист интеграционных тестов
- Как проектировать Windows-приложение так, чтобы при сбое оставались логи и дамп
- Сбор аварийных дампов Windows: введение — WER, ProcDump, WinDbg
- Как читать аварийный дамп в WinDbg + SOS — практический разбор после сбора
Смежные темы консультаций
KomuraSoft LLC консультирует по проектированию логирования, журнала событий и ETW для Windows-бизнес-приложений, а также по точкам наблюдения, которые пригодятся при расследовании сбоев.
- Разработка Windows-приложений
- Расследование сбоев и анализ причин
- Техническая консультация и ревью проектирования
- Контакты
Справочные ссылки
-
Microsoft Learn, EventSource. О EventSource как встроенном в .NET механизме быстрого структурированного логирования с поддержкой ETW и EventPipe. ↩ ↩2
-
Microsoft Learn, EventPipe. О том, что EventPipe — встроенный в рантайм .NET механизм трассировки, похожий на ETW и perf_events; что он умеет собирать события EventSource; что ETW только для Windows и часто требует прав администратора, а EventPipe кроссплатформенный и без них; что область EventPipe ограничена управляемым кодом и рантаймом и не включает события ядра и нативный стек вызовов. ↩ ↩2
-
Microsoft Learn, EventLog.CreateEventSource Method. Почему начиная с Windows Vista создание источника событий требует прав администратора: нужен поиск по всем журналам, включая Security. ↩ ↩2 ↩3
-
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, EventLogSettings Class. О значениях по умолчанию AddEventLog:
LogName—"Application",SourceName—".NET Runtime". ↩ ↩2 -
Microsoft Learn, Logging providers in .NET. О том, что стандартные провайдеры логирования Generic Host включают Console, Debug, EventSource и EventLog (только Windows). ↩ ↩2
-
Microsoft Learn, Event Sources. О том, что запись под незарегистрированным именем попадает в Application, но без файла сообщений Просмотр событий не может показать описание и сообщает ошибку. ↩ ↩2
-
Microsoft Learn, Get-WinEvent. О том, что в
-FilterHashtableможно передать ключи вродеLogNameиProviderNameи сузить выборку, а у полученных событий есть свойстваTimeCreated,Id,LevelDisplayName,Message. ↩ -
Microsoft Learn, Event Tracing. О Event Tracing for Windows (ETW) как механизме запуска, остановки, инструментирования и потребления событий трассировки приложений. ↩
-
Microsoft Learn, dotnet-trace performance analysis utility. О dotnet-trace — кроссплатформенном инструменте сбора трасс на базе EventPipe — и команде collect. ↩
-
Microsoft Learn, Introduction to WPR. О Windows Performance Recorder (WPR) как инструменте, который расширяет ETW и даёт подробную запись системы и приложений. ↩
-
Microsoft Learn, Logging in C# and .NET. О синтаксисе
{PlaceHolder}в шаблонах сообщений и о том, что ключи шаблона становятся именами свойств лога. ↩ -
Microsoft Learn, High-performance logging in .NET. О том, что логирование с генерацией кода через LoggerMessageAttribute позволяет не платить за boxing и разбор шаблона. ↩
-
Microsoft Learn, Comparison of ETW and EventLog logger functionality. О создании настраиваемого представления в Просмотре событий XPath-запросом, в том числе по имени события. ↩
-
Microsoft Learn, wevtutil. О командной работе с журналом событий: экспорт (epl), запрос (qe), смена настроек (sl). ↩
-
Microsoft Learn, OverflowAction Enum. О поведении при достижении максимального размера журнала (перезапись или отбрасывание) и об устаревании OverwriteOlder. ↩
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Как найти причину «тормозов» с PerfView и dotnet-trace — практика анализа производительности .NET
Когда бизнес-приложение тормозит или ЦП постоянно на максимуме, какой инструмент взять и на что смотреть. Разделение ролей PerfView и dot...
Окончание драйверов принтера Windows ── как готовить печать форм и этикеток в бизнес-приложениях
Microsoft поэтапно прекращает сопровождение драйверов принтера v3/v4; с июля 2026 IPP class driver предпочтут. Что исчезает в Windows pro...
Локализация WinForms и WPF: resx, спутниковые сборки, переключение культуры
Разбираем локализацию десктопных приложений Windows: чем CurrentCulture отличается от CurrentUICulture, как устроены resx и спутниковые с...
CSV — не «просто текст». Практика CSV в бизнес-приложениях на C# (кодировки, совместимость с Excel, защита от инъекций)
Разбираем типичные сбои ввода-вывода CSV в бизнес-приложениях — самописный разбор через Split(','), искажение текста в Excel из-за UTF-8 ...
Не создавайте HttpClient в using на каждый запрос — создание, тайм-ауты и повторные попытки в C#
Если создавать HttpClient через using на каждый запрос, исчерпываются сокеты; static-экземпляр не отслеживает смену DNS. Разбираем схемы ...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Проектирование логирования, журнала событий и ETW входит в консультации по разработке Windows-приложений.
Расследование ошибок и причин
Проектирование логов с расчётом на расследование сбоев напрямую связано с консультациями по поиску ошибок и анализу причин.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Если уже есть файловые логи, журнал событий не нужен?
- Для подробного разбора часто хватает файлового лога. Но если специалисты по эксплуатации должны замечать сбой приложения в стандартных средствах ОС — Просмотре событий или системах мониторинга (уведомления о сбое в Планировщике заданий, SCOM и т. п.) — на практике в журнал событий выводят только ключевые точки. Это не замена одного другим: у записей разные читатели и разный слой.
- Как задать правила нумерации идентификаторов событий?
- Единственно верного ответа нет, но на практике меньше путаницы, если держать три правила: выделять диапазоны по функциональным областям (например, 1000-е — запуск, 2000-е — связь); выданный идентификатор не менять по смыслу и не использовать повторно; допускать пропуски в нумерации. EventId в ILogger ещё и ключ поиска в структурированных логах, поэтому схему, которую потом легко описать в документации, проще сопровождать.
- Я использую Serilog или NLog. Как это соотносится со статьёй?
- Serilog и NLog — одна из реализаций под ILogger: они направляют одни и те же записи в несколько sink — файл, журнал событий, ETW, внешний SaaS и т. д. Принципы статьи — в журнал событий только главное, шаблон сообщения не ломать — одинаково работают и при прямом ILogger, и через Serilog/NLog. Брать sink для журнала событий (например Serilog.Sinks.EventLog) или стандартный AddEventLog — уже выбор реализации.
- Насколько глубоко нужно изучать ETW?
- Если цель — разработка и сопровождение бизнес-приложения, достаточно уметь описать свой поставщик через EventSource и собирать его события через dotnet-trace. Разбор стека вызовов в PerfView и сбор событий ядра через WPR имеет смысл осваивать, когда производительность становится отдельной специальностью; в этой статье мы сознательно оставляем это за рамками.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.