Excel VBA 逆引き集 | ログ設計高度版

Excel VBA
スポンサーリンク
  1. ねらい:Excelで「壊れない・追跡できる」高度ログ設計を標準化する
  2. ログ設計の原則とスキーマ(固定カラムで“読める・機械が読める”)
    1. スキーマ(CSV行の固定カラム)
    2. 重要ポイントの深掘り
  3. 高速・安全なロガーの芯(バッファリング、非同期フラッシュ、ローテーション)
    1. バッファリング+OnTimeで非同期フラッシュ(貼って動く)
    2. 重要ポイントの深掘り
  4. 相関ID・スコープ付きロガー(開始/終了を自動記録)
    1. スコープロガーで“処理の入口/出口と所要時間”を自動化
    2. 重要ポイントの深掘り
  5. JSONログ併用とメタデータ拡張(機械連携向け)
    1. JSON行形式の追加(必要に応じて二重出力)
    2. 重要ポイントの深掘り
  6. エラー記録の“粒度”設計と例外ラッパー
    1. Tryラッパーで例外→ERRORログ→再スロー(運用しやすい形)
    2. 重要ポイントの深掘り
  7. 回転・アーカイブ・保持ポリシー(肥大化を止める)
    1. 週次アーカイブ例(ZIPにまとめて古いものを整理)
    2. 重要ポイントの深掘り
  8. ログビューワとアラート(赤が一目、件数サマリで傾向)
    1. シートビュー(色分け&集計)
    2. 重要ポイントの深掘り
  9. 例題の通し方:ETLの各工程でログを出し、相関IDで追跡
    1. 入口→工程→出口のログ例
    2. 重要ポイントの深掘り
  10. 落とし穴と対策(深掘り)
    1. 逐次書きで遅い・固まる
    2. 相関IDがないため追跡できない
    3. タイムスタンプがバラバラ(ローカル時刻)
    4. ログが肥大・消えない
    5. CSVのカンマや改行で壊れる
  11. まとめ:固定スキーマ+相関ID+バッファリングで“追跡できる速いログ”にする

ねらい:Excelで「壊れない・追跡できる」高度ログ設計を標準化する

ログは“後から真実を再現するための証拠”です。大規模や自動化の現場では、単なるテキスト出力では足りません。レベル、タイムスタンプ、カテゴリ、相関ID、コンテキスト(誰が・何を・どこで)を固定フォーマットにし、バッファリングで高速化、ローテーションで肥大化防止、CSV/JSON二系統で運用と機械読取を両立させます。初心者向けに貼って動くテンプレートを、例題とともにかみ砕いて解説します。


ログ設計の原則とスキーマ(固定カラムで“読める・機械が読める”)

スキーマ(CSV行の固定カラム)

  • タイムスタンプ(UTC)
  • レベル(INFO/WARN/ERROR/DEBUG)
  • 相関ID(処理単位のUUID)
  • カテゴリ(処理の区分:Import/Validate/Transform/Exportなど)
  • メッセージ(人間向け)
  • コンテキスト(Key=Valueのペア連結)

この固定カラムで書けば、後からフィルタ・集計・結合が容易です。相関IDがあると、1件の処理に紐づく全ログを一発抽出できます。

重要ポイントの深掘り

  • タイムスタンプは“UTC”で統一し、時差を排除します。画面表示は後からローカル時刻に変換。
  • 相関IDは1処理の“糸”になります。ETLやAPI的な処理は必ず紐づけて出力。
  • コンテキストは“後から必要になる断片(ファイル名、件数、キー)”を揃えます。冗長に見えても、解析では命綱です。

高速・安全なロガーの芯(バッファリング、非同期フラッシュ、ローテーション)

バッファリング+OnTimeで非同期フラッシュ(貼って動く)

' ModLog.bas(CSVロガー:バッファ→定期フラッシュ→日別ローテーション)
Option Explicit

