【テクニカル・上級編】VB.NETでの例外情報取得(Exception.StackTraceとInnerException):マルチレイヤーアプリにおけるエラー追跡の強化 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

【VB.NET極限知見】InnerExceptionとStackTraceを完全支配せよ:マルチレイヤーエラー追跡の極意

レガシーなVBAマクロの海を泳ぎ切り、現代の厳格な.NETエコシステムに至るまで、数多の基幹システムを構築・保守してきた者なら誰もが一度は直面する悪夢がある。

「ユーザーからの『エラーが発生しました』という一報。しかし、ログに残っているのは、DAL(データアクセス層)で発生した生々しい例外ではなく、UI層でキャッチされた薄っぺらい汎用例外のスタックトレースのみ。」

この情報ロンダリングとも言える例外の握りつぶしは、システムの保守性を殺す最大の癌である。
今回は、多層アーキテクチャ(マルチレイヤーアプリ)において、VB.NETの`Exception`オブジェクトが持つ真のポテンシャルを解放し、根本原因(Root Cause)へ一瞬で到達するための極限の知見を授ける。

1. 例外の「ラッピング」と失われる文脈

業務システムは、プレゼンテーション層、ビジネスロジック層(BLL)、データアクセス層(DAL)といったレイヤーに分離される。ここで重要となるのが「例外の抽象化と伝播」だ。

DALで発生した`SqlException`をそのままUI層まで垂れ流すのはアーキテクチャの観点から愚行である。ゆえに、各層は下位層からの例外をキャッチし、ドメインにより適した独自のカスタム例外(例: `BusinessLogicException`)でラップして上位へスローし直す必要がある。

しかし、ここで多くのVB.NETプログラマーが犯す致命的なミスがある。

.net
‘ 【アンチパターン】例外の文脈を断ち切る愚行
Try
‘ DALの処理を呼び出し
DataRepository.Execute()
Catch ex As Exception
‘ ★NG: InnerExceptionを指定せずに新規例外をスローしているため、元のスタックトレースが消滅する
Throw New BusinessLogicException(“データ処理に失敗しました。”)
End Try

上記のコードを実行した場合、上位層でキャッチされる例外の `StackTrace` は、この `Throw` を行った行を指すことになり、肝心の「どこでSQLが死んだのか」という根本的な情報は永遠の闇に葬り去られる

2. `InnerException` とスタックトレースの連鎖

この悲劇を防ぐ唯一にして絶対の手段が、コンストラクタにおける `InnerException` の明示的な引き渡しである。

VB.NET(.NET Framework / .NET Core)の `Exception` クラスチェーンは、例外のツリー構造を形成する。下位層の例外を上位の例外のコンストラクタにぶち込むことで、全レイヤーの足跡を完全にトレース可能にする。

以下に、マルチレイヤー環境における模範的な例外ハンドリングと、その全深部を再帰的に解析するロガーの実装を示す。

.net
Imports System
Imports System.Text

Namespace Enterprise.Architecture.Exceptions

‘ カスタム例外の基底:InnerExceptionの連鎖を確実にサポートする
Public Class LayeredApplicationException
Inherits Exception

Public Sub New(message As String)
MyBase.New(message)
End Sub

Public Sub New(message As String, innerException As Exception)
MyBase.New(message, innerException)
End Sub
End Class

End Namespace

Namespace Enterprise.Architecture.Services

Public Class OrderService

Public Sub ProcessOrder(orderId As Integer)
Try
‘ ビジネスロジックのつもりでDALを叩く
DataAccessLayer.ExecuteQuery(orderId)

Catch dbEx As System.Data.SqlClient.SqlException
‘ 【極限知見】DB層の物理エラーをBLL層のビジネス例外でラップし、InnerExceptionに封入する
Throw New Enterprise.Architecture.Exceptions.LayeredApplicationException(
$”注文ID: {orderId} の処理中にデータベース障害が発生しました。”,
dbEx)

