ログの氾濫を制する:FSOによるシーケンシャル・デデュープリケーションの極意
システムが異常をきたした際、ログファイルは「情報の宝庫」から「解析を阻害するノイズの塊」へと変貌する。特に、ループ処理のバグや通信タイムアウトが頻発した際、同一のエラーメッセージが数万行にわたって出力される現象は、シニアエンジニアなら一度は経験したことがあるだろう。
この肥大化したログを、単なる「テキストエディタの検索」で片付けるのは素人の仕事だ。我々プロフェッショナルに求められるのは、「データの文脈(Context)を維持したまま、情報の密度を極限まで高めること」である。
本稿では、VBScriptの`FileSystemObject` (FSO) を用い、大量のログファイルから連続する重複行を検出し、出現回数を付与して1行に集約する「ログ圧縮エンジン」の設計思想とその実装について詳述する。
1. 設計思想:メモリ効率とI/Oコストのトレードオフ
多くの初級者が陥る罠は、`TextStream.ReadAll` を使用してファイル全体をメモリにロードすることだ。数GBに達するログファイルに対してこれを行うことは、システムのメモリを食いつぶし、最悪の場合、スクリプト自体がクラッシュする。
プロフェッショナルの設計は、「ストリーム・プロセッシング」に基づかなければならない。
状態遷移(State Machine)による走査
連続する重複をカウントするためには、以下の3つの状態を保持するステートマシンを構築する必要がある。
1. `currentLine`: 現在読み込んでいる行。
2. `previousLine`: 直前に読み込んだ行。
3. `occurrenceCount`: 直前行が継続している回数。
この3要素をループ内で回すことで、メモリ消費量を「ファイルサイズ」ではなく「単一行の最大長」に抑え込むことができる。
2. 実装:高密度ログ圧縮スクリプト
以下に、実戦投入可能なレベルのコードを示す。単なる動作確認用ではなく、エラーハンドリング、オブジェクトの明示的解放、そして大規模ファイルへの耐性を考慮した構成となっている。
'==============================================================================
' Script Name: LogCompressor.vbs
' Description: 連続する重複ログ行を集約し、視認性を向上させる。
' Author: Legendary Chief Architect
'==============================================================================
Option Explicit
' 定数定義
Const TR_READ = 1
Const TR_WRITE = 2
Dim fso, inputFile, outputFile
Dim tsIn, tsOut
Dim currentLine, prevLine, compressedLine
Dim count, targetFile, resultFile
' --- 設定領域 ---
targetFile = "C:\Logs\system_error.log" ' 処理対象ファイル
resultFile = "C:\Logs\system_error_compressed.log" ' 出力先
' ----------------
Set fso = CreateObject("Scripting.FileSystemObject")
' 1. 入力ファイルの存在確認
If Not fso.FileExists(targetFile) Then
WScript.Echo "Error: Target file not found: " & targetFile
WScript.Quit
End If
On Error Resume Next
' 2. ストリームのオープン
' 読み込み用:1行ずつ走査するため ReadOnly でオープン
Set tsIn = fso.OpenTextFile(targetFile, TR_READ)
' 書き出し用:新規作成(既存があれば上書き)
Set tsOut = fso.OpenTextFile(resultFile, TR_WRITE, True)
If Err.Number <> 0 Then
WScript.Echo "Error: Failed to open files. " & Err.Description
WScript.Quit
End If
' 3. メイン・ロジック(ストリーム・プロセッシング)
prevLine = ""
count = 0
Do Until tsIn.AtEndOfStream
currentLine = tsIn.ReadLine
' 境界条件:ファイル開始時
If count = 0 Then
prevLine = currentLine
count = 1
' 重複判定:直前の行と同じ場合
ElseIf currentLine = prevLine Then
count = count + 1
' 差異検出:直前の行と異なる場合、これまでの集約結果を書き出す
Else
WriteCompressedLine tsOut, prevLine, count
prevLine = currentLine
count = 1
End If
Loop
' 4. 最終行のフラッシュ
' ループ終了時、バッファに残っている最後の行を書き出す
If count > 0 Then
WriteCompressedLine tsOut, prevLine, count
End If
' 5. オブジェクトの明示的解放(メモリリーク防止の鉄則)
tsIn.Close
tsOut.Close
Set tsIn = Nothing
Set tsOut = Nothing
Set fso = Nothing
If Err.Number = 0 Then
WScript.Echo "Process Completed Successfully."
Else
WScript.Echo "An error occurred during processing: " & Err.Description
End If
On Error GoTo 0
'==============================================================================
' Sub WriteCompressedLine
' 引数: tsOut (TextStream), line (String), c (Integer)
' 役割: 集約された情報を整形して出力する
'==============================================================================
Sub WriteCompressedLine(ts, line, c)
Dim outputMsg
If c > 1 Then
' 重複がある場合は [xN] 形式で付与
outputMsg = "[" & c & " times] " & line
Else
' 単発の行はそのまま出力
outputMsg = line
End If
ts.WriteLine outputMsg
End Sub
3. アーキテクトの視点:極限の最適化と保守
このコードを単なる「動くスクリプト」で終わらせてはならない。運用環境に組み込む際には、以下の3点を意識せよ。
オブジェクトのライフサイクル管理
VBScriptにおける `Set obj = Nothing` は、単なる作法ではない。大規模なバッチ処理において、参照が残ったままのオブジェクトはメモリ断片化を招き、長時間稼働するシステムにおいて予測不能な挙動を引き起こす原因となる。特に `FileSystemObject` はOSのファイルハンドルを掴むため、明示的な `.Close` と `Nothing` 代入は絶対条件である。
I/O パフォーマンスの最適化
上記のコードでは、行が切り替わるたびに `tsOut.WriteLine` を実行している。これはメモリ消費を最小限に抑えるためのトレードオフだ。もし、さらに速度を追求し、メモリに余裕がある環境であれば、一定行数(例:1000行)ごとに文字列バッファに溜め込み、一括で書き出す「チャンク・ライティング」を検討すべきだろう。
レガシー環境への配慮
VBScriptは、Windowsのレガシーなインフラストラクチャにおいて、今なお強力な武器となる。PowerShellが導入されていない、あるいは実行ポリシーの制限で高度なスクリプトが動かせない環境において、このFSOによるストリーム処理は、極めて軽量かつ確実なソリューションを提供する。
結論
大量のログを捌く技術とは、単に文字を読み書きする技術ではない。「限られたリソースの中で、いかにデータの意味を損なわずに、ノイズを削ぎ落とすか」という、情報工学の本質的な問いへの回答である。
このスクリプトを基盤とし、君たちの現場における「情報の濁流」を、洗練された「知見の源泉」へと変えていただきたい。
