2015年12月28日月曜日

EGPからログを抽出するVBScript

EGPからログを抽出するスクリプトを作りました。EGPをZIPのファイルにコピーして、そのなかからresult.logのファイルを特定し、1つのファイルに出力しています。この方法は、公式ではなくNon Supportです。EGPのファイルが壊れたときに救済手段として、どこかに紹介されていました。
DebugFlag = Trueだと、メッセージのダイアログを表示します。あと、ログファイルの並び順は意識していません。

  1. '---  
  2. ' プログラム: EGPのファイルからSASログを抽出します。  
  3. '       説明: EGPの拡張子をZIPに変更して、ZIPのフォルダからresult.logのファイルを抽出  
  4. '     作成者: mining  
  5. '  
  6. '      引数1: EGPのファイルパス  
  7. '      引数2: ログファイルのパス  
  8. '     実行例: cscript foo.vbs C:\temp\foo.egp C:\temp\foo.log  
  9. '  
  10. '---  
  11.   
  12. Option Explicit  
  13. On Error Resume Next  
  14.   
  15. '---  
  16. ' 定数  
  17. '---  
  18.   
  19. Const FOF_SILENT = &H4              ' 進捗ダイアログを表示しない  
  20. Const FOF_NOCONFIRMATION = &H10     ' 上書き確認ダイアログを表示しない  
  21. Const ForWriting = 2                ' テキストファイルのオープン  
  22. Const ForReading = 1                ' テキストファイルのオープン  
  23. Const TristateUseDefault = -2       ' Opens the file using the system default.  
  24. Const TristateTrue = -1             ' Opens the file as Unicode.  
  25. Const TristateFalse = 0             ' Opens the file as ASCII.  
  26. Const DebugFlag = True  
  27.   
  28. '---  
  29. ' 変数  
  30. '---  
  31.   
  32. Dim objShell  
  33. Dim objFso  
  34. Dim objTs  
  35. Dim sZipFile  
  36. Dim sTempFolder  
  37. Dim sEgpFilePath  
  38. Dim sLogFilePath  
  39. Dim ErrCount  
  40. Dim WarCount  
  41. Dim sMsg  
  42.   
  43. '---  
  44. ' オブジェクト生成します。  
  45. '---  
  46.   
  47. Set objShell = CreateObject("Shell.Application")  
  48. Set objFso = CreateObject("Scripting.FileSystemObject")  
  49.   
  50. '---  
  51. ' 引数をチェックします。  
  52. '--  
  53.   
  54. If WScript.Arguments.Count <> 2 Then  
  55.     Call MsgBox("引数1にEGP、引数2にログファイルを指定してください。", vbOKOnly + vbExclamation, WScript.ScriptName)  
  56.     WScript.Quit(1)  
  57. End If  
  58.   
  59. sEgpFilePath = WScript.Arguments.Item(0)  
  60. sLogFilePath = WScript.Arguments.Item(1)  
  61.   
  62. If objFso.FileExists(sEgpFilePath) = False Then  
  63.     Call MsgBox("引数1で指定したファイルが存在しません。", vbOKOnly + vbExclamation, WScript.ScriptName)  
  64.     WScript.Quit(1)  
  65. End If  
  66.   
  67. '---  
  68. '   デバッグ用のメッセージ出力  
  69. '---  
  70.   
  71. Sub DebugMsg(msg)  
  72.     If DebugFlag = True Then  
  73.         Call MsgBox(msg, vbOKOnly + vbInformation, WScript.ScriptName)  
  74.     End If  
  75. End Sub  
  76.   
  77.   
  78. '---  
  79. '   ZIPファイルを指定したフォルダに解凍  
  80. '---  
  81.   
  82. Sub Unzip(objShell, sFile, sFolder)  
  83.     Dim objFilesInZip  
  84.     Dim objFolder  
  85.       
  86.     Set objFilesInZip = objShell.Namespace(sFile).Items  
  87.     If Err.Number <> 0 Then  
  88.         Exit Sub  
  89.     End If  
  90.     Set objFolder = objShell.Namespace(sFolder)  
  91.     If Err.Number <> 0 Then  
  92.         Exit Sub  
  93.     End If  
  94.       
  95.     If (Not objFolder Is Nothing) Then  
  96.         objFolder.CopyHere objFilesInZip, FOF_NOCONFIRMATION + FOF_SILENT  
  97.     Else  
  98.  Err.Raise 432 ' オートメーションの操作中にファイル名またはクラス名を見つけられませんでした。  
  99.     End If  
  100.   
  101.    Set objFilesInZip = Nothing  
  102.    Set objFolder = Nothing  
  103. End Sub  
  104.   
  105. '---  
  106. '   フォルダを作成  
  107. '---  
  108.   
  109. Sub CreateUnzipFolder(objFso, sFolder)  
  110.     objFso.CreateFolder sFolder  
  111. End Sub  
  112.   
  113. '---  
  114. '   フォルダを削除  
  115. '---  
  116.   
  117. Sub DeleteUnzipFolder(objFso, sFolder)  
  118.     If objFso.FolderExists(sFolder) = True Then  
  119.         objFso.DeleteFolder sFolder, True  
  120.     End If  
  121. End Sub  
  122.   
  123. '---  
  124. '   テンポラリのフォルダのパスを作成  
  125. '---  
  126.   
  127. Function CreateFolderPath(objFso, sFolder)  
  128.     Const TemporaryFolder = 2  
  129.     Dim objTempFolder  
  130.       
  131.     Set objTempFolder = objFso.GetSpecialFolder(TemporaryFolder)  
  132.     CreateFolderPath = objFso.BuildPath(objTempFolder.Path, sFolder)  
  133.     Set objTempFolder = Nothing  
  134. End Function  
  135.   
  136. '---  
  137. '   サブフォルダからresult.logを探して、objTsに出力  
  138. '---  
  139.   
  140. Sub SearchLog(objFso, objTs, tmpFolderItems)  
  141.     Const FileName = "result.log"  
  142.     Dim objFolderItemsB  
  143.     Dim objItem  
  144.     Dim Stream  
  145.       
  146.     For Each objItem in tmpFolderItems  
  147.       
  148.         ' 取り出した物がファイルかフォルダかを判定  
  149.         If objItem.IsFolder Then  
  150.             ' フォルダであれば、再帰呼び出しでフォルダ階層を手繰ります。  
  151.             Set objFolderItemsB = objItem.GetFolder  
  152.             Call SearchLog(objFso, objTs, objFolderItemsB.Items())  
  153.         ElseIf objItem.Name = FileName Then  
  154.             ' ファイル名が一致したら、テキストを読み取りobjTSに出力します。  
  155.             Set Stream = CreateObject("ADODB.Stream")  
  156.             Stream.Charset = "UTF-8"  
  157.             Stream.Type = 2  
  158.             Stream.Open  
  159.             Stream.LoadFromFile(objItem.Path)  
  160.             objTs.Write(Stream.ReadText)  
  161.             Stream.Close  
  162.             Set Stream = Nothing  
  163.         End If  
  164.       
  165.     Next  
  166.       
  167.     Set objItem = Nothing  
  168.     Set objFolderItemsB = Nothing  
  169.   
  170. End Sub  
  171.   
  172. '---  
  173. '   ログファイルからERROR, WARNINGの件数をカウント  
  174. '---  
  175.   
  176. Sub CountLog(objFso, sLogFile, byRef ErrCount, byRef WarCount)  
  177.     Const KeyError = "e ERROR"  
  178.     Const KeyWarning = "w WARNING"  
  179.     Dim objTs  
  180.     Dim sBuf  
  181.   
  182.     On Error Goto 0  
  183.   
  184.     ErrCount = 0  
  185.     WarCount = 0  
  186.   
  187.     Set objTs = objFso.OpenTextFile(sLogFile, ForReading, False, TristateTrue)  
  188.     If Err.Number <> 0 Then  
  189.         Exit Sub  
  190.     End If  
  191.   
  192.     Do Until objTs.AtEndOfLine = True  
  193.         sBuf = objTs.ReadLine  
  194.         If Left(sBuf, Len(KeyError)) = KeyError Then  
  195.             ErrCount = ErrCount + 1  
  196.         ElseIf Left(sBuf, Len(KeyWarning)) = KeyWarning Then  
  197.             WarCount = WarCount + 1  
  198.         End If  
  199.     Loop  
  200.   
  201.     objTs.Close  
  202.     If Err.Number <> 0 Then  
  203.         Exit Sub  
  204.     End If  
  205.   
  206.     Set objTs = Nothing  
  207.   
  208. End Sub  
  209.   
  210. '---  
  211. ' エラーチェック  
  212. '---  
  213.   
  214. Function CheckError(fnName)  
  215.     Checkerror = False  
  216.       
  217.     Dim strmsg  
  218.     Dim errNum  
  219.       
  220.     If Err.Number <> 0 Then  
  221.         strmsg = "Error #" & Hex(Err.Number) & vbCrLf & "In Function " & fnName & vbCrLf & Err.Description  
  222.         Call MsgBox(strmsg, vbOkOnly + vbCritical, WScript.ScriptName)  
  223.         Checkerror = True  
  224.     End If  
  225.            
  226. End Function  
  227.   
  228. '---  
  229. ' ログファイルを開きます。  
  230. '---  
  231.   
  232. Set objTs = objFso.CreateTextFile(sLogFilePath, True, True)  
  233. If CheckError("objFso.CreateTextFile") Then  
  234.     WScript.Quit(1)  
  235. End If  
  236.   
  237. '---  
  238. ' EGPの拡張子をZIPに変更してコピーします。  
  239. '---  
  240.   
  241. sZipFile = CreateFolderPath(objFso, objFso.GetTempName & ".zip")  
  242. Call DebugMsg("EGPの拡張子をZIPに変えてコピー:" & sZipFile)  
  243. objFso.CopyFile sEgpFilePath, sZipFile  
  244. If CheckError("objFso.CopyFile") Then  
  245.     WScript.Quit(1)  
  246. End If  
  247.   
  248. '---  
  249. ' 解凍先のテンポラリのフォルダを作成します。  
  250. '---  
  251.   
  252. sTempFolder = CreateFolderPath(objFso, objFso.GetTempName)  
  253. Call DebugMsg("テンポラリのフォルダを作成:" & sTempFolder)  
  254. Call CreateUnzipFolder(objFso, sTempFolder)  
  255. If CheckError("CreateUnzipFolder") Then  
  256.     WScript.Quit(1)  
  257. End If  
  258.   
  259.   
  260. '---  
  261. ' ZIPファイルを解凍します。  
  262. '---  
  263.   
  264. Call DebugMsg("ZIPファイルを解凍:" & sZipFile)  
  265. Call Unzip(objShell, sZipFile, sTempFolder)  
  266. If CheckError("Unzip") Then  
  267.     WScript.Quit(1)  
  268. End If  
  269.   
  270.   
  271. '---  
  272. ' テンポラリフォルダからログファイル探してobjTSに出力します。  
  273. '---  
  274.   
  275. Call DebugMsg("テンポラリのフォルダからログを収集:" & sTempFolder)  
  276. Call SearchLog(objFso, objTs, (objShell.NameSpace(sTempFolder)).Items)  
  277. If CheckError("SearchLog") Then  
  278.     WScript.Quit(1)  
  279. End If  
  280.   
  281. '---  
  282. ' ログファイルを閉じます。  
  283. '---  
  284.   
  285. objTs.Close  
  286. If CheckError("objTs.Close") Then  
  287.     WScript.Quit(1)  
  288. End If  
  289.   
  290. '---  
  291. ' テンポラリのフォルダを削除します。  
  292. '---  
  293.   
  294. Call DebugMsg("テンポラリのフォルダを削除:" & sTempFolder)  
  295. Call DeleteUnzipFolder(objFso, sTempFolder)  
  296. If CheckError("DeleteUnzipFolder") Then  
  297.     WScript.Quit(1)  
  298. End If  
  299.   
  300. '---  
  301. ' ZIPファイルを削除します。  
  302. '---  
  303.   
  304. Call DebugMsg("ZIPファイルを削除:" & sZipFile)  
  305. objFso.DeleteFile sZipFile  
  306. If CheckError("objFso.DeleteFile") Then  
  307.     WScript.Quit(1)  
  308. End If  
  309.   
  310. '---  
  311. ' Error, Warningの件数を数えます。  
  312. '---  
  313.   
  314. Call CountLog(objFso, sLogFilePath, ErrCount, WarCount)  
  315. If CheckError("objFso.DeleteFile") Then  
  316.     WScript.Quit(1)  
  317. End If  
  318. Call DebugMsg("ERROR件数:" & CStr(ErrCount) & " WARNING件数:" & CStr(WarCount))  
  319.   
  320. '---  
  321. ' オブジェクトを破棄します。  
  322. '---  
  323.   
  324. Set objTs = Nothing  
  325. Set objFso = Nothing  
  326. Set objShell = Nothing  
  327.   
  328. '---  
  329. ' 終了  
  330. '---  
  331.   
  332. WScript.Quit(0)  

