Windowsアプリのクラッシュ時ログ出力方法
一番大事な考え方
落ちるプロセスの中だけで「必ずログを残す」ことはできないと割り切る。
スタック破損やメモリ破壊、強制終了まで含めると、in-processの最終ログは本質的にベストエフォート(できる限り頑張る)でしかない。だから次の3層で考える。
- 通常時の時系列ログ(普段から残す)
- 落ちる瞬間の最終クラッシュマーカー(最小限だけ残す)
- OSまたは別プロセスが残す証跡(WERダンプなど)
推奨アーキテクチャ:役割分担する
| フェーズ | 目的 | やること |
|---|---|---|
| 通常時 | 時系列を残す | 構造化ログ、境界イベント |
| クラッシュ時 | 最低限の証跡 | 最終クラッシュマーカー、WERダンプ |
| 終了直後 | 異常終了を検知 | 別プロセスでexit code記録、再起動判断 |
| 次回起動後 | 重い後処理 | 圧縮、アップロード、ユーザー通知 |
最小構成(小規模ツール向け)
- 通常ログ: ローカルの追記専用ファイル
- 最終クラッシュマーカー: 専用の短いファイル
- ダンプ: WER LocalDumps
- 次回起動時: 「前回異常終了しました」の表示
強めの構成(24時間運転向け)
- workerプロセス: 本体処理
- launcher/watchdog: 起動監視、exit記録、再起動
- WER LocalDumps: worker側
- 次回起動またはwatchdog: 診断情報回収
通常ログのベストプラクティス
ログには最低限以下を入れる。
- UTCタイムスタンプ
- PIDとTID(プロセスIDとスレッドID)
- アプリ名、バージョン、ビルド番号
- セッションID
- 操作ID(ジョブIDなど)
- 直前の外部作用(ファイル書き込み、DB更新、装置コマンド送信など)
- 例外の種類、HRESULT、Win32エラーコード
おすすめは 1行1イベントのJSON Lines形式。人間向けの長文より、後で複数ファイルを突き合わせられることが重要。
クリティカルイベントは同期的に書く
- 細かいイベント: 非同期バッファでOK
- Warning以上: 早めにフラッシュ
- 重要な境界イベント: 同期的に残す(ProcessStart, ConfigLoaded, ExternalCommandSentなど)
最終クラッシュマーカーのルール
ここはフル機能ロガーを作る場所ではない。 1回だけ、短く、確実に残す。
入れる情報:
- 発生時刻(UTC)
- PID / TID
- セッションID
- バージョン / ビルド番号
- どのフックから来たか(UnhandledExceptionなど)
- 例外の種類または例外コード
- 直前の操作ID
絶対にやってはいけないこと:
- DIコンテナからロガーを取得する
- async/awaitを使う
- ロック待ちをする
- UIダイアログを出す
- 圧縮やHTTP送信をする
クラッシュハンドラでやることはこれだけ:
- 多重突入を防ぐ
- 1行だけ書く
- フラッシュする
- 終了する
フレームワーク別の注意点
- WinForms:
ThreadExceptionで継続させるのは危険。プログラムミス相手には向かない。 - WPF:
DispatcherUnhandledExceptionも同様。記録の入口として使う。 - .NET共通:
AppDomain.UnhandledExceptionは最後の通知。重い回復処理はしない。 - ネイティブC++:
SetUnhandledExceptionFilterだけでなく、_set_invalid_parameter_handlerやset_terminateも拾う。
WER LocalDumpsを土台にする
WER LocalDumpsが一番扱いやすい。OS側でダンプを残せるから。
設定例:
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /v DumpFolder /t REG_EXPAND_SZ /d "C:\CrashDumps\MyApp" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /v DumpCount /t REG_DWORD /d 10 /f
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\MyApp.exe" /v DumpType /t REG_DWORD /d 2 /f
ダンプとPDBはセットで保管する。 ダンプだけあっても、その時のEXE/DLLとPDBがなければ読めない。
監視プロセスを入れると何が変わるか
watchdog(監視プロセス)は以下を記録できる。
- 子プロセス開始時刻と終了時刻
- exit code(終了コード)
- 再起動回数
- ダンプの有無
これだけで「本当にクラッシュしたのか」「OSシャットダウンだったのか」「ユーザーが閉じたのか」が見えるようになる。
よくあるNGパターン
catch(Exception)でログだけ出して続ける → 途中状態が残り、後続障害が増える- 非同期ロガーのキューだけを信じる → 落ちた瞬間にキューごと消える
- クラッシュハンドラでHTTP送信する → DNSや認証の問題が落ちた文脈に乗る
- ダンプはあるが通常ログと結びつかない → sessionやPIDが共通でない
- WinForms/WPFの未処理例外イベントで延命する → ゾンビ状態になりやすい
最低限の導入チェックリスト
- 通常ログが1行1イベントで残る
- 全ログにUTC、PID、TID、version、sessionがある
- 最終クラッシュマーカー専用ファイルがある
- WER LocalDumpsがアプリ単位で設定されている
- PDBと配布バイナリを保管している
- 次回起動時に前回異常終了を検知できる
- 検証機で意図的に落として、本当に残るか確認した
まとめ
「落ちる側のプロセスだけに期待しない」 これがすべて。
- 通常ログ、最終クラッシュマーカー、OS/別プロセス側の証跡に分ける
- クラッシュ時はローカルへ短く残すだけ
- 重い処理は再起動後か別プロセスへ回す
- WER LocalDumpsを土台にする
- 継続より、記録して終了を基本にする
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
クラッシュダンプ収集入門【WER/ProcDump/WinDbg】
Windows アプリで再現が難しいクラッシュを追うときに、WER LocalDumps、ProcDump、MiniDumpWriteDump をどう使い分け、ミニダンプとフルダンプを選び、WinDbg で何を見るかを入門としてまとめます。
例外処理の実践ルール【catchとログの判断基準】
深い helper での広い catch と各層の重複ログを避けるため、呼び出し階層のどこで例外を catch し、どこで主ログを出し、どこで結果化や回復を判断するかを実務観点で整理した解説記事です。
未想定例外発生時の対応判断フロー
想定していない例外が発生したとき、アプリを終了すべきか継続すべきかを判断するための実用的な指針をまとめた記事です。失敗単位、共有状態、外部副作用、ネイティブ境界の観点から3段階の選択肢と判断表で整理し、設計判断に直接使えます。
Application Verifier で作る異常系テスト基盤
Application Verifier の役割と、Handles・Heaps・Low Resource Simulation・!htrace を組み合わせた Windows ネイティブアプリ向け異常系テスト基盤の組み立て方を、harness 設計と合格条件まで具体的に整理...
長期稼働クラッシュの原因調査:ハンドルリーク編
24/7で動く産業用カメラ制御アプリが1か月後に突然クラッシュする原因をハンドルリーク観点で解説し、Handle Countの傾き計測、異常系を短ループで回す再現法、open/closeを追える構造化ログ設計までをまとめた前編記事です。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
不具合調査 / 長期稼働テーマ
再現しにくい不具合、通信停止、長期稼働障害、失敗パス検証を整理するトピックです。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
不具合調査・原因解析
再現しにくい障害、長期稼働後の不具合、通信停止などの調査を支援します。