サーバーログの解析手法とVBScriptを使ったバッチ処理
経緯
出向先でIISのログを解析するツールを作成するように言われ、お手伝いすることになりました。クライアントの意向により、なるべくお手軽に、できればAccessベースでVBAつかって。とのことでした。期間は1週間を目処。当方、Web系のエンジニアだったので、使い慣れたスクリプト系で組もうと思いましたができないとのこと。途方に暮れた末、Microsoft製のログ解析ツールがあることを調べそれを中心にVBScriptで組みました。ちなみに、途中でギブした模様。
Microsoft Log Parser
ログパーサはログファイルのひとつひとつのファイルを、データベースでいうテーブルのように扱うことができるソフトです。ログファイル自体にSQLと似た構文を投げ、データを抽出することが可能になります。コマンドプロンプトから実行するタイプのもので、本体はCUIとなっています。ログパーサーをGUIで扱うツールなどもありますが、独自に使ったほうがしっくりきます。ログ以外にも、CSV、XML、など多岐にわたり、Microsoft製なのでレジストリやイベントログなどWindowsに密着したものを対象にすることもできます。出力も、CSV、XML、SQL、画像(チャート)など機能が豊富です。詳細はインストール先のヘルプなどにあります。日本語対応しているので基本英語NGでもいけます。
開発上の制限
サーバー系の運用、保守ではなるべくマシンに余計なものをいれないでスクリプトを実装してこれをつくってくれなどいわれます。オープンソース系と違い、あまり自由度がききません。PHPでローカル実行はこの時点でダメっぽいので、バッチ系を考えるとDOSバッチですがいざやってみるとデバッグが非常に難しいのでこの時点でダメ。最終的にVBScriptで連携処理する手法となります。ログパーサはMicrosoft製なのでなんとか入れさせてもらえました。VBAやっときゃよかった・・・
VBScriptとの連携
VBScriptでGUI部分、SQL生成処理を担当、実際のパース処理、出力処理はログパーサを使用ということにしました。仕様としては複数あるログをまとめて選択、マウスでVBScriptファイルにドロップします。
すると、VBScriptでドロップされたファイルを引数にとってあるので、それを元にSQL文を生成します。
コマンドプロンプトを通じ、ログパーサにそれを投げるという具合です。
実際のコードは以下。
Dim cnt
Dim sql
Dim sqlfrom
Dim outputfile
'ドロップされたファイルのパスを配列に取得
Set myArray = WScript.Arguments
'ドロップされたファイル総数
DDcount = myArray.Length
'配列の内容整形
cnt = 1
For Each pass_str in myArray
If cnt = 1 Then
from = pass_str
outputfile = pass_str
Else
from = from & ", " & pass_str
End If
cnt=cnt+1
Next
'抽出対象ファイルの連結
sqlfrom = " FROM " & from
'SQL構文の構築
sql = """%ProgramFiles%\Log Parser 2.2\LogParser.exe"""
sql = sql & " -i:IISW3C -o:CSV -headers:OFF "
sql = sql & """SELECT date, time, s-ip, cs-uri-stem, cs-uri-query, sc-bytes, cs-bytes, time-taken INTO "
'出力先CSVファイルパス
'into = "%HOMEPATH%\Desktop\output.csv"
into = Split(outputfile, "\")
intofile = into(Ubound(into))
intofile = "%HOMEPATH%\Desktop\" & intofile & ".csv"
sql = sql & intofile
'SQL抽出条件指定
sqlwhere = " WHERE (cs-uri-stem = '/favicon.ico' OR cs-uri-stem = '/index.php') AND sc-status = 200"""
'SQL構文連結
sql = sql & sqlfrom & sqlwhere
'SQL文確認
'MsgBox(sql)
'Set FSO = CreateObject("Scripting.FileSystemObject")
'Set oLog = FSO.CreateTextFile("C:\Users\hoge\Desktop\check.sql")
'oLog.WriteLine(sql)
'oLog.Close()
'Set oLog = Nothing
'Set FSO = Nothing
'抽出実行
startcmd = "cmd /c (" & sql & ")"
'MsgBox(startcmd)
Set objWshShell = WScript.CreateObject("WScript.Shell")
Set objWshExec = objWshShell.Exec(startcmd)
'ファイル出力完了
MsgBox("コンソールが閉じたらOKをクリックしてください。" & vbCr & vbCr & "【抽出ログ対象ファイル数】" & vbCr & DDcount)
'ファイル結果の表示
Set objFso = CreateObject("Scripting.FileSystemObject")
If objFso.FileExists(intofile) Then
MsgBox("【ファイル出力先】" & vbCr & intofile & vbCr & vbCr & "抽出対象済みのCSVが出力されました。")
Else
MsgBox("抽出対象がないため、ファイルは出力されませんでした。")
End If
Set objFso = Nothing
基本的にそれほど複雑というわけではありません。
引数でわたされたたくさんのログファイルのパスを、ループでカンマ結合しFromに代入、
WHEREは適当ですが、これを元に抽出条件を指定します。
成果物のファイルがあるなしで、抽出対象があるなしを判定しています。
おわり
VBScriptはMicrosoft製なので使い方しだいではとても便利です。
OS自体のバージョンによる差異も、今後もMicrosoft製なのでメンテしてくれるはずです。
なんといっても、パワーシェルをインストールしてないWindows機でも関係ないし、どこでもたいてい動くのがいいです。・・・開発の苦労を理解していないクライアント向けには最適かもしれません。
資料
Log Parserでログを統合的に扱い運用保守に役立てる(基本編)
Log Parserでログを統合的に扱い運用保守に役立てる(実践編)