2015年12月26日土曜日

転職、3ヶ月が経過

忙しさを理由に2015年9月に転職しました。
3ヶ月経過しましたが、思うところを書き留めておきます。

旅費清算、出張手配、捺印申請とかはやってみないとわからない。
イントラに書いてあるとおりやっても、大体うまくいきません。書かれていないルールに対応するのは慣れしかないと諦めます。

査定分の収入がしばらく入らず減収です。給与はベースとインセンティブの部分で分かれていますが、評価を受けるまではベースのみです。来年4月までは評価部分がゼロです。

3ヶ月間はNon Billableということで、順応期間でした。が、5週目から客先に単独で放り込まれましたが、なんとかなっています。亀の甲より、年の功。持っているスキルがお客様に合ってよかったです。

記録まで

EGPからログを取り出す方法

SAS Enterprise Guide Projectファイルからログを取り出す方法を調べました。調べてみるといくつか技術的な課題あり、てこずったのでここに記しておきます。EGのバージョンは7.1です。さて、その方法ですが、4種類みつけました。

  1. OLE AutomationでVBScriptやPowerShellから取り出す
  2. EGのツール、オプションでカスタムコードを書いてログの出力先を変える
  3. EGPの拡張子をZIPに変えて、フォルダの中からログを取り出す
  4. EGのツール、オプション、Application Loggingでログを出力する


