ExcelVBAログ出力

# ExcelVBAでログ出力

## ExcelVBAでアプリケーション開発時にエラーの内容を見たい

Excelで簡単なアプリケーションを作る中で、
処理のログを通常のアプリケーションログの様に出力したいなと思い、
試行錯誤試してみました。
作成したExcel VBAでエラーが起きた時にどのようなエラーがどこで起きたのか
把握するために作成してみました。

公開中：[GitHub](https://github.com/Rin-bamboo/VBALogOutput "GitHub")

### 求める機能
* 日時（フォーマット指定迄）
* ログレベル
* ログが出力されている関数名
* ログ内容（エラーメッセージ）

アプリケーションログと似た形にしていけば、エラーの特定が容易になる。

### ログは、テキストファイルへの出力
ログが、Excel内のシートに出力されてしまっては
無駄なシートを作成する必要が出てきてしまう。

その為、指定のパスへテキストファイルとして出力を行いたい

パス指定も、簡単に変更できるようにしなければならない
これもまた、Excel内のセルにパスを入力するようにすると
設定シートなるものを作成したりしなければならないので、
configファイルを作成するようにしてみる

これなら、設定を勝手にいじったり消されたりすることはなくなる
（ファイルを消されたら元も子もないが・・・）

### 設定ファイルの作成
XML形式のファイルで設定を作成していく。
Microsoft XML, v6.0を使用してXMLの取込解析を行います
``` xml:config.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <config>
        <logfile>C:\home\log\log.txt</logfile>
    </config>
```

上記ファイルの設定を読み込む専用Classを作成

クラスモジュール
```VB:ConfigReader
Option Explicit

Private xmlDoc As Object


Public Sub LoadConfig(ByVal configFilePath As String)
    Set xmlDoc = CreateObject("MSXML2.DOMDocument.6.0")
    xmlDoc.async = False
    xmlDoc.Load configFilePath
    
    If xmlDoc.parseError.ErrorCode <> 0 Then
        Err.Raise xmlDoc.parseError.ErrorCode, , xmlDoc.parseError.reason
    End If

End Sub

Public Function GetConfigValue(ByVal key As String) As String

    On Error GoTo notvalue
    
    GetConfigValue = xmlDoc.SelectSingleNode("/config/" & key).Text
    
    Exit Function
    
notvalue:
    '値がなかったら空白を返却
    GetConfigValue = ""

    
End Function


```
上記でコンフィグの設定を取得することができる。
今回の場合は、

```VB:vbaconfigreader

	Private ConfigReader As ConfigReader
    'コンフィグファイル取得
    Set ConfigReader = New ConfigReader
    'ログファイル出力 初期値：同じファイル階層
    LogOutputPath = ConfigReader.GetConfigValue("logfile")

```

これで、ログ出力先のファイルパスを指定することができる。

### ログをファイルに書き込む処理
ログをファイルに書き込む際、クラスでファイルを保持すると他の関数から呼び出しいたときに
書き込めない・何ならファイルを掴めずにエラーとなることがある。
というか、エラーになる。

そのため、ログ書き込み時にその都度ファイルを掴んで書き込みを行うようにする

``` vb:logoutput
Private Sub LogOutput(logLevelName As String, LogLevel As Integer, logMessage As String)

        Call File_Open
        Print #fileNo, Format(Now(), DateFormat) & " [" & logLevelName & "]" & Space(spaceCnt) & "[" & CallMethodName & "] " & logMessage
        Call File_Close

End Sub

Private Sub File_Open()
    fileNo = FreeFile
    Open LogOutputPath For Append As #fileNo
End Sub

Private Sub File_Close()
    Close #fileNo
End Sub

```

各ログ出力の関数(DEBUG/INFO/ALEART/ERROR)を呼び出すと
ファイルを開く・書き込む・閉じるという流れを踏むようにする。
静的クラスにすれば行けるのかもしれないが、Excelというファイルを使っている以上
複数の場所から同じ場所に書き込みが起きないとも限らないので、
今回は、書き込みのたびに開く・書き込む・閉じるという流れを採用した。


### ログ出力
ログ出力する際に、
```LogOutput("ログレベル","ログ内容",....```
という関数にするかも迷ったが、ログレベルはすべて同じように出力するようにしたかったため、
それぞれのログレベルで関数を作成し呼び出すようにした

```vb:logLevel

Public Sub DebugMsg(logMessage As String, methodName As String)
    CallMethodName = methodName
    'DEBUGログ出力
    Call LogOutput("DEGUB", LOGLEVEL_DEBUG, logMessage)
End Sub

Public Sub InfoMsg(logMessage As String, methodName As String)
    CallMethodName = methodName
    'INFOログ出力
    Call LogOutput("INFO", LOGLEVEL_INFO, logMessage)
End Sub

Public Sub AleartMsg(logMessage As String, methodName As String)
    CallMethodName = methodName
    'WARNINGログ出力
    Call LogOutput("ALEART", LOGLEVEL_ALEART, logMessage)
End Sub

Public Sub ErrorMsg(logMessage As String, methodName As String)
    CallMethodName = methodName
    'ERRORログ出力
    Call LogOutput("ERROR", LOGLEVEL_ERROR, logMessage)
End Sub

```
この方法をとることで、INFOがinfoになったり、Infoになったりとバラツキが出ないようにした。
同じになるというのはとても大事なことであると考えています。

必要に応じてログを出力するが、残念ながら行番号までは表示ができない…
VBA自体行番号を持っていないので、出力することすらできない…

VBAでまるで通常のアプリケーションのようなログ出力ができるようになった。
```2024/06/18 11:30:00 [INFO] [メソッド名] ログメッセージ```
使う機会は少ないかもしれないが、少し複雑なVBAを作成した時は何かしらバグが出ないとも限らない
その時に再度再現するための調査をするのは結構大変なのでログはとても重要な情報となる。


また気になる機能があったら都度追加したりしていきたいと思います。

公開中：[GitHub](https://github.com/Rin-bamboo/VBALogOutput "GitHub")