ADOの接続エラーをログファイルに書き出す:運用保守のための堅牢なエラーハンドリング設計
開発現場で最も恐ろしいのは、テスト環境では完璧に動いていたシステムが、本番環境のユーザーのPCで突如として沈黙することだ。特に、外部データベースやSQL ServerへADO(ActiveX Data Objects)経由で接続する際、ネットワークの瞬断、認証情報の変更、ファイアウォールの仕様変更などにより、接続エラーは容赦なく発生する。
「なんか繋がらなくなったんですけど」
ユーザーからのこの抽象的な一言に、開発者が何時間もログの無いコードの海を彷徨う――この不毛な時間を終わらせよう。
今回は、Access VBAにおけるADO接続の失敗を確実に捕捉し、詳細なエラーコード、発生時刻、そしてADO特有の複数エラー情報までをもれなくFSO(FileSystemObject)でログファイルに出力する、「プロフェッショナルな運用保守設計」を授ける。
—
なぜ「On Error Resume Next」だけのコードは悪なのか?
初学者や急ごしらえのコードによありがちなアンチパターンがこれだ。
‘ 【最悪な実装例】
On Error Resume Next
cnn.Open connectionString
If Err.Number <> 0 Then
MsgBox “接続に失敗しました。”
Exit Sub
End If
この書き方がなぜ現場のエンジニアとして失格なのか。理由は3つある。
1. 原因が消える: `Err.Number` は直近のエラーしか保持しない。ADOの接続失敗時には、単一のVBAエラーだけでなく、Providerが返す複数の詳細なエラー(Connection.Errorsコレクション)が発生していることが多く、これらをドブに捨てることになる。
2. 証拠が残らない: ユーザーが「エラーが出ました」と言っても、画面を閉じればエラーメッセージの全貌は消え去り、開発者はデバッグの手がかりを失う。
3. サイレント・フェイルの恐怖: 接続に失敗したまま後続のレコードセット操作を実行し、データベースではなくローカルのゴミデータを書き換えるといった致命的なデータ破損を誘発する。
プロのアーキテクチャにおいて、エラーとは「隠すもの」ではなく「記録し、解析させるための最も貴重な情報資産」である。
—
堅牢なADOエラーハンドリングの全体設計
プロダクションコードとして耐えうるシステムには、以下の要素が必須となる。
1. トランザクションと接続のスコープ管理: 接続オブジェクトは常にクリーンアップを保証する(Finally句のイディオム)。
2. ADO `Errors` コレクションの全件走査: 1回の接続試行で発生したすべてのエラーチェーンをログに焼き付ける。
3. FSOによる堅牢なI/O: ログファイル出力時の排他制御やパス解決を確実に実装する。
—
コピペで使えるプロダクションコード
以下のコードを標準モジュールに配置し、実務の基幹処理に組み込んでほしい。保守性を極限まで高めるため、エラーハンドリングとログ書き込みの責任を明確に分離している。
Option Explicit
‘ ==============================================================================
‘ モジュール名: modDatabaseConnector
‘ 概要 : ADO接続と堅牢なエラーロギングを提供するプロフェッショナルモジュール
‘ ==============================================================================
‘ 接続テスト用のパブリックプロシージャ
Public Sub ExecuteSecureConnection()
Dim cnn As Object
Dim connectionString As String
‘ 例:SQL Serverへの接続文字列(環境に合わせて変更してください)
connectionString = “Provider=MSOLEDBSQL;Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;”
Set cnn = CreateObject(“ADODB.Connection”)
cnn.CommandTimeout = 30
cnn.ConnectionTimeout = 15
On Error GoTo ErrorHandler
‘ 接続試行
cnn.Open connectionString
‘ — 接続成功時の処理 —
MsgBox “データベースへの接続に成功しました。”, vbInformation, “接続成功”
‘ (ここに実際のデータ処理を記述)
CleanExit:
‘ 接続のクリーンアップ(ライフサイクル管理の鉄則)
If Not cnn Is Nothing Then
If cnn.State = 1 Then cnn.Close
Set cnn = Nothing
End If
Exit Sub
ErrorHandler:
‘ 致命的なエラーをキャッチし、ログへ記録
Call WriteADOErrorLog(cnn, Err.Number, Err.Description)
MsgBox “データベース接続に失敗しました。” & vbCrLf & _
“詳細はログファイルを確認してください。”, vbCritical, “致命的なエラー”
Resume CleanExit
End Sub
‘ ==============================================================================
‘ 担当者必携:ADOエラーおよびFSOログ出力エンジン
‘ ==============================================================================
Private Sub WriteADOErrorLog(ByRef cnn As Object, ByVal vbaErrNum As Long, ByVal vbaErrDesc As String)
Dim fso As Object
Dim logFile As Object
Dim logPath As String
Dim errObject As Object
Dim i As Long
On Error GoTo LogWriterError
‘ ログファイルの保存先(カレントデータベースと同じ階層の “Logs” フォルダ)
logPath = CurrentProject.Path & “\Logs”
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ログ用ディレクトリが存在しなければ作成
If Not fso.FolderExists(logPath) Then
fso.CreateFolder (logPath)
End If
‘ ログファイルを開く(存在しない場合は作成、追加書き込みモード: ForAppending = 8)
Dim logFilePath As String
logFilePath = logPath & “\DB_Error_” & Format(Date, “YYYYMMDD”) & “.log”
Set logFile = fso.OpenTextFile(logFilePath, 8, True)
‘ — ログヘッダーの書き込み —
logFile.WriteLine “————————————————–”
logFile.WriteLine “発生時刻 : ” & Now
logFile.WriteLine “VBA Err : #” & vbaErrNum & ” – ” & vbaErrDesc
‘ — ADO独自のErrorsコレクションを完全走査 —
‘ ※SQL ServerやOracleなどのプロバイダは、単一の操作で複数のエラーを返すことがある
If Not cnn Is Nothing Then
If cnn.Errors.Count > 0 Then
logFile.WriteLine “— [ADO Provider Errors Detail] —”
For i = 0 To cnn.Errors.Count – 1
Set errObject = cnn.Errors(i)
logFile.WriteLine ” [” & i & “] Description : ” & errObject.Description
logFile.WriteLine ” Number : ” & errObject.Number
logFile.WriteLine ” NativeError : ” & errObject.NativeError
logFile.WriteLine ” Source : ” & errObject.Source
logFile.WriteLine ” SQLState : ” & errObject.SQLState
Next i
End If
End If
logFile.WriteLine “————————————————–”
logFile.Close
Set logFile = Nothing
Set fso = Nothing
Exit Sub
LogWriterError:
‘ ログ書き込み自体が失敗した場合のフォールバック(無限ループ防止のためDebug.Print)
Debug.Print “CRITICAL: ログファイルの書き込みに失敗しました – ” & Err.Description
If Not logFile Is Nothing Then logFile.Close
Set logFile = Nothing
Set fso = Nothing
End Sub
—
アーキテクチャの解説:プロが仕込んだ3つのこだわり
1. `cnn.Errors` コレクションの全件ループ
VBAの `Err` オブジェクトだけでは、ODBCやOLEDBドライバが吐き出した具体的なSQLSTATEやデータベース側のネイティブエラーコード(例: ログイン失敗、パスワード期限切れなど)が隠蔽されてしまう。ADOの `Errors` をインデックスで走査し書き出すことで、トラブルシューティングのスピードが桁違いに向上する。
2. 日付別のログファイル分割 (`DB_Error_YYYYMMDD.log`)
すべてのエラーを1つの巨大なファイルに書き込んではならない。ファイルサイズが肥大化し、I/Oのパフォーマンスが劣化する。日付ごとにファイルを分割することで、保守性を担保し、不要になった古いログのローテーション(削除)も容易になる。
3. FSOによるフォルダ自動生成とフォールバック
ログ出力先の `Logs` フォルダが存在しない環境であっても、コード側で自動生成する(`fso.CreateFolder`)。また、万が一ディスクの容量不足や権限エラーでログ書き込み自体が失敗した場合でも、`LogWriterError` ラベリングによってメインの業務処理がクラッシュ(フリーズ)しない防壁を張っている。
—
運用保守の現場から:明日への提言
システム開発において「エラーが起きないこと」を祈る設計はアマチュアのそれである。「エラーは必ず起きる、だから起きた後にどうトレースできるようにするか」をデザインするのがプロのエンジニアだ。
この仕組みを導入すれば、ユーザーから「繋がらない」と言われた際、開発者は「`Logs` フォルダの中にある本日のログファイルをメールで送ってください」と言うだけで済む。ログを開けば、エラーコード、SQLState、発生時刻が鮮明に記録されている。
手戻りのない、知的でスマートな保守運用体制を、あなたのAccessアプリにも今すぐ実装してほしい。