OLE AutomationからVBSやPowerShellでログを取り出す

この方法は、スケジューリングで出力されるVBSのファイルを開いて、手を加えればできます。しかしながら、クエリ、コードタスク以外のログが取れませんでした。ProjectItemsの中をループで探りましたがランクや転置などデータ加工のタスク、グラフ作成のタスクが捕捉できませんでした。何故かは分かっていませんが、そういう実装なのだと割り切って考えています。

EGのツール、オプションでカスタムコードを書いてログの出力先を変える

proc printtoでログの出力先を変えます。この方法が使えるかと思いきや、クライアント/サーバ構成だとログがサーバ上に出力されて、クライアントPCから参照するためにもう一工夫必要になりあます。サーバ側でログの加工をするのであれば、問題ありません。カスタムのコードを設定する箇所が複数ありますが、使い分けは調べてください。

EGPの拡張子をZIPに変えて、フォルダの中からログを取り出す

元ネタはココの記事です。試してみると、サブフォルダのなかにresult.logがありますので、これをピックアップすればログを抽出できます。サブフォルダの中を見ると、削除されたと思しきタスクの殻フォルダがいくつかあります。ゴミがあるやも知れないので、この方法を使うときには検証をしてください。Non Supportです。

Tools、Options、Application Loggingでログを出力する

