2008年8月31日日曜日

IISのログファイルをタスクで月ごとに圧縮する

WindowsServer2003 に付属のHTTP他もろもろサーバ、IIS6を長いこと動かすとログがたまってくる。logrotateみたく圧縮ローテーション&自動削除ならいいのに。
 
サイトが少しならいいけど、サーバに何十とWEBサイトを作るとログファイルは結構な量になるので、容量はかさむしNTBackupにかかる時間は延びるし運用上よろしくない。
 
ということから作成して使っていたVBScriptを公開する。目的の大半は自分があとで使いまわすためですが、興味がある人は使ってみたらよいと思います。
WindowsServer2008+IIS7の環境でも動作することを確認。これでまだしばらく使い物になる。
cabarc.exeは取りあえずMicrosoft Cabinet SDKのものを使った。2008のサポートツールにも入ってるのかな?

 
 

一応、スクリプトの使い方と処理内容を記述しておく。
 
使い方は叩くだけ、丸々一カ月分/約30のログファイルが1つの cab ファイルにまとまる。
ログファイルの場所は特に指定しないでよい。
 
処理は下記の流れでやっている。

  1. WMIでIISのWEBサイト設定から、ログファイルの保存フォルダを取得

  2. 先月分のログファイルがあるか確認する
    例) 現在時刻が2008年8月に実行した場合、"ex200807*.log" に該当するファイルを探す

  3. 全部cabに圧縮して、成功したら元のログファイルを消去

  4. 1から3の処理をIIS上のWEBサイトすべてに対して繰り返す


 

なお、このスクリプトを使うにはサポートツールがいるので事前に導入しておく必要がある。
 
ほか、FTPのログも対象にしているのでFTPのサイトがないとエラーになる。IISのFTPを使っていない場合はFTPのところは消すべし、"objFTPsvc" の定義と、2つ目の大きなループを消せばOK.
 
 

以下スクリプト、拡張子VBSで保存して実行する。
結構前に作ったのだけどい書き直したい個所も結構あるが動くのでまあいいや。
ほか、RunCapturedファンクションはMicrosoftのどこかで拾った、スクリプトガイだったかな?
[sourcecode language='vb']
'// IISのログをcab に圧縮
'// 要サポートツールのcabarc.exe
'// 引数を省略したら先月のログを圧縮、指定したい場合は -2 など何か月前のログにするか指定、月単位

'// 共通オブジェクト定義

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objW3svc = GetObject("IIS://localhost/W3SVC")
Set objFTPsvc = GetObject("IIS://localhost/MSFTPSVC")
set objWshShell = WScript.CreateObject("WScript.Shell")
'// cmd.exeの場所
strComspec = "C:\WINDOWS\system32\cmd.exe"

'何ヶ月前のexYYMMを作成するか
Dim CountDiff
' 引数なければ1ヶ月前
if WScript.Arguments.count=0 Then
CountDiff = -1
Else
if WScript.Arguments(0) >= 0 Then
Wscript.Quit(0)
Else
CountDiff = WScript.Arguments(0)
End if
End if

'単位指定 mなら○ヶ月, dなら○日
Dim strCountType
strCountType = "m"

'// 日付文字列作成
'Date型でずらした日付作成
dtDate = DateAdd(strCountType, CountDiff, Date)
'YYMM作成
strDate = Right("0000" & Year(dtDate),2) & Right("00" & Month(dtDate),2)


'// IIS用のファイル名定義
strCompTargetFile = "ex" & strDate & "*.log"
strCompFileName = "ex" & strDate & ".cab"



'// 開始をイベントログに書き込み
objWshShell.LogEvent 4, "Tsk_IISLogCompress.vbs の 開始"


'// IISのログフォルダに対する処理

'// http、W3SVC
for each obj in objW3svc
' IISWebServerのクラスを取得
if obj.Class = "IIsWebServer" then
' ログフォルダのパス作成
strWLogFoderName = "W3SVC" + obj.Name
strWLogfilepath = obj.LogFileDirectory + "\" + strWLogFoderName

' フォルダの存在チェックして、あればフォルダオブジェクト作成
if objfso.folderexists(strWLogfilepath) then
Set objFolder = objFSO.GetFolder(strWLogfilepath)
'ファイルの存在フラグリセット
flgExsitLog = false

'指定されたexYYMM*.logの存在チェック
For each strFileNames in objFolder.Files

flgCheckFileA = strComp(Left(strCompTargetFile,6),Left(objFSO.GetBaseName(strFileNames),6),1)
flgCheckFileB = strComp("log",objFSO.GetExtensionName(strFileNames),1)
' ファイル名と拡張子で条件一致しているか判断
if flgCheckFileA = 0 and flgCheckFileB = 0 Then
'ファイルの存在フラグをセットしてさっさと抜ける
flgExsitLog = true
Exit For
End if
Next

'exYYMM*.logが存在していたら、ログファイルの圧縮を行い、実行結果をイベントログに記録
if flgExsitLog Then
' cabと圧縮対象のフルパス作成、存在チェックに使う
strCompFileFullName = strWLogfilepath & "\" & strCompFileName
strCompTargetFileFull = strWLogfilepath & "\" & strCompTargetFile

' 対象のIISログファイルをCABに圧縮
strRuncmd = strComspec & " /C" & chr(34) & "start /B /BELOWNORMAL cabarc.exe -p -r N " & chr(34) & _
strCompFileFullName & chr(34)& _
" " & chr(34) & strCompTargetFileFull & chr(34) & chr(34)
strResults = RunCaptured(strRuncmd)

