更新履歴(4件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- 書けたことを確認する節とまとめを新設し、イベントビューアーでの確認手順とコード上の指定との対応表を追加しました。あわせてイベントログとETWの違いを図にし、対象バージョンと、EventPipe経由である点の説明を加えています。
- 出典が本文のどこからも参照されておらず、「参考資料」に表示されていなかった問題を修正しました。本文の内容は変えていません。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.21589963)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「Windowsイベントログ・ETW入門 ── 業務アプリのログをOS標準の仕組みに乗せる」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21589963 https://staging.comcomponent.com/blog/windows-eventlog-etw-structured-logging/
- DOI(最新版)
- 10.5281/zenodo.21589963
- DOI(この版)
- 10.5281/zenodo.21732947
C#で業務アプリやWindowsサービスを開発していると、たいてい自作のファイルロガーか、Serilog・NLogのようなライブラリで日々のログを出力しています。ではWindowsイベントログやETW(Event Tracing for Windows)はもう不要かというと、そうではありません。この2つは「ファイルログの代わり」ではなく、運用担当者やOS標準ツールから見える別レイヤーの記録です。
この記事では、ファイルログ・イベントログ・ETWの使い分けから、.NETでの実装方法、ETWの最小限の勘所、収集・調査の実務、そして現場で踏みがちな落とし穴までを整理します。
対象バージョン: この記事のサンプルコードは .NET 8(Windows)を前提に書いています。.NET Framework 4.8を使っている場合との主な違いは次の2点です。1つ目は、System.Diagnostics.EventLog が.NET Framework ではBCLに含まれるのに対し、.NET(Core系)では同名のNuGetパッケージを明示的に参照する必要があること。2つ目は、EventSource の届け先です。.NET Frameworkでは行き先がETWだけですが、.NET(Core系)ではETWに加えてEventPipe(ランタイム内蔵のトレース機構)にも流れるため、dotnet-trace のようなツールで管理者権限なしに収集できます。12
1. まず結論
- 3つの手段は代替関係ではなく、役割分担です。 ファイルログは開発者が後から詳細を追うための記録、イベントログは運用担当者やOS標準のイベントビューアー・監視ツールが異常に気づくための記録、ETWは性能調査や再現困難な不具合の解析のためにオンデマンドで有効化する高頻度トレースです。基本は併用し、それぞれに書く情報量を変えます。
- イベントログへは「起動・停止・致命的エラー」だけを書きます。 すべてのログをイベントログにも流すと、運用担当者が本当に見るべき異常が埋もれます。ファイルログの方は今まで通り詳細に出しつつ、イベントログは絞り込みます。
- イベントソースの登録には管理者権限が必要です。
EventLog.CreateEventSourceはWindows Vista以降、管理者権限がないと呼び出せません。実行時の初回アクセスで登録しようとする設計は避け、インストーラーで事前に登録しておきます。3 - ETWは「常時収集」ではありません。
EventSourceでイベントを定義しておいても、購読者(dotnet-trace、PerfView、ETWベースのツール)がいなければ何も記録されません。パフォーマンス分析や特定の不具合調査のときだけ、対象のプロバイダーを指定して収集するのが基本的な使い方です。4 - メッセージは文字列連結や文字列補間ではなく、テンプレート形式で書きます。
logger.LogInformation("Order {OrderId} failed", orderId)のように書けば、構造化ログとしてOrderIdをプレースホルダー名付きで検索できます。文字列連結・補間で組み立てると、この構造情報が失われます。5 - ログの肥大化は既定値の小ささが原因になりがちです。 イベントログの既定の最大サイズは512KBしかなく、業務アプリがイベントログを日常的に使うなら明示的に拡張し、上限に達したときの挙動(上書きするか、破棄するか)も設計時に決めます。6
以下は判断表です。
| 観点 | ファイルログ | イベントログ | ETW |
|---|---|---|---|
| 主な読者 | 開発者・保守担当 | 運用担当者、監視ツール、OS標準のイベントビューアー | パフォーマンス調査担当、サポートエンジニア |
| 出力量の目安 | 詳細(Trace/Debugも含めてよい) | 少(起動・停止・致命的エラーのみ) | 有効化時のみ大量。既定では何も記録されない |
| 寿命・保持 | ローテーション設定次第で長期保持しやすい | ログサイズ上限に達すると古いものから上書き・破棄される | セッション単位。収集し終えたら.etl/.nettraceファイルとして保存 |
| OS標準ツールとの統合 | なし(自前のビューアーが必要) | イベントビューアー、wevtutil、Get-WinEventで標準的に見られる |
dotnet-trace、PerfView、WPR/WPAで見る |
| 権限 | アプリの実行ユーザーの書き込み権限のみ | ソース登録に管理者権限が必要(書き込み自体は登録済みソースなら標準ユーザーでも可) | 収集開始に管理者権限が必要な場合が多い |
この記事の知識マップ
業務アプリのログは、開発者が詳細を追うファイルログ、運用担当者やOS標準ツールが異常に気づくためのWindowsイベントログ、性能調査や不具合解析のために購読者がいるときだけ記録されるETWという役割分担で使い分ける。Windowsイベントログへの書き込みは事前に登録されたイベントソースを前提とし、その登録には管理者権限が要る一方、登録済みソースへの書き込みは標準ユーザーでも行える。.NETのETW計装はEventSourceクラスで独自プロバイダーを定義し、dotnet-traceはETWそのものではなくクロスプラットフォームなEventPipe経由でこれを収集する。ILoggerを使えば、同じログ呼び出しをAddEventLogでイベントログへ、ファイルシンクへ、EventSourceLoggerProvider経由でETWへとまとめて振り分けられる。
flowchart LR
accTitle: Windowsイベントログ・ETW入門の知識マップ
accDescr: ファイルログ・イベントログ・ETWが役割分担の関係にあること、イベントログの利用がイベントソースの登録を前提とすること、ETWがEventSourceとEventPipe・dotnet-traceを介して収集されること、ILoggerが複数のプロバイダーへ同じログ呼び出しを振り分ける仕組みを示す図
windows_event_log["Windowsイベントログ"]
etw["ETW(Event Tracing for Windows)"]
eventlog_source["イベントソース"]
admin_rights["管理者権限"]
ilogger_addeventlog["ILogger + AddEventLogプロバイダー"]
ilogger_abstraction[".NETのILogger"]
structured_logging_template["構造化ログのメッセージテンプレート"]
file_log["ファイルログ"]
event_viewer["イベントビューアー"]
wevtutil["wevtutil"]
get_winevent["Get-WinEvent"]
eventsource_class[".NETのEventSourceクラス"]
dotnet_trace["dotnet-trace"]
eventpipe["EventPipe"]
dotnet[".NET(Core以降)"]
dotnet_framework[".NET Framework"]
windows_event_log -.->|"前提とする"| eventlog_source
eventlog_source -->|"前提とする"| admin_rights
ilogger_addeventlog -->|"利用する"| windows_event_log
ilogger_addeventlog -->|"前提とする"| eventlog_source
ilogger_abstraction -->|"利用する"| structured_logging_template
ilogger_abstraction -.->|"利用する"| ilogger_addeventlog
ilogger_abstraction -.->|"利用する"| file_log
ilogger_abstraction -.->|"利用する"| etw
windows_event_log -->|"で確認できる"| event_viewer
windows_event_log -->|"で確認できる"| wevtutil
windows_event_log -->|"で確認できる"| get_winevent
etw -.->|"利用する"| eventsource_class
dotnet_trace -->|"利用する"| eventpipe
eventpipe -->|"利用する"| eventsource_class
etw -.->|"前提とする"| admin_rights
dotnet -.->|"前提とする"| windows_event_log
dotnet_framework -->|"両立しない"| eventpipe
dotnet -->|"利用する"| eventpipe
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全18件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. Windowsイベントログの基礎
Windowsには既定でApplication・System・Securityの3つのログがあり、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 の管理者特権が必要になるのはいつなのか」に整理していますが、イベントソースの登録もまさにこのパターンで、インストーラーで登録し、アプリ本体は登録済みのソースへ書き込むだけにするのが安全な設計です。
イベントには「レベル」と「イベントID」の2つの識別情報が付きます。レベルは EventLogEntryType(Information、Warning、Error、SuccessAudit、FailureAudit)で、Event Viewerのアイコンや既定のフィルターに使われます。イベントIDはアプリケーション定義の整数で、同じ種類の事象を後から検索・集計するためのキーになります。
3. .NETからイベントログへ書く
.NETからイベントログへ書く経路は大きく2つあります。先に前提条件の違いを並べておきます。
| 観点 | 3.1 System.Diagnostics.EventLog を直接使う |
3.2 ILogger + AddEventLog |
|---|---|---|
| イベントソース登録の要否 | 必要。WriteEntry に渡すソース名を、インストーラー側で CreateEventSource 済みにしておく3 |
必要。SourceName に指定する名前を同じくインストーラーで登録しておく。省略すると .NET Runtime という汎用名になる8 |
| 登録に必要な権限 | 管理者権限(登録時のみ。登録済みソースへの書き込みは標準ユーザーで可) | 同左 |
| 追加で必要な参照 | .NET(Core系)では System.Diagnostics.EventLog パッケージ |
Microsoft.Extensions.Logging.EventLog パッケージ(Generic Host では Windows 上で既定有効)9 |
| 指定できる項目 | レベル・イベントID に加えて、カテゴリやバイナリデータの添付まで細かく指定できる | レベル・イベントID・メッセージテンプレート。カテゴリやバイナリデータの添付は扱えない |
| 出力量の絞り込み | 呼び出し箇所ごとに自分で判断する | settings.Filter でイベントログ向けだけレベルを引き上げられる |
| 向いている場面 | ログ基盤を持たない小さなサービス、既存のWin32的な作りからの移植 | すでに ILogger で構造化ログを組んでいるアプリ |
どちらの経路でも、指定したソース名が事前に登録されていることが前提です。未登録のソース名で書き込むと、イベント自体はApplicationログに入るものの、対応するメッセージリソースがないためイベントビューアーが説明文を表示できません。10
3.1 System.Diagnostics.EventLogを直接使う
低レベルなAPIで、細かい制御(カテゴリ、バイナリデータの添付など)が必要な場合に向いています。.NET(.NET Framework以外)では System.Diagnostics.EventLog NuGetパッケージの参照が必要です。
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 で構造化ログを組んでいるなら、Microsoft.Extensions.Logging.EventLog パッケージの AddEventLog を使うほうが既存のロギングパイプラインに素直に乗ります。ASP.NET CoreやGeneric HostベースのWorker Serviceでは、Windows上で動く場合に既定でEventLogプロバイダーが有効になっています。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 書けたことを確認する
コードを書いたら、実際にイベントビューアーで見えるところまでを1度は通しておきます。手順は次のとおりです。
Win + Rからeventvwr.mscを実行してイベントビューアーを開きます。- 左ペインで イベント ビューアー(ローカル) > Windows ログ > Application を開きます(日本語環境では「アプリケーション」と表示されることがあります)。カスタムログを作った場合は、アプリケーションとサービス ログ の配下に自分のログ名が並びます。
- 件数が多くて埋もれる場合は、右ペインの 現在のログをフィルター を開き、「イベント ソース」に自分のソース名を、「すべてのイベント ID」に確認したいIDを入れて絞り込みます。
コードで指定した値が、イベントビューアーのどの欄に出るかの対応は次のとおりです。ここを押さえておくと、「書いたはずのものが見つからない」ときにどこを見ればよいかが分かります。
| コード上の指定 | イベントビューアーでの表示 |
|---|---|
WriteEntry の第1引数 / settings.SourceName |
「ソース」列 |
eventID 引数 / ILogger の EventId |
「イベント ID」列 |
EventLogEntryType(Information・Warning・Error) / LogLevel |
「レベル」列(情報・警告・エラー) |
WriteEntry に渡した文字列 / テンプレート展開後のメッセージ |
一覧の下部、「全般」タブの説明文 |
LogName(既定は Application) |
どのログの配下に出るか |
コマンドラインで確認したい場合は、PowerShellの Get-WinEvent が扱いやすいです。-FilterHashtable にログ名とプロバイダー名(=ソース名)を渡すと、そのソースが書いたイベントだけを取得できます。11
# 自分のソースが書いた直近10件を、日時・ID・レベル・本文だけに絞って表示する
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'KomuraSoft.OrderService' } -MaxEvents 10 |
Select-Object TimeCreated, Id, LevelDisplayName, Message
ここで1件も返らない場合、原因はたいてい「ソース名の綴り違い」「ソースが未登録」「そもそも書き込みに失敗して例外を握りつぶしている」のいずれかです。ソースが登録済みかどうかは [System.Diagnostics.EventLog]::SourceExists('KomuraSoft.OrderService') で確認できます。
4. ETWとは何か ── OS全体を貫くトレース基盤
Event Tracing for Windows(ETW)は、カーネルからユーザーモードアプリケーションまでを共通の基盤で計装できるトレース機構です。カーネルイベント(ディスクI/O、プロセス生成など)とアプリケーションの独自イベントを、同じタイムライン上でまとめて記録・分析できるのが最大の特徴です。12
.NETでは System.Diagnostics.Tracing.EventSource を継承したクラスを作ることで、独自のETWプロバイダーを定義できます。1 ETWはPublish-Subscribe方式で動作し、購読者がいなければイベントは記録されません。つまり EventSource を実装しておくだけでは何も起きず、収集ツールで明示的に有効化して初めてイベントが記録されます。4
1章の判断表で「イベントログ=常時・運用向け」「ETW=オンデマンド・調査向け」と書いた違いは、この購読の有無から来ています。図にすると次のようになります。
flowchart LR
APP["業務アプリ"]
APP -->|"起動・停止・致命的エラーだけ"| EL["イベントログ<br/>常時記録され、OSが永続化する"]
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 のイベントを集められます。違いは3つあります。EventPipeは.NETがサポートする全プラットフォームで同じように動くのに対し、ETWはWindows専用であること。ETWの収集開始には管理者権限が要るのに対し、EventPipeは対象アプリと同じユーザーで実行していれば管理者権限が不要なこと。そして、EventPipeの守備範囲はマネージドコードとランタイムに限られ、OS・カーネルのイベントやネイティブのコールスタックは取れないことです。2 カーネルまで含めて追いたいときにETW(PerfView、WPR/WPA)が必要になるのは、この3つ目の制約が理由です。
収集した .nettrace ファイルはVisual StudioやPerfViewで開いて中身を確認します。より本格的なパフォーマンス調査、たとえばカーネルイベントまで含めた分析をしたい場合は、Windows Assessment and Deployment Kit(ADK)に含まれる Windows Performance Recorder(WPR)を使い、Windows Performance Analyzer(WPA)で見る構成になります。14 ただし、この記事の範囲は「ETWで何ができて、いつ使うか」までとし、PerfViewやWPAでの詳細な解析手順には立ち入りません。業務アプリの開発・保守という観点では、まず自分のプロバイダーをEventSourceで定義でき、dotnet-traceで収集できることまでを押さえれば十分です。
5. 構造化ログの実務
ILogger のメッセージテンプレートは、{PlaceHolder} という名前付きプレースホルダーを含む固定の文字列として書きます。これは単なる見た目の作法ではなく、テンプレート自体を変えずに引数だけを渡すことで、ログ基盤側がプレースホルダー名と値の対応関係(構造化データ)を保持できるという意味を持ちます。15
// 推奨: テンプレートは固定、引数は別に渡す
logger.LogWarning("Order {OrderId} の在庫確保に失敗しました。倉庫={WarehouseId}", orderId, warehouseId);
// 非推奨: 文字列連結・補間はテンプレートと値の対応関係を壊す
logger.LogWarning("Order " + orderId + " の在庫確保に失敗しました。倉庫=" + warehouseId);
logger.LogWarning($"Order {orderId} の在庫確保に失敗しました。倉庫={warehouseId}");
コードアナライザーのルール CA2254 は、この非推奨パターン(テンプレートが呼び出しごとに変わってしまう書き方)を検出してくれます。5 呼び出し頻度が高いホットパスでは、さらに LoggerMessageAttribute によるソース生成を使うと、ボクシングやテンプレート解析のコストを避けられます。16
using Microsoft.Extensions.Logging;
internal static partial class Log
{
[LoggerMessage(
EventId = 2001,
Level = LogLevel.Warning,
Message = "Order {OrderId} の在庫確保に失敗しました。倉庫={WarehouseId}")]
public static partial void StockReservationFailed(
this ILogger logger, string orderId, string warehouseId);
}
// 呼び出し側
logger.StockReservationFailed(orderId, warehouseId);
この構成なら、同じログ呼び出しから、ファイルログ(Serilog/NLogなどのシンク)には詳細を、イベントログには絞り込んだ内容を、ETWにはEventSourceLoggerProviderを通じて低コストなトレースを、それぞれ振り分けられます。ILoggerProvider を複数登録し、Filter でプロバイダーごとに出力レベルを変えるのが基本形です。自作ロガーの最小要件は「自作ロガーの最小要件と結合テストチェックリスト」、クラッシュ時にログとダンプをどう結び付けて残すかは「Windowsアプリのクラッシュ時にログとダンプを残す設計」を参照してください。
6. 収集と調査 ── 書いたログを読む側の話
イベントビューアーでは、ログ一覧から「フィルター」または「カスタムビュー」を使って、特定のソース・レベル・イベントIDだけを抽出できます。頻繁に使う条件はカスタムビューとして保存しておくと、次回以降の調査が速くなります。カスタムビューは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」タブ > 「手動でクエリを編集する」にチェック、という場所です。保存すると左ペインの「カスタム ビュー」配下に並び、以後はクリックするだけで、KomuraSoft.OrderService が書いたエラー(Level=2)と警告(Level=3)だけの一覧が開きます。イベントスキーマの 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: に渡しているのは、上のカスタムビューで使ったのと同じXPathの Select 要素の中身です。/f: は出力形式の指定で、人が目で追うなら text、スクリプトで機械的に処理するなら xml を選びます。xml を選ぶと、カスタムビューのXMLと同じスキーマがそのまま出てくるので、Provider や EventID を要素名で拾えます。エクスポートした .evtx ファイルは、イベントビューアーの「保存されたログを開く」から、別のPCでもそのまま開けます。
クラッシュ調査では、イベントログ・ファイルログ・クラッシュダンプの3つを時刻で突き合わせるのが基本です。イベントログに残った「致命的エラー」のタイムスタンプを起点に、同時刻のファイルログの詳細と、WER/ProcDumpで採取したダンプを照らし合わせます。ダンプの採取方法は「Windowsクラッシュダンプ収集入門 - WER/ProcDump/WinDbg」、採取したダンプの実際の解析手順は「WinDbg + SOSでクラッシュダンプを読む」にまとめています。
7. 落とし穴
- ソース未登録のまま書こうとして失敗する。 登録されていないソース名で
RegisterEventSource相当の呼び出しを行うと、イベント自体はApplicationログに書き込まれますが、対応するメッセージリソースDLLがないため、イベントビューアーは説明文を表示できずエラー表示になります。10 さらに、実行時にCreateEventSourceで自動登録しようとする設計では、非管理者ユーザーで動くプロセスの初回起動時に例外で落ちるという事故も起きがちです。ソース登録は必ずインストーラー側で完了させておきます。 - 「別ソース名で書けてしまう」混乱。 イベントログは、書き込み権限さえあれば、アプリが登録した覚えのない別のソース名でも書き込めてしまいます。ソース名はコンピューター上で一意である必要がありますが、これを守る仕組みはOS側にはなく、アプリ側の規律に委ねられています。7 汎用的すぎるソース名(会社名や製品名を含まない短い名前)を選ぶと、他のベンダーのアプリと衝突したり、意図せず他アプリのソース名を流用してしまったりする原因になります。
- ログの肥大化と保持ポリシー。 イベントログの既定の最大サイズはわずか512KBです。6 業務アプリが日常的にイベントログを使うなら、
wevtutil slやインストーラーでサイズを明示的に拡張し、上限に達したときに「古いものから上書きする」か「新しいものを破棄する」かを意識して設定してください。OverwriteOlderは非推奨化されており、指定しても実質的に「上書きしない」動作になり得る点にも注意が必要です。19 - 多言語環境でのメッセージ表示。 ローカライズされたメッセージリソースファイル(
MessageResourceFile)を使う構成にした場合、そのリソースDLLが存在しない・バージョンが違う環境でログを表示すると、「イベントID○○の説明を検出できません」という表示になります。転送されたイベントログを別サーバーで見る、あるいはアプリをアンインストールした後にログだけが残っている、といった場面で起こりやすい問題です。文字列を直接WriteEntryで書く(リソースファイルを使わない)構成であれば、この種の問題自体を持ち込まずに済みます。 - 権限不足で書けないケース。 誤解されがちですが、ソースの登録には管理者権限が必要な一方、登録済みのソースへの書き込みは標準ユーザーでも行えます。「管理者権限がないと動かない」と早合点してアプリ全体を管理者実行にする前に、実際にどの操作が権限を必要としているのかを切り分けてください。
8. まとめ
この記事の骨格は3点に集約できます。
- 3つの手段は役割分担です。 ファイルログは開発者が詳細を追うため、イベントログは運用担当者とOS標準ツールが異常に気づくため、ETW/EventPipeは性能調査や再現困難な不具合を掘るために使います。イベントログには起動・停止・致命的エラーだけを書き、詳細はファイルログに任せるのが基本形です(1章)。
- イベントソースの登録はインストーラーの仕事です。 登録に管理者権限が必要な一方、登録済みソースへの書き込みは標準ユーザーでも行えます。実行時に
CreateEventSourceで自動登録しようとする設計が、標準ユーザー環境での初回起動失敗を生みます(2章・3章・7章)。 - ETWは購読者がいて初めて記録されます。
EventSourceを書いただけでは何も起きません。裏を返せば、計装を仕込んでおくコストはほぼゼロで、必要になったときにdotnet-traceで拾える状態を作れるということです(4章)。
次に手を動かすなら、この順番が現実的です。
- 既存アプリのイベントログ出力を棚卸しし、イベントログに流している内容が「起動・停止・致命的エラー」に絞れているかを確認する。詳細ログまで流していたら、
AddEventLogのFilterで絞る(3章)。 - イベントIDの採番ルール(機能領域ごとの帯、一度払い出したIDは意味を変えない)を決めて、ドキュメントに残す(2章)。
- インストーラーにイベントソースの登録処理があるか、ログの最大サイズを既定の512KBから明示的に広げているかを確認する(7章)。
- 障害調査で毎回同じ絞り込みをしているなら、そのXPathをカスタムビューとして保存しておく(6章)。
- 性能面の相談が出そうな処理には、先に
EventSourceで開始・終了のイベントだけ仕込んでおく。収集していない間のコストはほぼゼロです(4章)。
関連記事
- Windows の管理者特権が必要になるのはいつなのか - UAC、保護領域、設計上の見分け方
- Windowsサービスの作り方と運用 ── タスクスケジューラとの使い分けからBackgroundServiceのサービス化まで
- タスクスケジューラのタスクが実行されない・0x1で終わる ── 原因の切り分けと安全な運用設計
- 自作ロガーの最小要件と結合テストチェックリスト
- Windowsアプリのクラッシュ時にログとダンプを残す設計
- Windowsクラッシュダンプ収集入門 - WER/ProcDump/WinDbg
- WinDbg + SOSでクラッシュダンプを読む ── 収集した後の実務解析入門
関連する相談領域
合同会社小村ソフトでは、Windows業務アプリのログ・イベントログ・ETW基盤の設計、障害調査を見据えた観測点設計の技術相談を扱っています。
参考リンク
-
Microsoft Learn, EventSource. EventSourceがETWおよびEventPipeに対応した.NET組み込みの高速構造化ログ機構であることについて。 ↩ ↩2
-
Microsoft Learn, EventPipe. EventPipeがETWやperf_eventsに似た.NETランタイム内蔵のトレース機構であること、EventSourceのイベントを収集できること、ETWがWindows専用かつ管理者権限を要するのに対しEventPipeはクロスプラットフォームで管理者権限が不要なこと、EventPipeの範囲がマネージドコードとランタイムに限られカーネルイベントやネイティブコールスタックを取得できないことについて。 ↩ ↩2
-
Microsoft Learn, EventLog.CreateEventSource Method. Windows Vista以降でイベントソースの作成に管理者権限が必要な理由(Securityログを含む全ログの検索が必要なため)について。 ↩ ↩2 ↩3
-
Microsoft Learn, Getting Started with EventSource. EventSourceがPublish-Subscribeパターンで動作し、購読者(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の3つの既定ログ、ソースは1台のコンピューター上で一意である必要があること、書き込み権限があれば任意の登録済みソース名で書き込めてしまう仕組みについて。 ↩ ↩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. EventPipeに基づくクロスプラットフォームなトレース収集ツールdotnet-traceの概要と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によるソース生成ログが、ボクシングやテンプレート解析のコストを避けられることについて。 ↩
-
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パフォーマンス調査の実務入門
業務アプリが「遅い」「CPUが張り付く」とき、どのツールで何を見るか。PerfViewとdotnet-traceの役割分担、CPUサンプリングの読み方、ThreadTimeによるブロック時間の調査まで、実務の調査手順を整理します。
Windowsアプリ 外注・受託開発を依頼する前に整理したいこと
Windowsアプリの外注・受託開発を依頼する前に、既存ソフト改修、装置連携、COM/ActiveX、配布・更新、保守の整理ポイントを解説します。
Windows I/Oの深層(第1回) ── すべての読み書きはIRPになる:I/Oシステムの全体像
WindowsのI/Oシステムを底から解説する連載の第1回です。オブジェクトマネージャーの名前空間、ドライバー・デバイス・ファイルの3つのオブジェクト、IRPのライフサイクル、CloseHandleの裏側までを図解で整理します。
障害対応は復旧で終わらない ── 小さな開発チームのためのポストモーテム(再発防止)の型
障害を「直して謝って終わり」にすると同じ障害を繰り返します。blameless postmortemを小規模チーム向けに翻訳し、1時間で書けるテンプレート、再発防止策の強度判断表、実施のトリアージまでをまとめます。
スリープ・休止・Modern Standbyと長時間稼働アプリ ── 「夜中に止まっていた」を設計で防ぐ
長時間動き続けるWindowsアプリが「朝見たら止まっていた」となる原因を、S3スリープ/休止/Modern Standbyの違いから整理します。スリープ中のタイマーやTCP接続の挙動、SetThreadExecutionStateによる抑止まで解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
不具合調査 / 長期稼働テーマ
再現しにくい不具合、通信停止、長期稼働障害、失敗パス検証を整理するトピックです。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
ログ・イベントログ・ETW基盤の設計はWindowsアプリ開発の相談範囲だからです。
不具合調査・原因解析
障害調査を見据えたログ設計は不具合調査・原因解析の相談に直結するためです。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- ファイルログがあればイベントログは不要ですか?
- 詳細な調査用途ならファイルログだけで十分なことも多いです。ただし、運用担当者がOS標準のイベントビューアーや監視ツール(タスクスケジューラの失敗通知、SCOMなど)でアプリの異常に気づけるようにしたいなら、イベントログへの要点だけの出力を併用するのが実務的です。両者は代替関係ではなく、読者が違う別レイヤーの記録だと考えてください。
- イベントIDの採番ルールはどう決めればよいですか?
- 決まった正解はありませんが、実務では『機能領域ごとに帯を分ける(起動系は1000番台、通信系は2000番台など)』『一度払い出したIDは意味を変えない・再利用しない』『欠番を許容する』という3点を守ると事故が減ります。ILoggerのEventIdは構造化ログの検索キーとしても使われるため、後からドキュメント化しやすい採番にしておくと保守時に役立ちます。
- SerilogやNLogを使っていますが、この記事の内容とどう関係しますか?
- SerilogやNLogは、ILoggerの下でファイル・イベントログ・ETW・外部SaaSなど複数のシンクへ同じログを振り分けるための実装の1つです。この記事で扱った『イベントログには要点だけ』『メッセージテンプレートを崩さない』という設計方針は、ILoggerを直接使う場合でもSerilog/NLog経由でも同じように当てはまります。イベントログ向けのシンク(Serilog.Sinks.EventLogなど)を使うか、標準のAddEventLogを使うかは実装上の選択です。
- ETWはどこまで学べば十分ですか?
- 業務アプリの開発・保守が主目的であれば、EventSourceで独自プロバイダーを作れること、dotnet-traceで自分のプロバイダーのイベントを収集できることまで押さえれば十分です。PerfViewでのコールスタック解析やWPRのカーネルイベント収集は、パフォーマンス調査を専門にする段階で学べば足りる領域で、この記事ではあえて範囲外にしています。