元ネタはココです。カスタムコードでproc printtoと似ていますが、サーバが出力するコード以外のログが含まれます。


EGPからログを取り出すときの検討項目を挙げます。

  • プロジェクトファイル名を取得するか否か
  • ログは時系列で取り出す必要があるか
  • SASログ以外のログが含まれても良いか
  • ログの出力先はローカルのPC、又はサーバ上?
  • 抽出するファイルは1本にまとめるか、複数バラバラで良いか?

参考まで

2015年9月11日金曜日

SAS Log Inspector 1.3.1.1 リリース


機能は変えず、組み込みのマッチングパターンを23種類に増やしました。どんなパターンが定義されているかは、以下の画像をご覧ください。久々の更新で、Vectorに登録申請しました。



SAS Log Inspectorの機能

  • 複数のログファイルからエラー、バグに繋がるNOTEを集計
  • 日本語と英語版のメッセージに対応
  • Excelファイルにログをチェックした結果をエクスポート
  • 任意のチェック項目を検査対象から外すことができる
  • Notepadを起動して、該当のログファイルを参照できる
  • マッチングパターンを画面から追加、修正できる

2015年9月8日火曜日

転職

ひどく忙しい期間が続き、仕事の方向性が発散してきたので、転職しました。
これからも、SASに絡んだ仕事を続けます。
滞っているSAS Logのツールも新しくししたいです。