膨大なログの嵐を鎮める:FSOで実現する「連続重複メッセージ」のスマート圧縮術
こんにちは。現場の自動化を支えるエンジニアの皆さん。
日々、システムから吐き出されるログと格闘していませんか?
「エラーが止まらない!ログファイルが数GBになっていて、テキストエディタすら開けない……」
「同じエラーメッセージが1万行続いていて、本当に知りたい情報が埋もれてしまった……」
そんな経験がある方に、今日伝授したいのは「ログの視認性を劇的に向上させるデータクレンジング」の技術です。
今回は、VBScriptの強力な武器である`FileSystemObject (FSO)`を使い、連続する重複メッセージを「1行+出現回数」へと集約する、実戦的なスクリプトを構築していきましょう。単にコードを書くだけでなく、「なぜその書き方をするのか」というプロの設計思想についても触れていきますね。
ここをクリアすれば、あなたは「マクロを記録する人」から「システムを制御するエンジニア」へと一歩踏み出せるはずですよ。
1. 戦略を立てる:どうやって「重複」を見抜くのか?
いきなりコードを書き始めるのは、設計図なしでビルを建てるようなものです。まずはアルゴリズム(手順)を整理しましょう。
大量のログを効率よく処理するためには、「ファイル全体を一度にメモリに読み込まない」ことが鉄則です。数GBのファイルを `ReadAll` で読み込もうとした瞬間、あなたのPCのメモリは悲鳴を上げ、スクリプトはクラッシュするでしょう。
私たちが取るべき戦略は、「1行ずつ読み進め、直前の行と比較する」というストリーム処理です。
処理のロジック図解
1. 準備: 入力ファイルを開き、出力用の新しいファイルを作成する。
2. 走査: 1行読み込む。
3. 比較:
- 「今読んだ行」が「前回読んだ行」と同じなら $\rightarrow$ カウントを+1。
- 「今読んだ行」が「前回読んだ行」と違うなら $\rightarrow$ 前回の行を「内容 + 回数」の形式で出力し、カウントをリセット。
4. 更新: 「今読んだ行」を「前回の行」として記憶し、次の行へ。
5. 完了: ファイルの終端に達したら、最後に残ったデータを書き込んで終了。
2. 実装:ログ圧縮バッチの全貌
それでは、魂を込めたコードを見ていきましょう。このまま `.vbs` ファイルとして保存して実行できる構成にしています。
' ==============================================================================
' Script Name: LogCompressor.vbs
' Description: 連続する重複メッセージをカウントして1行に集約する
' Author: Senior Architect
' ==============================================================================
Option Explicit ' 変数の宣言を強制(バグを防ぐためのプロの作法です)
' --- 設定エリア ---
Dim strInputFile, strOutputFile
strInputFile = "C:\temp\system_error.log" ' 解析したい元のログ
strOutputFile = "C:\temp\system_error_compressed.log" ' 結果の出力先
' --- メイン処理 ---
CompressLog strInputFile, strOutputFile
MsgBox "ログの圧縮が完了しました!" & vbCrLf & strOutputFile, vbInformation, "完了"
' ==============================================================================
' 関数: CompressLog
' 引数: strIn (入力パス), strOut (出力パス)
' ==============================================================================
Sub CompressLog(strIn, strOut)
Dim objFSO, objInFile, objOutFile
Dim strCurrentLine, strPrevLine
Dim lngCount
Dim blnIsFirstLine
' FileSystemObjectの生成
Set objFSO = CreateObject("Scripting.FileSystemObject")
' 入力ファイルの存在チェック(エラー回避の基本!)
If Not objFSO.FileExists(strIn) Then
MsgBox "エラー: 入力ファイルが見つかりません。" & vbCrLf & strIn, vbCritical
Exit Sub
End If
' ファイルを開く (ForReading: 読み取り専用)
Set objInFile = objFSO.OpenTextFile(strIn, 1)
' ファイルを作成 (True: 上書き許可)
Set objOutFile = objFSO.CreateTextFile(strOut, True)
strPrevLine = ""
lngCount = 0
blnIsFirstLine = True
' --- 1行ずつ走査するループ ---
Do Until objInFile.AtEndOfStream
strCurrentLine = objInFile.ReadLine
If blnIsFirstLine Then
' 最初の1行目の処理
strPrevLine = strCurrentLine
lngCount = 1
blnIsFirstLine = False
Else
' 直前の行と比較
If strCurrentLine = strPrevLine Then
' 同じ内容ならカウントアップ
lngCount = lngCount + 1
Else
' 違う内容になったら、これまでの集計結果を書き出す
WriteCompressedLine objOutFile, strPrevLine, lngCount
' 状態を更新
strPrevLine = strCurrentLine
lngCount = 1
End If
End If
Loop
' --- ループ終了後、最後に残った行を書き出す ---
If Not blnIsFirstLine Then
WriteCompressedLine objOutFile, strPrevLine, lngCount
End If
' --- オブジェクトの解放(ライフサイクル管理) ---
' これを忘れるとファイルがロックされたままになります
objInFile.Close
objOutFile.Close
Set objInFile = Nothing
Set objOutFile = Nothing
Set objFSO = Nothing
End Sub
' ==============================================================================
' サブルーチン: WriteCompressedLine
' 役割: 集約された情報を整形してファイルに書き込む
' ==============================================================================
Sub WriteCompressedLine(objFile, strText, lngNum)
If lngNum > 1 Then
' 2回以上連続していた場合: [内容] (x15)
objFile.WriteLine strText & " (x" & lngNum & ")"
Else
' 1回のみの場合: そのまま出力
objFile.WriteLine strText
End If
End Sub
3. アーキテクトの視点:ここが「プロ」の分かれ道
コードを読み解く準備はできましたか? 初級者と中級者、そしてプロのエンジニアを分けるポイントを解説します。
① `Option Explicit` の重要性
コードの冒頭にある `Option Explicit`。これは「すべての変数を明示的に宣言せよ」という命令です。
これがないと、タイポ(打ち間違い)で `strPrevLine` を `strPreveLine` と書いてしまった際、VBScriptはエラーを出さずに「新しい変数」として勝手に処理を続行してしまいます。バグの温床を未然に防ぐ、エンジニアの規律です。
② メモリへの優しさ:`ReadLine` vs `ReadAll`
今回のコードでは `objInFile.ReadLine` を使用しています。
- `ReadAll`: ファイル全部をメモリにドカッと載せる。小規模なら速いが、巨大ファイルでは死ぬ。
- `ReadLine`: 1行ずつメモリに乗せては捨てる。メモリ使用量は常に一定。
大量のログを扱う現場では、「メモリ消費量を予測可能にする」ことが、安定したシステム運用の絶対条件です。
③ オブジェクトのライフサイクル
コードの最後に `Set objFSO = Nothing` と書いている部分に注目してください。
VBScript(COMオブジェクト)の世界では、オブジェクトを使い終わったら明示的に「使い終わりました」と伝える(参照を解放する)ことが推奨されます。これを怠ると、「スクリプトは終わっているのに、ファイルが『他のプログラムが使用中』で開けない!」という、現場でよくあるトラブルを引き起こします。
④ 境界条件(エッジケース)の処理
ループが終わった直後に、なぜ `WriteCompressedLine` をもう一度呼んでいるのでしょうか?
これは、「最後の行のグループ」を書き忘れないためです。ループは「次の行が読み込めるか」で判定するため、最後の行に到達したとき、そのデータはメモリ(`strPrevLine`)に保持されたまま、ループを抜けてしまいます。この「最後のひと押し」こそが、アルゴリズムの完成度を決めます。
まとめ:一歩ずつ、確実に
今回のスクリプトは、単なる「ログの整理ツール」ではありません。
「大量のデータを、リソースを浪費せずに、論理的に処理する」という、エンジニアリングの基礎が詰まっています。
まずは、自分の環境にある小さなテキストファイルで試してみてください。うまく動いたときの感動は、あなたのエンジニアとしての血肉になります。
もしエラーが出ても、それは失敗ではありません。「なぜこのエラーが起きたのか?」を考える絶好のチャンスです。そこから学び、吸収していきましょう。
応援していますよ。次はもっと複雑な、自動化の極致へ一緒に進みましょう!