Catch ex As Exception
‘ 予期せぬ例外の再ラップ
Throw New Enterprise.Architecture.Exceptions.LayeredApplicationException(
“注文処理の途中で致命的な予期せぬエラーが発生しました。”,
ex)
End Try
End Sub
End Class

Public Class DataAccessLayer
Public Shared Sub ExecuteQuery(id As Integer)
‘ 意図的に例外を発生させる(例:接続タイムアウト)
Throw New System.Data.SqlClient.SqlException(“A transport-level error has occurred…”)
End Sub
End Class

End Namespace

3. 根本原因(Root Cause)を暴き出す再帰的解析エンジン

例外がラップされ、何重にも階層化されたとしても、それを人間が読める形式で一網打尽にログへ吐き出せなければ意味がない。

ここで、`InnerException` を再帰的に辿り、全てのレイヤーにおけるメッセージと `StackTrace` を完全抽出するユーティリティメソッドの決定版を提示する。

.net
Public NotInheritable Class ExceptionInspector

Private Sub New()
‘ 静的クラスとしてのインスタンス化を抑止
^^End Sub

”’

”’ 例外チェーンを再帰的に解析し、全てのレイヤーのエラー情報を構築する
”’

Public Shared Function FormatDetailedStackTrace(ex As Exception) As String
Dim sb As New StringBuilder()
Dim depth As Integer = 0

Dim currentEx As Exception = ex

While currentEx IsNot Nothing
sb.AppendLine($”— [Layer Depth: {depth}] —“)
sb.AppendLine($”Exception Type: {currentEx.GetType().FullName}”)
sb.AppendLine($”Message: {currentEx.Message}”)
sb.AppendLine($”TargetSite: {If(currentEx.TargetSite IsNot Nothing, currentEx.TargetSite.ToString(), “N/A”)}”)
sb.AppendLine(“Stack Trace:”)
sb.AppendLine(currentEx.StackTrace)
sb.AppendLine()

‘ 次の階層(InnerException)へ降下する
currentEx = currentEx.InnerException
depth += 1
End While

Return sb.ToString()
End Function

End Class

この `FormatDetailedStackTrace` をグローバル例外ハンドラー(`AppDomain.CurrentDomain.UnhandledException` や ASP.NET / ASP.NET Core のミドルウェア)に組み込むことで、どのレイヤーで何が起きて、それがどう伝播したのかの完全な歴史がログファイルに刻まれることになる。

4. シニアアーキテクトが知るべき「メモリとパフォーマンスの罠」

最後に、パフォーマンスの極限を追求するアーキテクトへ向けて、例外に関する深淵な注意点を述べておく。

1. スタックトレース生成のコスト
VB.NET(.NET)において、`New Exception()` が実行され、そのインスタンスがスローされる瞬間、CLR(共通言語ランタイム)はCPUのコールスタックをキャプチャする。このスタックウォーク(Stack Walk)は非常に重い処理である。
業務ロジックの制御フロー(例:データが存在しないことの検知など)に例外を使用してはならない。例外は文字通り「例外的な異常事態」にのみ使用せよ。

2. StackTraceプロパティの遅延評価と文字列化
一度スタックトレース文字列が生成されると、それはヒープ上にメモリを占有する。不要な例外のインスタンス化や、ログ出力時の過剰な文字列結合(String Concatenation)は、GC(ガベージコレクション)のプレッシャーを高め、高負荷時のスループットを確実に低下させる。
マルチスレッド環境や高スループットなWebAPI / バッチ処理においては、例外ログの非同期出力や、必要な深度までの限定的な解析を常に意識すべきである。

総括

VB.NETにおける例外処理は、単なる `Try-Catch` の構文暗記ではない。
システム全体を貫くデータの流れと、エラーという負の情報のライフサイクルをデザインする高度なアーキテクチャそのものである。

`InnerException` を制し、レイヤー間の文脈を途切れさせることなくログへ昇華させること。それこそが、レガシーとモダンをつなぐプロフェッショナルなVB.NETエンジニアの生存戦略である。

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