Private gBuf() As String
Private gW As Long
Private gNextFlush As Date
Private gCorId As String

Public Sub LogInit(Optional ByVal corId As String = "")
    ReDim gBuf(1 To 1000)
    gW = 0
    gCorId = IIf(Len(corId) > 0, corId, NewCorrelationId())
    ScheduleFlush 1 ' 1秒後
End Sub

Public Sub LogSetCorrelation(ByVal corId As String)
    gCorId = corId
End Sub

Public Sub LogInfo(ByVal category As String, ByVal msg As String, Optional ByVal ctx As String = "")
    Enqueue "INFO", category, msg, ctx
End Sub
Public Sub LogWarn(ByVal category As String, ByVal msg As String, Optional ByVal ctx As String = "")
    Enqueue "WARN", category, msg, ctx
End Sub
Public Sub LogError(ByVal category As String, ByVal msg As String, Optional ByVal ctx As String = "")
    Enqueue "ERROR", category, msg, ctx
End Sub
Public Sub LogDebug(ByVal category As String, ByVal msg As String, Optional ByVal ctx As String = "")
    ' 運用フラグで出力制御も可(例:環境変数やConfig)
    Enqueue "DEBUG", category, msg, ctx
End Sub

Private Sub Enqueue(ByVal level As String, ByVal category As String, ByVal msg As String, ByVal ctx As String)
    On Error Resume Next
    gW = gW + 1
    If gW > UBound(gBuf) Then ReDim Preserve gBuf(1 To gW + 1000)
    gBuf(gW) = MakeCsvLine UtcNow(), level, gCorId, category, msg, ctx
    On Error GoTo 0
End Sub

Public Sub LogFlushNow()
    On Error GoTo EH
    If gW = 0 Then Exit Sub
    Dim path As String: path = EnsureLogPath()
    Dim file As String: file = path & "\" & Format(Date, "yyyy-mm-dd") & ".csv"
    Dim h As Integer: h = FreeFile
    Open file For Append As #h
    Dim i As Long
    For i = 1 To gW
        Print #h, gBuf(i)
    Next
    Close #h
    gW = 0
    Exit Sub
EH:
    ' 失敗時は、即座にシートログへフォールバック(最後の砦)
    SheetFallbackLogs gBuf, gW
End Sub

Public Sub LogShutdown()
    LogFlushNow
    gCorId = ""
End Sub

Private Sub ScheduleFlush(ByVal seconds As Double)
    gNextFlush = Now + seconds / 86400#
    Application.OnTime gNextFlush, "'" & ThisWorkbook.Name & "'!LogFlushTick", , True
End Sub

Public Sub LogFlushTick()
    LogFlushNow
    ' 処理継続中のみ再スケジュール(必要なら管理フラグで制御)
    ScheduleFlush 2 ' 2秒ごと
End Sub

Private Function EnsureLogPath() As String
    Dim p As String: p = ThisWorkbook.Path & "\logs"
    If Dir(p, vbDirectory) = "" Then MkDir p
    EnsureLogPath = p
End Function

Private Function MakeCsvLine(ByVal tsUtc As String, ByVal level As String, ByVal corId As String, ByVal category As String, ByVal msg As String, ByVal ctx As String) As String
    MakeCsvLine = _
        SafeCsv(tsUtc) & "," & SafeCsv(level) & "," & SafeCsv(corId) & "," & SafeCsv(category) & "," & SafeCsv(msg) & "," & SafeCsv(ctx)
End Function

