【テクニカル・上級編】ADOの接続エラーをログファイルに書き出す:運用保守のためのエラーハンドリング – Access VBA解析バイブル

スポンサーリンク

ADO接続異常の深層:実戦に耐えうる堅牢なエラーロギングの構築

現場で稼働するAccessVBAアプリケーションの寿命を決定づけるのは、華やかなUIでも洗練されたクエリでもない。「異常系における振る舞いの精密さ」、これに尽きる。

特に、SQL Serverや外部基幹DBへADO(ActiveX Data Objects)を介して接続するシステムにおいて、ネットワークの瞬断、認証情報の失効、あるいはDB側のリソース枯渇による接続失敗(`-2147467259` 等の不条理なエラー)は不可避の現実である。

「なぜ繋がらないのか」を現場のオペレーターに尋ねても、「なんかエラーが出ました」以上の情報は得られない。この無駄なデバッグ工数をゼロにし、運用保守のコストを極限まで圧縮するための「ADO接続エラーのロギングアーキテクチャ」をここに提示する。

—

1. ADOエラーハンドリングの設計思想:なぜ「`On Error Resume Next`」だけでは罪深いのか

多くのVBAプログラマは、接続失敗を恐れるあまり、次のようなコードを書く。

‘ 決して真似してはならないアンチパターン
On Error Resume Next
conn.Open strConn
If Err.Number <> 0 Then
MsgBox “接続に失敗しました。”
Exit Sub
End If
On Error GoTo 0

この実装の何が問題か。
1. エラーのコンテキストが消滅する:ADOは、単一のVBランタイムエラーの裏側で、複数の詳細なエラー情報(`Errors`コレクション)を保持している。`Err.Number`だけでは、ODBCドライバレベルの真の原因(例:SQL Server側のファイアウォール拒否、ログインタイムアウト)が闇に葬られる。
2. オブジェクトのリーク:接続試行中の異常終了により、メモリ上にインスタンスが残留し、Accessのプロセス bloat(肥大化)を引き起こす。

シニアエンジニアが実装すべきは、「ADOの`Errors`コレクションの全摘出」と「FileSystemObject(FSO)による確実な同期I/Oログ出力」を組み合わせた、自己診断機能を持つ接続モジュールである。

—

2. 実装:堅牢なADO接続と構造化ログ出力モジュール

以下のコードは、単にエラーをキャッチするだけでなく、発生時刻、マシン名、ユーザー名、そしてADOが吐き出した全ての生エラーコードを時系列でログファイルに刻む、実戦投入仕様のクラスレス・モジュールである。

Option Explicit

‘ ログファイルの出力先(UNCパスやローカルの適切なフォルダに変更すること)
Private Const LOG_FILE_PATH As String = “C:\AppLogs\Access_ADO_Error.log”

Public Sub ExecuteRobustConnection()
Dim conn As Object
Dim strConn As String
Dim startTime As Double

startTime = Timer

‘ 接続文字列の構築(OLEDBプロバイダの例)
strConn = “Provider=SQLOLEDB;Data Source=ServerName;Initial Catalog=DBName;User Id=UserName;Password=Password;Connect Timeout=5;”

‘ ADO Connectionオブジェクトの生成
Set conn = CreateObject(“ADODB.Connection”)

‘ 接続タイムアウトの設定(明示的)
conn.ConnectionTimeout = 5

On Error GoTo ErrorHandler

‘ 接続試行
conn.Open strConn

‘ — 接続成功時の処理 —
‘ Debug.Print “Connection established in ” & (Timer – startTime) & ” sec.”

‘ 正常系クローズ処理
conn.Close
Set conn = Nothing
Exit Sub

ErrorHandler:
‘ 致命的なエラー捕捉とログ記録
Call WriteAdoErrorLog(conn, Err.Number, Err.Description)

‘ クリーンアップ
On Error Resume Next
If Not conn Is Nothing Then
If conn.State = 1 Then conn.Close
Set conn = Nothing
End If

‘ オペレーターへの最小限の通知と処理の安全な中断
MsgBox “データベースへの接続に失敗しました。” & vbCrLf & _
“詳細なエラー内容がログに記録されました。” & vbCrLf & _
“システム管理者に連絡してください。”, vbCritical, “接続エラー”

Exit Sub
End Sub

Private Sub WriteAdoErrorLog(ByRef conn As Object, ByVal runtimeErrNum As Long, ByVal runtimeErrDesc As String)
Dim fso As Object
Dim ts As Object
Dim adoErr As Object
Dim logMessage As String
Dim logStream As String

