【実務・中級編】【実務中級者向け】FSOで読み込んだ大量のログから「連続する重複メッセージ」を1行に間引いて視認性を高めるログ圧縮バッチ – VBScript (Visual Basic Scripting Edition)解析バイブル

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

【極限の知見】FSOによるログ圧縮エンジニアリング:ノイズを削ぎ落とし、本質を抽出する

システム障害の火消しに追われている時、我々の前に立ちはだかるのは、膨大な「ノイズ」だ。
同一のエラーメッセージが数万行、律儀に並んでいるログファイル。これをテキストエディタで開こうものなら、メモリを食いつぶし、アプリケーションは沈黙する。

「とりあえず、重複を消せばいいんだろう?」

もし君がそんな安易な思考でコードを書こうとしているなら、今すぐその手を止めてほしい。実務におけるデータクレンジングは、単なる「文字列の比較」ではない。リソースの制約、ファイルロックの挙動、そして計算量の最適化を考慮した、極めてエンジニアリング的な設計が求められる領域だ。

本稿では、VBScriptの`FileSystemObject` (FSO) を使い、大量のログから「連続する重複メッセージ」をスマートに集約する、プロダクションクオリティのログ圧縮スクリプトの設計思想を伝授する。

1. 初心者が陥る「メモリ破綻」の罠

多くの開発者が最初に書くコードは、おそらくこうだ。
`strContent = objFile.ReadAll`

ファイルの中身を一度に全てメモリへロードし、配列に分割してループを回す。小規模なファイルならこれで十分だ。しかし、数GBに及ぶログファイルに対してこれを行えば、スクリプトは即座に `Out of Memory` で死ぬ。

プロフェッショナルは、ファイルを「ストリーム」として扱う。
一度に全てを読み込むのではなく、1行ずつ読み込み、処理し、書き出す。この「逐次処理」こそが、メモリ使用量を一定(定数時間 $O(1)$)に保つための絶対条件である。

2. 設計思想:ステートマシンによる「状態管理」

連続する重複をカウントするためには、「今見ている行」と「直前に見た行」を比較する状態(State)を持つ必要がある。

ロジックの核心は以下の通りだ。
1. 現在の行を読み込む。
2. 「現在の行」が「直前の行」と一致するか判定する。
3. 一致する場合: カウンタをインクリメントする。
4. 一致しない場合: 直前の行を「(カウント数付きで)出力」し、現在の行を「新しい直前の行」として保持、カウンタをリセットする。

この設計により、ファイル全体を走査する際の計算量は $O(n)$ となり、極めて高速に動作する。

3. 実装例:堅牢なログ圧縮バッチ

以下に、実務でそのまま投入可能なレベルまで作り込んだコードを示す。単なるロジックだけでなく、エラーハンドリングとリソースの解放(ライフサイクル管理)に魂を込めた。

' ==============================================================================
' Script Name: LogCompressor.vbs
' Description: 連続する重複行をカウントし、1行に集約するログクレンジングツール
' Author: Chief Architect
' ==============================================================================
Option Explicit

' --- 設定エリア ---
Dim strInputFile, strOutputFile
strInputFile  = "C:\Logs\system_error.log"  ' 処理対象ファイル
strOutputFile = "C:\Logs\system_error_compact.log" ' 出力先

' --- メイン処理 ---
Main