Private Function SafeCsv(ByVal s As String) As String
    Dim t As String: t = Replace(Replace(s, """", "'"), vbCrLf, " ")
    SafeCsv = """" & Replace(t, ",", " ") & """"
End Function

Private Function UtcNow() As String
    ' Excelはタイムゾーン非対応:UTC表現はNow-オフセットでも良いが、ここはISO8601風の文字列で固定
    UtcNow = Format(Now, "yyyy-mm-dd HH:NN:SS")
End Function

Private Sub SheetFallbackLogs(ByRef buf() As String, ByVal n As Long)
    Dim ws As Worksheet: Set ws = PrepareOut("Logs_Fallback")
    ws.Range("A1").Value = "CSVLine"
    Dim i As Long
    For i = 1 To n
        ws.Cells(i + 1, "A").Value = buf(i)
    Next
End Sub

Private Function PrepareOut(ByVal name As String) As Worksheet
    Dim ws As Worksheet
    On Error Resume Next: Set ws = Worksheets(name): On Error GoTo 0
    If ws Is Nothing Then Set ws = Worksheets.Add: ws.Name = name
    ws.Cells.Clear
    Set PrepareOut = ws
End Function
VB

重要ポイントの深掘り

  • 逐次Writeは遅いので“メモリに貯めてまとめて書く”。OnTimeの非同期フラッシュでUIを固めません。
  • 日ごとにファイルを分けるローテーションで肥大化防止。週次/月次アーカイブは運用ルール化。
  • 失敗時のフォールバック(シートへ退避)を最後の砦として用意し、絶対に失われない設計にします。

相関ID・スコープ付きロガー(開始/終了を自動記録)

スコープロガーで“処理の入口/出口と所要時間”を自動化

' ModLogScope.bas
Option Explicit
Private Type ScopeInfo
    Category As String
    StartTs As String
    CorId As String
End Type
Private gScopes As Collection

Public Function BeginScope(ByVal category As String, Optional ByVal corId As String = "") As ScopeInfo
    Dim s As ScopeInfo
    s.Category = category
    s.StartTs = UtcNow()
    s.CorId = IIf(Len(corId) > 0, corId, NewCorrelationId())
    LogSetCorrelation s.CorId
    LogInfo category, "BEGIN", "corId=" & s.CorId
    BeginScope = s
End Function

Public Sub EndScope(ByRef s As ScopeInfo, Optional ByVal ok As Boolean = True)
    Dim dur As Double: dur = Timer - CDate(s.StartTs) ' 簡易:文字→Date→Timerは誤差あり。厳密化は別途。
    Dim msg As String: msg = IIf(ok, "END OK", "END FAIL")
    LogSetCorrelation s.CorId
    LogInfo s.Category, msg, "durSec=" & Format(dur, "0.000")
    LogSetCorrelation "" ' 明示解除
End Sub
VB
' ModCorrelation.bas(UUID生成の簡易版)
Option Explicit

Public Function NewCorrelationId() As String
    NewCorrelationId = Replace(Replace(CreateObject("Scriptlet.TypeLib").GUID, "{", ""), "}", "")
End Function
VB

重要ポイントの深掘り

  • “BEGIN/END+所要時間”は障害解析の核です。どこで詰まったかが瞬時に分かります。
  • 相関IDはスコープ開始時に必ず新規発行し、スコープ内のログへ一貫して付けます。並列処理でも追跡が容易になります。

JSONログ併用とメタデータ拡張(機械連携向け)

JSON行形式の追加(必要に応じて二重出力)

' ModLogJson.bas
Option Explicit

Public Sub LogJson(ByVal level As String, ByVal category As String, ByVal msg As String, ByVal ctx As String, ByVal corId As String)
    Dim line As String
    line = "{""ts"":""" & UtcNow() & """,""lvl"":""" & level & """,""cor"":""" & corId & """,""cat"":""" & category & """,""msg"":""" & JEsc(msg) & """,""ctx"":""" & JEsc(ctx) & """}"
    EnqueueJson line
End Sub

Private Sub EnqueueJson(ByVal line As String)
    Dim path As String: path = EnsureLogPath()
    Dim file As String: file = path & "\" & Format(Date, "yyyy-mm-dd") & ".jsonl"
    Dim h As Integer: h = FreeFile
    Open file For Append As #h
    Print #h, line
    Close #h
End Sub

Private Function JEsc(ByVal s As String) As String
    JEsc = Replace(Replace(Replace(s, "\", "\\"), """", "\"""), vbCrLf, "\n")
End Function

Private Function EnsureLogPath() As String
    Dim p As String: p = ThisWorkbook.Path & "\logs"
    If Dir(p, vbDirectory) = "" Then MkDir p
    EnsureLogPath = p
End Function
VB

重要ポイントの深掘り

  • CSVは人間に読みやすい、JSONLは機械に優しい。両方持つと運用と分析の両輪が回ります。
  • JSONはエスケープが命。最低限の文字と改行を逃がしておけば、下流で安全にパースできます。

エラー記録の“粒度”設計と例外ラッパー

Tryラッパーで例外→ERRORログ→再スロー(運用しやすい形)

' ModTry.bas
Option Explicit

Public Function TryRun(ByVal category As String, ByVal procName As String) As Boolean
    Dim scope As ScopeInfo: scope = BeginScope(category)
    On Error GoTo EH
    Application.Run procName
    EndScope scope, True
    TryRun = True
    Exit Function
EH:
    LogError category, "EXCEPTION: " & Err.Description, "number=" & Err.Number
    EndScope scope, False
    TryRun = False
End Function
VB

重要ポイントの深掘り

  • エラーは“どこで発生したか、何が原因か、相関IDは何か”を同じ行で残します。
  • ラッパーに寄せることで“例外でも必ずEND Scopeに到達”し、孤児スコープをなくします。

回転・アーカイブ・保持ポリシー(肥大化を止める)

週次アーカイブ例(ZIPにまとめて古いものを整理)

' ModLogRotate.bas
Option Explicit

Public Sub ArchiveLogsWeekly()
    Dim dirPath As String: dirPath = ThisWorkbook.Path & "\logs"
    Dim zipPath As String: zipPath = dirPath & "\archive_" & Format(Date, "yyyy_ww") & ".zip"
    If Dir(zipPath, vbNormal) <> "" Then Kill zipPath
    CreateEmptyZip zipPath

    Dim sh As Object: Set sh = CreateObject("Shell.Application")
    Dim srcNS As Object: Set srcNS = sh.NameSpace(dirPath)
    Dim dstNS As Object: Set dstNS = sh.NameSpace(zipPath)
    dstNS.CopyHere srcNS.Items, 16
    ' 実務は“今週は除外”などのルール化推奨(例:ファイル名で判別)
End Sub

Private Sub CreateEmptyZip(ByVal zipPath As String)
    Dim h As Integer: h = FreeFile
    Open zipPath For Binary As #h
    Put #h, , Chr$(80) & Chr$(75) & Chr$(5) & Chr$(6) & String$(18, Chr$(0))
    Close #h
End Sub
VB

重要ポイントの深掘り

  • 日次CSV/JSONLは“直近+今週”のみ残し、週次ZIPへ回転を。分析用にはPower Queryや外部保管を併用。
  • 保持期間は業務要件(監査/トラブルシュート)に合わせて明文化します。

ログビューワとアラート(赤が一目、件数サマリで傾向)

シートビュー(色分け&集計)

' ModLogView.bas
Option Explicit

Public Sub ViewLatestLogs()
    Dim ws As Worksheet: Set ws = PrepareOut("Logs_View")
    ws.Range("A1:F1").Value = Array("ts", "level", "corId", "category", "msg", "ctx")

    Dim path As String: path = ThisWorkbook.Path & "\logs\" & Format(Date, "yyyy-mm-dd") & ".csv"
    If Dir(path, vbNormal) = "" Then
        ws.Range("A2").Value = "ログなし"
        Exit Sub
    End If

    Dim h As Integer: h = FreeFile
    Open path For Input As #h
    Dim r As Long: r = 2, line As String
    Do While Not EOF(h)
        Line Input #h, line
        Dim cols() As String: cols = ParseCsv(line)
        Dim c As Long: For c = 0 To WorksheetFunction.Min(UBound(cols), 5): ws.Cells(r, c + 1).Value = cols(c): Next
        r = r + 1
    Loop
    Close #h

    ' 条件付き書式(ERROR行を赤、WARNを黄)
    With ws.Range("A2:F" & r - 1)
        .FormatConditions.Delete
        .FormatConditions.Add Type:=xlExpression, Formula1:="=$B2=""ERROR"""
        .FormatConditions(1).Interior.Color = RGB(255, 200, 200)
        .FormatConditions.Add Type:=xlExpression, Formula1:="=$B2=""WARN"""
        .FormatConditions(2).Interior.Color = RGB(255, 255, 180)
    End With
    ws.Columns.AutoFit
End Sub

Private Function ParseCsv(ByVal line As String) As String()
    ' 簡易CSV(ダブルクォートで囲み、カンマをスペースに置換済み前提)
    ParseCsv = Split(Replace(line, """" , ""), ",")
End Function

Private Function PrepareOut(ByVal name As String) As Worksheet
    Dim ws As Worksheet
    On Error Resume Next: Set ws = Worksheets(name): On Error GoTo 0
    If ws Is Nothing Then Set ws = Worksheets.Add: ws.Name = name
    ws.Cells.Clear
    Set PrepareOut = ws
End Function
VB

重要ポイントの深掘り

  • ビューワは“赤と黄色が目立つ”だけでも十分効果あり。現場で異常が一目で分かります。
  • 日次サマリ(ERROR/WARN件数の合計)を出して傾向監視。急増・急減は品質のシグナルです。

例題の通し方:ETLの各工程でログを出し、相関IDで追跡

入口→工程→出口のログ例

' ModExample_Etl.bas
Option Explicit

Public Sub RunEtlOnce()
    LogInit
    Dim s As ScopeInfo: s = BeginScope("ETL")
    On Error GoTo EH

    LogInfo "Import", "start", "file=input.csv"
    ' ... 読み込み処理
    LogInfo "Validate", "ok", "rows=10000"
    ' ... 検証処理
    LogWarn "Transform", "trim applied", "affected=234"
    ' ... 加工処理
    LogInfo "Export", "done", "out=report.xlsx"

    EndScope s, True
    LogShutdown
    Exit Sub
EH:
    LogError "ETL", "failed", "err=" & Err.Description
    EndScope s, False
    LogShutdown
End Sub
VB

重要ポイントの深掘り

  • 各工程の“開始/件数/所要/終了”を記録。WARNは“品質には影響しないが挙動に注意”の意味で使うと、現場が強くなります。
  • エラーでもShutdownで必ずフラッシュし、証跡を残します。

落とし穴と対策(深掘り)

逐次書きで遅い・固まる

メモリバッファ+定期フラッシュへ。I/Oをまとめると速度が劇的に改善します。

相関IDがないため追跡できない

スコープ開始時に必ず発行・紐付け。一本の処理を“見えるひも”にします。

タイムスタンプがバラバラ(ローカル時刻)

表記はUTCに固定。表示時にローカルへ変換。集計・比較が安定します。

ログが肥大・消えない

日次ローテーション+週次アーカイブ+保持期間を明文化。運用ルールに。

CSVのカンマや改行で壊れる

SafeCsvで最低限のエスケープ。運用上の“壊れない”が第一。


まとめ:固定スキーマ+相関ID+バッファリングで“追跡できる速いログ”にする

  • レベル・時刻・カテゴリ・相関ID・メッセージ・コンテキストの固定カラム。
  • バッファリングとOnTimeフラッシュで高速安定、失敗時はシートへフォールバック。
  • スコープBEGIN/ENDで所要時間と完了/失敗を自動記録、日次ローテーションと週次アーカイブで運用が軽い。
  • CSV+JSONLの二系統で、人間・機械の両輪を回し、ビューとサマリで“赤がすぐ分かる”。

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