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

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

膨大なログの嵐を鎮める: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`)に保持されたまま、ループを抜けてしまいます。この「最後のひと押し」こそが、アルゴリズムの完成度を決めます。

まとめ:一歩ずつ、確実に

今回のスクリプトは、単なる「ログの整理ツール」ではありません。
「大量のデータを、リソースを浪費せずに、論理的に処理する」という、エンジニアリングの基礎が詰まっています。

まずは、自分の環境にある小さなテキストファイルで試してみてください。うまく動いたときの感動は、あなたのエンジニアとしての血肉になります。

もしエラーが出ても、それは失敗ではありません。「なぜこのエラーが起きたのか?」を考える絶好のチャンスです。そこから学び、吸収していきましょう。

応援していますよ。次はもっと複雑な、自動化の極致へ一緒に進みましょう!

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