【テクニカル・上級編】【上級プロフェッショナル向け】robocopy実行ログをFSOでリアルタイム監視しエラー転送のみを即時ピックアップする解析エンジン – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

ログの深淵を覗く:FSOによるRobocopyリアルタイム監視エンジンの設計思想

バックアップ・レプリケーション業務において、`robocopy`は極めて信頼性の高い武器である。しかし、その出力は膨大であり、数テラバイトのデータを同期するプロセスにおいて、エラーが発生した瞬間に「気づけるか」という問いは、システムの可用性を定義する決定的な境界線となる。

多くのエンジニアは、ジョブ終了後に生成されたログファイルを解析する「事後処理」に甘んじている。だが、我々プロフェッショナルが求めるのは、エラーが書き込まれたその瞬間に、通信経路を確保し、アラートを飛ばす「即時性」である。

本稿では、VBScriptの`FileSystemObject (FSO)`を用い、ファイルポインタの挙動を制御することで、`robocopy`のログを疑似的に`tail -f`(リアルタイム追従)し、エラー行のみを極めて低負荷に抽出する解析エンジンの実装手法を詳解する。

1. アーキテクチャの核心:ポーリング・メカニズムの最適化

VBScriptには、Linuxの`inotify`のようなファイルシステムイベントを直接捕捉するネイティブな仕組みは存在しない。したがって、我々は「ポーリング(定期的な監視)」を選択せざるを得ない。

しかし、単なるループによる監視は、CPUリソースを無秩序に消費し、ホストOSのパフォーマンスを著しく低下させる。エンジニアが設計すべきは、「ファイルポインタの保持」と「スリープ・インターバルによるCPU占有率の制御」の高度なバランスである。

設計指針

1. ファイルポインタの追跡: ログファイルを開き、一度ファイルの末尾(EOF)までスキップする。これにより、監視開始前の過去ログを読み飛ばし、監視開始時点からの差分のみを対象とする。
2. 非ブロッキング的な読み取り: `AtEndOfStream`プロパティを利用し、新しい行が書き込まれた瞬間のみ読み取り処理をトリガーする。
3. リソースの完全解放: COMオブジェクトのライフサイクルを厳格に管理し、メモリリークとファイルロックの競合を回避する。

2. 実装コード:High-Performance Log Tailer

以下に、実戦投入可能なレベルの監視エンジンを実装したコードを示す。このスクリプトは、`robocopy`の出力ログを監視し、”ERROR” または “FAILED” という文字列を検知した瞬間に、エラー専用ログへ書き出す。

' ==============================================================================
' Module: RobocopyRealTimeMonitor
' Description: FSOを用いたrobocopyログのリアルタイム解析エンジン
' Author: Legendary Chief Architect
' ==============================================================================
Option Explicit

' 定数定義:システム環境に合わせたチューニングが必要
Const LOG_FILE_PATH = "C:\Logs\robocopy_backup.log"      ' 監視対象のログ
Const ERROR_LOG_PATH = "C:\Logs\alert_errors.log"       ' エラー抽出先
Const CHECK_INTERVAL_MS = 1000                          ' ポーリング間隔 (ms)
Const ERROR_PATTERN = "ERROR"                           ' 検知キーワード

Dim objFSO, objLogFile, objErrorLog
Dim strLine, lastPosition

Set objFSO = CreateObject("Scripting.FileSystemObject")

' 1. 監視対象ファイルの存在確認
If Not objFSO.FileExists(LOG_FILE_PATH) Then
    WScript.Echo "Critical Error: Target log file not found."
    WScript.Quit
End If

' 2. 解析エンジンの初期化
Call InitializeMonitor()

' 3. メイン監視ループ
WScript.Echo "Monitoring started: " & LOG_FILE_PATH
Do
    ' ファイルの最新状態を確認
    ProcessLogUpdates
    
    ' CPU負荷を抑制するための待機
    ' VBScript.Sleepはミリ秒単位での制御を可能にする
    WScript.Sleep CHECK_INTERVAL_MS
Loop

' ==============================================================================
' 関数: InitializeMonitor
' 役割: オブジェクトの生成と初期ポインタの設定
' ==============================================================================
Sub InitializeMonitor()
    On Error Resume Next
    
    ' エラー出力用ファイルのオープン (ForAppending)
    Set objErrorLog = objFSO.OpenTextFile(ERROR_LOG_PATH, 8, True)
    If Err.Number <> 0 Then
        WScript.Echo "Failed to open error log: " & Err.Description
        WScript.Quit
    End If

    ' 監視対象ファイルをオープン (ForReading)
    ' 最初は末尾までスキップして、過去のログを無視する
    Set objLogFile = objFSO.OpenTextFile(LOG_FILE_PATH, 1, False)
    objLogFile.AtEndOfStream = True ' EOFまで移動
    
    If Err.Number <> 0 Then
        WScript.Echo "Failed to open target log: " & Err.Description
        WScript.Quit
    End If
    On Error GoTo 0
