FSOとRegExpでログを制する:VBScriptによる堅牢な時間帯別集計エンジンの構築術
現場の自動化を進める際、避けて通れないのが「ログの解析」だ。しかし、多くのエンジニアが陥る罠がある。`Line Input`での全行読み込み、力任せの文字列分割(`Split`関数の乱用)、そしてメモリを浪費する巨大な配列操作。これらは、ログが数メガバイトを超えた瞬間に崩壊する。
今日は、VBScriptにおける「ファイルシステムオブジェクト(FSO)」と「正規表現(RegExp)」を、単なるツールではなく、「ストリーム処理のエンジン」として実装する極意を伝授する。
—
1. ログ解析における「メモリ効率」と「堅牢性」の原則
ログ解析バッチにおいて最も重要なのは、「ファイルを一度にメモリへ載せないこと」だ。FSOの`OpenTextFile`で取得した`TextStream`オブジェクトを、一行ずつ、かつ必要最小限の正規表現マッチングで処理する。これが、低スペックな環境でも安定して動作する唯一の解だ。
陥りがちなアンチパターン
- `ReadAll`の多用: 数百MBのログでこれをやれば、即座にメモリ不足(Out of Memory)で落ちる。
- 過剰な文字列操作: `Mid`や`InStr`をネストさせるコードは、保守性が極めて低い。正規表現で「構造」を捉えるべきだ。
- エラーハンドリングの欠如: ファイルがロックされている、あるいは空であるという「異常系」を考慮しないスクリプトは、プロダクションコードとは呼べない。
—
2. 実践:時間帯別集計エンジンの設計と実装
今回は、`YYYY-MM-DD HH:MM:SS [Level] Message` という形式のログから、特定の時間帯(例: 09:00〜17:59)のエラー発生数を集計するエンジンを作成する。
プロダクションコード例
Option Explicit
‘ ログ解析のメイン処理
Call Main()
Sub Main()
Dim fso, ts, regEx, matches, line, logTime, timeSlot
Dim errorCounts : Set errorCounts = CreateObject(“Scripting.Dictionary”)
‘ 正規表現オブジェクトの構築 (ログのタイムスタンプとレベルを抽出)
Set regEx = New RegExp
regEx.Pattern = “^(\d{4}-\d{2}-\d{2})\s(\d{2}):\d{2}:\d{2}\s\[ERROR\]”
regEx.IgnoreCase = True
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ファイルが存在するか確認(堅牢性の基本)
If Not fso.FileExists(“C:\logs\application.log”) Then
WScript.Echo “ログファイルが見つかりません。”
Exit Sub
End If
‘ ストリームを開く(読み取り専用、Unicode未指定)
Set ts = fso.OpenTextFile(“C:\logs\application.log”, 1, False)
Do Until ts.AtEndOfStream
line = ts.ReadLine
‘ 正規表現でマッチング
If regEx.Test(line) Then
Set matches = regEx.Execute(line)
‘ 時刻部分(matches(0).SubMatches(1))をキーに集計
timeSlot = matches(0).SubMatches(1) & “時台”
If Not errorCounts.Exists(timeSlot) Then
errorCounts(timeSlot) = 0
End If
errorCounts(timeSlot) = errorCounts(timeSlot) + 1
End If
Loop
ts.Close
‘ 結果出力
Call PrintResults(errorCounts)
End Sub
Sub PrintResults(dict)
Dim key
WScript.Echo “— 時間帯別エラー発生数 —”
For Each key In dict.Keys
WScript.Echo key & “: ” & dict(key) & ” 件”
Next
End Sub
—
3. この設計の技術的ポイント
正規表現による構造抽出
`regEx.Pattern = “^(\d{4}-\d{2}-\d{2})\s(\d{2}):\d{2}:\d{2}\s\[ERROR\]”`
このパターンは、単純な文字列検索よりも遥かに強力だ。特に`SubMatches`を活用することで、日時やレベルを切り出す手間を完全に排除している。将来的にログフォーマットが微修正されても、正規表現のパターンを書き換えるだけで対応可能だ。
Dictionaryオブジェクトによる高速集計
連想配列である`Scripting.Dictionary`を使用しているのがポイントだ。これを自前で配列とループで実装しようとすると計算量が爆発するが、Dictionaryを使えばハッシュテーブルの恩恵により、O(1)に近い速度で集計が可能となる。
—
4. プロフェッショナルへのアドバイス:DB連携と注意点
もし、この結果をデータベース(SQL ServerやAccess)に書き込む必要があるなら、以下の点に留意してほしい。
1. トランザクションの意識: ログの行ごとに`INSERT`を発行してはいけない。集計結果が確定してから、一括でコミットする設計にすること。
2. FSOの解放: スクリプトが途中でエラー終了した際、`TextStream`が開いたままになる可能性がある。必ず`On Error Resume Next`を併用し、`If Not ts Is Nothing Then ts.Close`のようなクリーンアップ処理を構築せよ。
3. 文字コードの問題: `OpenTextFile`はデフォルトでASCII/ANSIを想定する。UTF-8のログを扱う場合は、`ADODB.Stream`を使用する高度な実装に切り替える必要がある。
結びに
VBScriptは「古い言語」と揶揄されることもあるが、OS標準のコンポーネントのみで完結し、かつ適切に設計されたコードは極めて軽量で安定している。今日伝えた「一行ずつ処理する」「正規表現で構造を抽出する」「辞書オブジェクトで集計する」というアプローチは、PythonやPowerShellへ移行しても通用する「自動化の本質」だ。
さあ、目の前の退屈なログ集計を、このエンジンで一瞬にして終わらせてほしい。君ならできるはずだ。
