【テクニカル・上級編】VB.NETアプリケーションの自動ログ出力基盤:NLogやlog4netを導入し、業務エラーの解析を容易にする構造化ログの設計 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

ログ出力は「記録」ではない。「システムの心電図」である

多くのVB.NET開発者が、`Debug.WriteLine`や不完全なテキストファイルへの追記でログを済ませている。だが、業務システムにおけるログとは、障害発生時の「黒箱」であるべきだ。何が起きたかを知るだけでなく、「なぜそうなったのか」を推論するためのデータ構造を最初から設計しなければならない。

今回は、NLogをエンジンに据え、レガシーとモダンが混在する環境下でも耐えうる「構造化ログ基盤」の構築について、アーキテクトの視点から説く。

1. なぜ「構造化ログ」なのか?

文字列としてログを吐き出す時代は終わった。ログはJSONやデータベースに解析可能な形で残すべきだ。

  • 相関ID (Correlation ID) の付与: Web API呼び出しや非同期処理の連鎖を追跡するために必須。
  • 構造化: ログを解析ツール(ELKスタックやAzure Monitor)に流し込むことで、特定のユーザーや特定の取引単位での検索を瞬時に行う。
  • パフォーマンス: ファイルI/Oを非同期で行う構造にしなければ、業務アプリケーションのレスポンスそのものを殺すことになる。

2. 実装:NLogを用いたプロ仕様の基盤構築

まずはNuGetから `NLog` と `NLog.Config` をインストールする。
設定ファイル(NLog.config)は実行ファイルと同階層に置くが、ハードコーディングは避け、環境変数でログ出力先を制御できるようにしておくのが定石だ。

構造化ログを出力するための設定例 (NLog.config)



















3. VB.NETにおけるメモリ管理とライフサイクル

VB.NET、特にGC(ガベージコレクション)が介入する環境下では、ログ出力によるメモリリークを警戒する必要がある。ログ出力のために巨大な文字列を結合し続けると、LOH (Large Object Heap) を圧迫し、フルGCを誘発してシステムが停止する。

ログラッパーの極意

ログ出力はシングルトンで管理し、`NLog.MappedDiagnosticsLogicalContext` (MDLC) を活用してコンテキスト情報を管理する。

Imports NLog

Public NotInheritable Class LoggerService
Private Shared ReadOnly logger As Logger = LogManager.GetCurrentClassLogger()

‘ コンテキストのスコープ制御。IDisposableを実装することで、
‘ usingブロック終了時に自動的にIDをクリアする
Public Shared Function BeginScope(correlationId As String) As IDisposable
MappedDiagnosticsLogicalContext.Set(“CorrelationId”, correlationId)
Return New ScopeDisposer()
End Function

Private Class ScopeDisposer
Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
MappedDiagnosticsLogicalContext.Remove(“CorrelationId”)
End Sub
End Class

Public Shared Sub LogError(ex As Exception, message As String)
‘ 構造化された例外情報を渡し、スタックトレースを破壊しない
logger.Error(ex, message)
End Sub
End Class

4. 伝説のアーキテクトからの忠告

① Windows APIとの協調

レガシーなVB6/VBAシステムからの移行期であれば、Windowsイベントログへの書き込みも併用すべきだ。`System.Diagnostics.EventLog` を使い、OS側の管理者に「異常検知」を通知せよ。ただし、イベントログは容量制限があるため、「重要エラーのみ」に絞るのが鉄則だ。

② パフォーマンスの重み

ログ出力は常に「同期」で行うべきではない。NLogのターゲット設定で `AsyncTargetWrapper` を使用し、ログ書き込みを別スレッドに逃がせ。これを怠ると、ファイルロック待ちでメイン処理が止まり、ユーザーから「システムが固まった」という苦情が飛んでくることになる。

③ オブジェクトの明示的解放

ログ出力先がデータベースの場合、接続のライフサイクル管理が命だ。`Using` ステートメントを徹底し、例外発生時にも確実にコネクションがクローズされることを保証せよ。

‘ 正しいUsingの使い方。リソースリークは許されない
Using conn As New SqlConnection(connectionString)
Try
conn.Open()
‘ … ログ書き込み処理
Catch ex As SqlException
‘ データベースへの書き込み失敗時は、最終防衛線としてローカルファイルへフォールバックする
FallbackLogger.Write(ex)
End Try
End Using

結論

ログとは、システムが放つ「声」である。
その声をいかにクリアに、いかに迅速に、いかに解析可能な形で拾い上げるか。それが、真のシニアエンジニアと、ただコードを書く者の分かれ道だ。

ログ設計を疎かにする者は、障害発生時に暗闇の中で迷路を彷徨うことになる。構造化ログという羅針盤を持ち、堅牢なシステムを構築せよ。

タイトルとURLをコピーしました