End Sub

' ==============================================================================
' 関数: ProcessLogUpdates
' 役割: ファイルの差分を読み取り、パターンマッチングを行う
' ==============================================================================
Sub ProcessLogUpdates()
    On Error Resume Next
    
    ' ファイルが他のプロセス(robocopy本体)によってロックされている場合、
    ' OpenTextFileが失敗することがある。その場合はスキップして次周期へ。
    If Err.Number <> 0 Then
        Err.Clear
        Exit Sub
    End If

    ' 新しいデータがあるか確認
    Do While Not objLogFile.AtEndOfStream
        strLine = objLogFile.ReadLine
        
        ' パターンマッチング(大文字小文字を区別しない判定)
        If InStr(1, strLine, ERROR_PATTERN, vbTextCompare) > 0 Or _
           InStr(1, strLine, "FAILED", vbTextCompare) > 0 Then
            
            ' エラー検知:即時書き出し
            Call WriteAlert(strLine)
        End If
    Loop

    ' エラーハンドリング:ファイルがローテート(削除・再作成)された場合の復旧
    If Err.Number <> 0 Then
        HandleFileRotation
    End If
    
    On Error GoTo 0
End Sub

' ==============================================================================
' 関数: WriteAlert
' 役割: エラーログへの書き出し(実際にはここでメール送信APIを叩くことも可能)
' ==============================================================================
Sub WriteAlert(ByVal content)
    Dim strTimestamp
    strTimestamp = Now ' タイムスタンプを付与
    
    objErrorLog.WriteLine "[" & strTimestamp & "] DETECTED: " & content
    objErrorLog.Flush ' バッファを強制的にディスクへ書き出し
End Sub

' ==============================================================================
' 関数: HandleFileRotation
' 役割: ログファイルのローテート(サイズ制限等による再作成)への対応
' ==============================================================================
Sub HandleFileRotation()
    ' オブジェクトを明示的に解放し、再接続を試みる
    Set objLogFile = Nothing
    WScript.Sleep 2000 ' ファイルの解放を待つための猶予
    Set objLogFile = objFSO.OpenTextFile(LOG_FILE_PATH, 1, False)
    objLogFile.AtEndOfStream = True
End Sub

3. プロフェッショナルのための深掘り:技術的知見

このコードを単なる「動くスクリプト」として終わらせてはいけない。シニアエンジニアとして、以下の3点についてその設計意図を理解しておく必要がある。

A. オブジェクトのライフサイクルとメモリ管理

VBScriptはCOM(Component Object Model)に基づいている。`Set obj = Nothing` を行うことは、単なる変数のクリアではなく、参照カウントをデクリメントし、基盤となるCOMオブジェクトのデストラクタを呼び出すプロセスである。特にループ内でオブジェクトを生成・破棄する場合、明示的な解放を怠ると、メモリリークが発生し、長期間稼働する監視エージェントがシステムを不安定にする原因となる。

B. I/Oバッファリングと `Flush` の重要性

`objErrorLog.WriteLine` を呼び出しただけでは、データは即座に物理ディスクに書き込まれるわけではない。OSのファイルシステムバッファに留まる可能性がある。リアルタイム性を追求するならば、`objErrorLog.Flush` を明示的に呼び出し、バッファを強制的にディスクへフラッシュさせる必要がある。これにより、万が一のシステムダウン時でも、直前まで検知したエラーログの消失を防ぐことができる。

C. ファイルロックの競合回避

`robocopy`は実行中、出力ログファイルに対して排他的、あるいは共有モードでのロックを保持する。`FileSystemObject`の`OpenTextFile`は、ロックの状態によってはエラーを返す。本実装では `On Error Resume Next` を戦略的に配置し、エラー発生時には「現在のサイクルを破棄して次周期に持ち越す」という、レジリエンス(回復力)を持たせた設計にしている。

結論

レガシーなVBScriptであっても、その背後にあるオブジェクトの挙動、I/Oの特性、そしてOSの挙動を深く理解していれば、極めて堅牢で強力なミッションクリティカル・ツールへと昇華させることができる。

「動けばいい」という思考を捨て、「どのようにリソースを消費し、どのように失敗を予測し、どのように回復するか」を設計に組み込むこと。それこそが、我々アーキテクトに課せられた責務である。

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