' cabファイルがちゃんとできたか確認、あれば元ファイル削除する。
if objfso.fileexists(strCompFileFullName) then
' cabarcの実行結果を 成功 でイベントログに記述
objWshShell.LogEvent 0, strResults
' 元ファイル削除 結果の取得のためsオプションつきなので注意
strRuncmd = strComspec & " /C " & chr(34) & "del /s " _
& chr(34) & strCompTargetFileFull & chr(34) & chr(34)
strResults = RunCaptured(strRuncmd)
' 削除の実行結果を 情報 でイベントログに記述
objWshShell.LogEvent 4, strResults
Else
' cabarcの実行結果を 警告 でイベントログに記述、元ファイルもそのまま
objWshShell.LogEvent 2, strResults
End if

'exYYMM*.logがない場合、なにもしない
End if

' フォルダの存在チェックのEnd
End if
' IISWebServerクラスのEnd
End if
Wscript.sleep(100)
Next


'// FTP、MSFTPSVC
for each obj in objFTPsvc
' IIsFtpServerのクラスを取得
if obj.Class = "IIsFtpServer" then
' ログフォルダのパス作成
strWLogFoderName = "MSFTPSVC" + obj.Name
strWLogfilepath = obj.LogFileDirectory + "\" + strWLogFoderName

' フォルダの存在チェックして、あればフォルダオブジェクト作成
if objfso.folderexists(strWLogfilepath) then
Set objFolder = objFSO.GetFolder(strWLogfilepath)
'ファイルの存在フラグリセット
flgExsitLog = false

'指定されたexYYMM*.logの存在チェック
For each strFileNames in objFolder.Files

flgCheckFileA = strComp(Left(strCompTargetFile,6),Left(objFSO.GetBaseName(strFileNames),6),1)
flgCheckFileB = strComp("log",objFSO.GetExtensionName(strFileNames),1)
' ファイル名と拡張子で条件一致しているか判断
if flgCheckFileA = 0 and flgCheckFileB = 0 Then
'ファイルの存在フラグをセットしてさっさと抜ける
flgExsitLog = true
Exit For
End if
Next

'exYYMM*.logが存在していたら、ログファイルの圧縮を行い、実行結果をイベントログに記録
if flgExsitLog Then
' cabと圧縮対象のフルパス作成、存在チェックに使う
strCompFileFullName = strWLogfilepath & "\" & strCompFileName
strCompTargetFileFull = strWLogfilepath & "\" & strCompTargetFile

' 対象のIISログファイルをCABに圧縮
strRuncmd = "cabarc.exe -p -r N " & chr(34) & _
strCompFileFullName & chr(34)& _
" " & chr(34) & strCompTargetFileFull & chr(34)
strResults = RunCaptured(strRuncmd)

' cabファイルがちゃんとできたか確認、あれば元ファイル削除する。
if objfso.fileexists(strCompFileFullName) then
' cabarcの実行結果を 成功 でイベントログに記述
objWshShell.LogEvent 0, strResults
' 元ファイル削除 結果の取得のためsオプションつきなので注意
strRuncmd = strComspec & " /C " & chr(34) & "del /s " _
& chr(34) & strCompTargetFileFull & chr(34) & chr(34)
strResults = RunCaptured(strRuncmd)
' 削除の実行結果を 情報 でイベントログに記述
objWshShell.LogEvent 4, strResults
Else
' cabarcの実行結果を 警告 でイベントログに記述、元ファイルもそのまま
objWshShell.LogEvent 2, strResults
End if

'exYYMM*.logがない場合、なにもしない
End if

' フォルダの存在チェックのEnd
End if
' IISWebServerクラスのEnd
End if
Wscript.sleep(100)
Next



'// 終了をイベントログに書き込み
objWshShell.LogEvent 4, "Tsk_IISLogCompress.vbs の 終了"


Wscript.Quit(0)


Function RunCaptured(strCmdLine)
' **********************************************************
' 関数 RunCaptured(strCmdLine)
' 説明: コマンドを実行し、出力を取得します
' この関数は stdout.err とコマンドが標準出力に対して発行したフル テキストを返します
' 実行するスクリプトが生成するすべての行も表示し、bReportErrors = False を関数に渡します
' 入力: strCmdLine .... 実行するコマンド ライン
' 省略した→bReportErrors .... すべての出力を返すかどうかを決定するブール値
'
' 出力: コマンド ラインの出力と出力の最後の行を含む配列
' **********************************************************
Dim strTemp
' コマンド ラインが渡されない場合は
' 完全に失敗します
If strCmdLine = "" Then
WScript.Quit(1)
End If
' Exec Object を作成します
Set oExec = objWshShell.Exec(strCmdLine)
' 実行されたコマンド ラインが停止するまでループします
Do while Not oExec.StdOut.AtEndOfStream
' 標準出力から行を読み込みます
strLineIn = oExec.StdOut.ReadLine

' 一時的な文字列を作成します
If strTemp = "" Then
strTemp = strLineIn
Else
strTemp = strTemp & vbCRLF & strLineIn
End If
' 出力を配列に返します

RunCaptured = strTemp
' RunCaptured = Array(oExec.StdErr.ReadAll, strTemp)
Loop
End Function

[/sourcecode]
 
 

実行結果はイベントログに色々と書かれます。
コメントが多いのはまず日本語だけでプログラムを作って、その間にスクリプトをもぐりこませる書き方をしているから。頭の中だけで考えるのは苦手なので。
 








アサマシ