‘ FSOの遅延バインディング生成
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ ログ保存先のディレクトリが存在しない場合は強制生成
Dim logDir As String
logDir = fso.GetParentFolderName(LOG_FILE_PATH)
If Not fso.FolderExists(logDir) Then
fso.CreateFolder logDir
End If

‘ ログストリームの構築
logStream = “————————————————–” & vbCrLf
logStream = logStream & “Timestamp : ” & Format$(Now, “yyyy-mm-dd hh:nn:ss”) & vbCrLf
logStream = logStream & “User : ” & Environ$(“USERNAME”) & ” (” & Environ$(“COMPUTERNAME”) & “)” & vbCrLf
logStream = logStream & “VBA Error : #” & runtimeErrNum & ” – ” & runtimeErrDesc & vbCrLf

‘ ADO Errorsコレクションの深掘り(ここが決定的な情報源となる)
If Not conn Is Nothing Then
If conn.Errors.Count > 0 Then
logStream = logStream & “[ADO Error Details]” & vbCrLf
For Each adoErr In conn.Errors
logStream = logStream & ” -> Number : ” & adoErr.Number & vbCrLf
logStream = logStream & ” -> Source : ” & adoErr.Source & vbCrLf
logStream = logStream & ” -> Desc : ” & adoErr.Description & vbCrLf
logStream = logStream & ” -> Native : ” & adoErr.NativeError & vbCrLf
Next adoErr
End If
End If

‘ ファイルへの書き込み(ForAppending = 8, Create = True = -1)
On Error Resume Next
Set ts = fso.OpenTextFile(LOG_FILE_PATH, 8, True)
If Err.Number = 0 Then
ts.Write logStream
ts.Close
End If

‘ オブジェクトの完全解放
Set ts = Nothing
Set fso = Nothing
End Sub

—

3. アーキテクチャの急所:なぜFSOと遅延バインディングなのか

上記のコードには、レガシー環境やマルチバージョンが混在するAccess運用において、極めて意図的な設計上の選択がなされている。

A. FSO(FileSystemObject)によるファイルI/Oの優位性

VBA標準の `Open` ステートメント(`Open “C:\…” For Append As #1`)は、レガシーであり、ファイル共有違反や文字コードのトラブルが発生しやすい。FSOを使用することで、堅牢なファイルストリーム操作が可能になり、さらにディレクトリの自動生成といったフォールトトレラントな処理を数行で実装できる。

B. 遅延バインディング(`CreateObject`)の徹底

`Dim conn As ADODB.Connection` と早期バインディング(参照設定)を行うことは、開発時には便利である。しかし、クライアントPCごとにOfficeのビット数(32bit / 64bit)やMDAC/WICのバージョン差異が存在する環境において、参照設定の破損(”MISSING:” エラー)はアプリを一撃で起動不能にする。
あえて `CreateObject(“ADODB.Connection”)` および `CreateObject(“Scripting.FileSystemObject”)` を用いる遅延バインディングを採用することで、実行時まで型の解決を遅らせ、環境依存のクラッシュを回避している。

C. ADO `Errors` コレクションの全件走査

VBAの `Err` オブジェクトは、直近の1つのエラーしか保持しない。しかし、データベースプロバイダ(SQL Server等)は、ネットワーク層、ODBCドライバ層、SQL Serverエンジン層という複数のレイヤーでエラーを発生させ、それをADOの `Errors` コレクションにスタックしていく。
このコードでは、スタックされた全てのエラー(`NativeError`含む)をログファイルに吐き出させるため、DBA(データベース管理者)にログを渡した瞬間に原因特定が完了するレベルの情報を担保している。

—

4. チーフアーキテクトからの提言:運用を自動化せよ

ログを出力する仕組みを作っただけでは、プロフェッショナルの仕事とは言えない。「誰も読まないログファイル」は存在しないのと同じだ。

1. ログローテーションの組み込み:ログファイルが肥大化してディスクを圧迫しないよう、FSOでファイルサイズを監視し、月次あるいは10MBを超えた時点で `YYYYMMDD_Archive.log` にリネームする仕組みを拡張せよ。
2. ネットワーク監視との統合:もし特定の端末で断続的にこの接続エラーログが頻発する場合、それはアプリのバグではなく、クライアントPCの省電力機能によるNIC(ネットワークインターフェースカード)の切断が原因であるケースが多い。ログのユーザー名とタイムスタンプから、インフラ起因の問題を切り分けろ。

エラーを隠蔽するな。エラーを構造化し、記録し、システムを透明化せよ。それこそが、長期稼働を義務付けられたAccessVBAシステムを守り抜く唯一の道である。

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