Sub Main()
    Dim objFSO, objInFile, objOutFile
    Dim strCurrentLine, strPrevLine, strLineContent
    Dim intRepeatCount
    Dim errStatus

    Set objFSO = CreateObject("Scripting.FileSystemObject")

    ' 1. ファイルの存在確認(堅牢性の基本)
    If Not objFSO.FileExists(strInputFile) Then
        WScript.Echo "Error: 入力ファイルが見つかりません: " & strInputFile
        WScript.Quit 1
    End If

    On Error Resume Next ' エラーハンドリング開始

    ' 2. ファイルストリームのオープン
    ' ※ 読み込みは ForReading、書き込みは ForWriting
    Set objInFile  = objFSO.OpenTextFile(strInputFile, 1) 
    Set objOutFile = objFSO.CreateTextFile(strOutputFile, True)

    If Err.Number <> 0 Then
        WScript.Echo "Error: ファイルのオープンに失敗しました。ファイルが他で使用中かもしれません。" & vbCrLf & Err.Description
        WScript.Quit 1
    End If

    ' 3. 初期化
    strPrevLine = ""
    intRepeatCount = 0

    ' 4. 逐次処理ループ(ストリーム処理によるメモリ節約)
    Do Until objInFile.AtEndOfStream
        strCurrentLine = objInFile.ReadLine
        
        ' 前の行と同じかチェック
        If strCurrentLine = strPrevLine Then
            intRepeatCount = intRepeatCount + 1
        Else
            ' 前の行が空でない場合、これまでの集計結果を書き出す
            If strPrevLine <> "" Then
                WriteCompressedLine objOutFile, strPrevLine, intRepeatCount
            End If
            
            ' 状態の更新
            strPrevLine = strCurrentLine
            intRepeatCount = 1
        End If
    Loop

    ' 5. 最終行の書き出し(ループ終了後に残ったバッファをフラッシュ)
    If strPrevLine <> "" Then
        WriteCompressedLine objOutFile, strPrevLine, intRepeatCount
    End If

    ' 6. 後処理(オブジェクトのライフサイクル管理)
    objInFile.Close
    objOutFile.Close
    
    If Err.Number <> 0 Then
        WScript.Echo "Error: 処理中にエラーが発生しました: " & Err.Description
    Else
        WScript.Echo "Success: ログ圧縮が完了しました。" & vbCrLf & "Output: " & strOutputFile
    End If

    ' オブジェクトの解放
    Set objInFile = Nothing
    Set objOutFile = Nothing
    Set objFSO = Nothing
    
    On Error GoTo 0
End Sub

' ------------------------------------------------------------------------------
' 関数名: WriteCompressedLine
' 引数: objStream (出力ストリーム), strLine (メッセージ), intCount (出現回数)
' 役割: 集約された形式でファイルに書き込む
' ------------------------------------------------------------------------------
Sub WriteCompressedLine(objStream, strLine, intCount)
    If intCount > 1 Then
        ' 2回以上連続する場合: "メッセージ (x10)" の形式
        objStream.WriteLine strLine & " (x" & intCount & ")"
    Else
        ' 1回のみの場合: そのまま出力
        objStream.WriteLine strLine
    End If
End Sub

4. プロフェッショナルとしての注意点:現場で起きる「不都合な真実」

このスクリプトを実戦投入する際、君が直面するであろう壁について予言しておこう。

① ファイルロックの壁

対象のログファイルが、稼働中のシステムによって「排他的」に書き込みロックされている場合、`OpenTextFile` は失敗する。
対策: ログを直接読みに行くのではなく、一旦 `FileCopy` で一時ファイルを作成してから、そのコピーを処理するのが定石だ。

② 文字コード(Encoding)の壁

FSOは、伝統的に ANSI (Shift-JIS) または Unicode (UTF-16) を扱う。もしログが UTF-8 で出力されている場合、FSOでは文字化けを引き起こす。
対策: もしUTF-8を扱う必要があるなら、`FileSystemObject` を捨て、`ADODB.Stream` オブジェクトを使用せよ。これがプロの使い分けだ。

③ エラーハンドリングの深さ

`On Error Resume Next` は諸刃の剣だ。エラーを無視して進むのではなく、「何が起きたか」を検知し、適切なリソース解放(`Close`)を行ってから終了するように設計しなければならない。放置されたファイルハンドルは、システム全体の不安定化を招く。

結びに代えて

自動化とは、単に作業を減らすことではない。「予測可能な、堅牢な仕組みを構築すること」だ。
今回紹介したストリーム処理と状態管理の考え方は、VBScriptに限らず、あらゆるプログラミング言語に通ずる普遍的な原理原則である。

この知見を血肉とし、単なる「作業者」から、システムを制御する「エンジニア」へと昇華してほしい。

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