🌿

VBA でスタックトレース機能付きのロガーアドインを作成しました - アドイン導入・使用方法 -

に公開

はじめに

プログラミングでデバッグを行う際、スタックトレース(関数の呼び出し履歴)は非常に重要な情報です。

エラーが起きたときにどの関数からどの関数を経由してここに至ったのかが分かれば、原因を突き止めやすくなります。

しかし、VBAでは「コード実行中」にスタックトレースを取得する標準的な機能がありません

そこで、VBA でスタックトレースを含むログ出力を簡単に行えるロガーアドインを作成しました。

ロガーのデモ
[2025-06-22 19:30:24.076][Trace][MyModule.MainProc] >> Enter MyModule.MainProc
[2025-06-22 19:30:24.077][DEBUG][MyModule.MainProc] This is my message
[2025-06-22 19:30:24.079][Trace][MyModule.SubProc < MyModule.MainProc] >> Enter MyModule.SubProc

エクセルとイミディエイトウインドウへの出力結果

本記事について

リファレンス的な構成としており、本ロガーの導入方法から基本的な使用方法を解説します。

より実践的な活用方法については、次回の記事(活用例)で解説する予定です。

OSS 情報

  • リポジトリ

https://github.com/ryuryu333/vba_stack_trace_logger

  • ライセンス

    • MIT
  • 対応環境

    • OS:Windows
    • 対応アプリ:Excel
    • VBA バージョン:7.x

対象読者

  • VBA 脱初級者 ~ 中級者

    • VBA の基本的な学習を一通り終え、ご自身で簡単なマクロを作成したり、既存のコードをカスタマイズをし始めた方
    • より効率的なデバッグ方法や、本格的なログ出力に関心を持ち始めた方
  • 具体的な目安

    • VBE(Visual Basic Editor)の基本的な操作(標準モジュールの挿入、コードの記述・実行、イミディエイトウィンドウの表示)ができる
    • 変数宣言 (Dim)、基本的なデータ型 (String, Boolean, Long など)を理解し、プロシージャ (Sub, Function) を作成・使用したことがある
    • Debug.Print を使ったことがある、または、その存在や基本的な使い方を理解している
    • (推奨)Excel ファイル (.xlsm など) でマクロを有効にする方法や、アドインファイル (.xlam) がどのようなものかについて、基本的な知識がある、または、学ぶ意欲がある
    • (推奨)外部のコードやライブラリを参照して利用することに抵抗がない、または、挑戦してみたい

アドインのダウンロード・導入

ファイルのダウンロード

ロガーアドインの本体となるファイル(.xlam 形式)をダウンロードし、PCに保存します。

1. 下記サイトへアクセスします。

https://github.com/ryuryu333/vba_stack_trace_logger

2. リリースページ(通常「Releases」と表示されています)から最新版のアドインファイルをダウンロードします。 (ファイル名:VbaStackTraceLogger.xlam 整備中

2. bin フォルダから VbaStackTraceLogger.xlam をダウンロードします。

https://github.com/ryuryu333/vba_stack_trace_logger/blob/main/bin/VbaStackTraceLogger.xlam

ダウンロードページ

3. ダウンロードしたアドインファイルを任意のフォルダに保存します。

VBA プロジェクトからの参照設定

ロガーを利用したい Excel ファイル(.xlsm などマクロ有効ブック)を開き、VBA エディタ(VBE)にてアドインへの参照設定を行います。

これにより、VBA コードからロガーの機能(関数やオブジェクト)を呼び出せるようになります。

1. ロガーを導入したい Excel ファイルを開き、VBE を起動します。

Excel の 開発 タブ > Visual Basic をクリックします。

Excel の開発タブと Visual Basic の場所

開発タブがリボンに表示されていない場合

Excel の ファイル タブ > オプション をクリックします。
リボンのユーザー設定 を選択します。
画面右側のリストにある 開発 のチェックボックスをオンにし、OK をクリックします。

リボンのユーザー設定画面

2. VBE のメニューから「参照設定」ダイアログを開きます。

ツール > 参照設定 をクリックします。

VBE の参照設定の場所

3. 「参照設定」ダイアログで、ダウンロードしたアドインファイルを参照します。

参照(B) ボタンをクリックします。

「ファイル参照」ダイアログが開きます。
右下のファイル種類のドロップダウンリストで、デフォルトのタイプライブラリ (*.olb;*.tlb;*.dll) から Microsoft Excel Files (*.xlsm;*.xlam;*.xls;*.xla) を選択します。

その後、前の手順でアドインファイルを保存したフォルダに移動し、アドインファイル(VbaStackTraceLogger.xlam)を選択して 開く ボタンをクリックします。

参照画面とファイル参照画面におけるクリックする位置

4. 参照設定が正しく行われたことを確認します。

「参照設定」ダイアログの「参照可能なライブラリファイル(A)」のリスト内に、アドインのプロジェクト名(VbaStackTraceLogger)が表示され、その左側のチェックボックスがオンになっていることを確認してください。

問題なければ OK ボタンをクリックしてダイアログを閉じます。

アドイン登録後の参照設定画面

導入確認

ここまでの手順で、VBA プロジェクトからロガーアドインの機能を利用する準備が整いました。

実際にロガーが利用可能か、簡単なコードを実行して確認します。

1. 新しいモジュールを作成します。

VBE のメニューから 挿入 > 標準モジュール をクリックします。

標準モジュール作成のボタンの場所

2. 挿入された標準モジュールに、以下の確認用コードを記述します。

Sub CheckLogger()
    ' === ロガーの初期化 ===
    myLogger.StartConfiguration.Build
    ' === ログの出力 ===
    myLogger.Log "This is logger test"
    ' === ロガーの終了処理 ===
    myLogger.Terminate
End Sub

3. 確認用コードを実行します。

VBE のメニューから 実行 > Sub/ユーザーフォームの実行 をクリックするか、F5 キーを押します。

下画像のように「マクロ」ダイアログが表示された場合は、CheckLogger を選択して実行をクリックします。

マクロダイアログの画面

「マクロ」ダイアログが表示されない場合もあります

カーソルが特定のプロシージャ内(この例では Sub CheckLogger() から End Sub の間)にある状態で実行を指示すると、そのプロシージャが直接実行され、「マクロ」ダイアログは表示されません。

4. ログ出力を確認します。

イミディエイトウィンドウを確認し、ログが以下の様に出力されているか確認します。

実行結果
Finished initializing Immediate Window writer.
[2025-06-22 23:41:05.296][INFO] This is logger test
VBE でイミディエイトウィンドウが表示されていない場合

VBE のメニューから 表示 > イミディエイト ウィンドウ(Ctrl + G) をクリックします。

イミディエイトウィンドウを表示するボタンの場所

ロガーの使用方法

Tips インテリンス機能を活用する

アドインごとに固有のプロシージャ名を覚えるのは面倒なことです。

そこで、本ロガーは VBE のインテリセンス機能を活用し、暗記箇所を極力減らすことを念頭に置いて設計しています。

ロガーの全ての機能は MyLogger というワード経由でアクセスできます。

MyLogger. の様に ピリオド . を記入すると、プロシージャの候補が表示されます。
また、引数が必要な場合もインテリセンス機能で確認できます。

  • 何ができるのか?
  • どの引数を渡すべきか?

といった時はインテリセンス機能を頼ってみてください。

MyLogger. と入力した時
インテリンスの例1

MyLogger.Log と入力した時
インテリンスの例2

MyLogger.Log "message", と入力した時
インテリンスの例3

ロガーの初期化

ロガーアドインの機能を利用する前に、まずロガーを初期化する必要があります。

初期化処理では、ログの出力先スタックトレース情報の出力有無といった、ロガーの動作をカスタマイズできます。

ロガーの設定をカスタマイズする

MyLogger.StartConfiguration と記入することでロガーの設定を行えます。

StartConfiguration.Build とすると、デフォルトの設定で初期化が行われます。

設定をカスタマイズする場合、Build の手前にメソッドチェーンを足してください。
(具体例:StartConfiguration.EnableStackTrace.Build

インテリンスで設定候補が表示されている

デフォルトの設定について

必要最低限の機能のみになっています。
スタックトレース機能は無効化、ログ出力先は VBE のイミディエイトウインドウのみです。

設定可能な項目

プロシージャ名 説明
DisableLogging ログ出力を無効化します。
EnableTagFiltering 指定されたタグのログ出力を無効化します。
EnableStackTrace スタックトレース機能を有効化します。
DisableWriteToImmediate VBE のイミディエイトウィンドウへのログ出力を無効化します。
EnableWriteToExcelSheet エクセルシートへのログ出力を有効化します。

EnableTagFiltering を使用した場合

次のメソッドチェーンは AddApply に限定されます。
Apply を使用すると、他の設定が再度選択可能になります。

プロシージャ名 説明
Add(LoggerLogTag) ログ出力を無効化するタグを引数で指定します。
Apply タグの指定を完了します。

※ 複数のタグを指定する場合は、Add を複数回使用してください。

インテリセンスを利用したタグの指定画面

EnableWriteToExcelSheet を使用した場合

次のメソッドチェーンは SetOutputExcelSheet に限定されます。
SetOutputExcelSheet を使用すると、他の設定が再度選択可能になります。

プロシージャ名 説明
SetOutputExcelSheet(Worksheet) ログを出力するエクセルシートを引数で指定します。
エクセルシート(Worksheet ブジェクト)の設定例
' 現在アクティブな(表示されている)Excel シート
ActiveSheet

' マクロが記述されている Excel ファイル(ThisWorkbook)内の
' "SheetName" という名前のシート
' ※該当する名前のシートが存在しない場合はエラーになるため注意
ThisWorkbook.Sheets("SheetName")

ログの出力

プロシージャの実行中に任意の情報をログとして記録したい場合、myLogger.Log を使用します。

このプロシージャは、ロガーが初期化された後であれば、プロジェクト内のどこからでも呼び出すことができます。

1. ログを出力したい箇所で、MyLogger.Log を呼び出します。

VBA 標準の Debug.Print と同じく、引数としてログ出力したい文字列を渡します。

MyLogger.Log "ログメッセージ"
文字列だけでなく、変数や式の結果も使用可能です
Dim userName As String
userName = "John"
MyLogger.Log "現在のユーザー: " & userName

Dim quantity As Long
quantity = 10
Dim unitPrice As Currency
unitPrice = 150.5
MyLogger.Log "合計金額: " & quantity * unitPrice & " 円"

2. (任意) タグを指定します。

ログメッセージの重要度や種類を示すために、第二引数でタグ(ログレベル)を指定できます。

MyLogger.Log "ログメッセージ", と入力し、カンマの後にスペースを打つと、VBE のインテリセンス機能により利用可能なタグの候補が表示されます。

マウスでクリックするか、キーボードの矢印キーで選択し Enter キーまたは Tab キーで決定できます。

ログ出力の命令時にオプションとしてタグを指定できる、タグの候補がインテリセンス機能で表示されている。

MyLogger.Log "Error タグを選択", LogTag_Error
MyLogger.Log "Warning タグを選択", LogTag_Warning
MyLogger.Log "タグを省略した場合は Info タグが自動選択される"
ログの出力結果
[2025-05-23 00:40:55.381][ERROR] Error タグを選択
[2025-05-23 00:40:55.384][WARNING] Warning タグを選択
[2025-05-23 00:40:55.388][INFO] タグを省略した場合は Info タグが自動選択される

利用可能なタグは以下の 6 種類です。
タグを指定しない場合は Info が選択されます。

利用可能なタグ
Public Enum LoggerLogTag
    LogTag_Debug = 0
    LogTag_Info = 1
    LogTag_Warning = 2
    LogTag_Error = 3
    LogTag_Critical = 4
    LogTag_Trace = 5
End Enum

スタックトレース機能

この機能を用いることで、以下の 2 点が実現可能となります。

  • プロシージャ開始・終了ログの自動出力
    監視対象プロシージャの呼び出し時と終了時に、ログが自動的に記録されます。
[2025-05-22 23:36:48.867][Trace][MyModule.MainProc] >> Enter MainProc
[2025-05-22 23:36:48.898][Trace][MyModule.MainProc] << Exit  MainProc
  • 通常ログへの呼び出し履歴(スタックトレース)情報の付与
    ログ出力されるメッセージに、現在の呼び出し階層(どのプロシージャから呼び出されたか)が自動的に付加されます。
[2025-05-22 23:36:48.875][INFO] [MyModule.SubProc < MyModule.MainProc] 処理2

この例では、モジュール MyModuleMainProc プロシージャから SubProc プロシージャが呼び出されている、ログ出力が実施された場所は SubProc である、といった情報が得られます。

1. 初期化時にスタックトレース機能を有効化します。

MyLogger.StartConfiguration.EnableStackTrace.Build

2. モジュール名を定数として定義します。

スタックトレース機能を利用したいプロシージャが含まれるモジュールの一番最初(宣言セクション)で、そのモジュールの名前を文字列定数として定義します。

"ModuleName" の部分を実際のモジュール名に書き換えてください。

Option Explicit
' モジュールの宣言セクションに記述
Private Const MODULE_NAME As String = "MyModule"

この定数は、そのモジュール内の全てのプロシージャで共通して使用されます。

3. プロシージャの先頭行に、プロシージャ名定義と UsingTracer 関数の呼び出しを記述します。

スタックトレースの対象としたい各プロシージャの内部、実質的な処理の最初の行に、以下の 2 つの要素を 1 行にまとめて記述します。

3-a. プロシージャ名を定数として定義します。

"ProcedureName" の部分を実際のプロシージャ名に書き換えてください。

Const PROC_NAME As String = "ProcedureName"

3-b. UsingTracer を呼び出し、その戻り値を scopeGuard 変数に格納します。

先述(3-a)定義したプロシージャ名定数に続けて、同じ行にコロン(:)で区切り、以下のコードを記述します。

この MyLogger.UsingTracer にて、先ほど記述した MODULE_NAMEPROC_NAME が使用されます。

: Dim scopeGuard As Variant: Set scopeGuard = MyLogger.UsingTracer(MODULE_NAME, PROC_NAME)

コード全体

Option Explicit
' 1. モジュール名を定数として定義
Private Const MODULE_NAME As String = "MyModule"

Sub CheckLogger()
    ' 2. 初期化時にスタックトレース機能を有効化
    MyLogger.StartConfiguration.EnableStackTrace.Build
    MainProc
End Sub

Sub MainProc()
    ' 3-a. プロシージャ名を定数として定義
    ' 3-b. UsingTracer 関数を呼び出し、scopeGuard 変数に格納
    Const PROC_NAME As String = "MainProc": Dim scopeGuard As Variant: Set scopeGuard = MyLogger.UsingTracer(MODULE_NAME, PROC_NAME)

    ' (プロシージャの処理)
End Sub
scopeGuard 変数の役割について

scopeGuard 変数は、ロガーのスタックトレース機能の核心であり、以下の役割を持つ変数です。

詳細は別の記事で解説する予定です。

プロシージャの先頭で UsingTracer 関数の戻り値をこの変数に代入するだけで、終了ログが自動的に記録されることが保証されるため、プロシージャ内の複数の終了(End SubExit SubEnd FunctionExit Functionそれぞれに終了ログを記述する手間が省け、記述漏れも防げます

ロガーの終了処理

VBA の一連の処理がすべて完了した後、ロガーの終了処理も忘れずに行ってください

この終了処理では、エクセルの列幅の最終調整などを行っています。

1. MyLogger.Terminate を呼び出します。

メインプロシージャの最後など、すべてのログ関連処理が完了した箇所で以下を実行します。

MyLogger.Terminate

サンプルコード

本項では、ロガーの初期化・スタックトレース機能・ログ出力・ロガーの終了処理、を組み合わせた具体的なコード例を紹介します。

以下の例では、以下の様なモジュール構成・処理の流れとなっています。

  • 標準モジュール MyModule
    • プロシージャ UseCaseMainProcSubProc
  • クラスモジュール MyClass
    • プロシージャ ClsMainProc
プロシージャが呼び出される順番
UseCase -> MainProc -> SubProc -> ClsMainProc
標準モジュール
' === MyModule ===
Option Explicit
Private Const MODULE_NAME As String = "MyModule"

Sub UseCase()
    ' === ロガーの初期化 ===
    MyLogger.StartConfiguration.EnableStackTrace.Build
    ' === 処理開始 ===
    MainProc
    ' === ロガーの終了処理 ===
    MyLogger.Terminate
End Sub

Sub MainProc()
    Const PROC_NAME As String = "MainProc": Dim scopeGuard As Variant: Set scopeGuard = MyLogger.UsingTracer(MODULE_NAME, PROC_NAME)
    
    MyLogger.Log "処理1"

    ' モジュール内のプロシージャを呼び出し
    SubProc
End Sub

Private Sub SubProc()
    Const PROC_NAME As String = "SubProc": Dim scopeGuard As Variant: Set scopeGuard = MyLogger.UsingTracer(MODULE_NAME, PROC_NAME)
    
    MyLogger.Log "処理2"

    ' クラスモジュールのプロシージャを呼び出し
    Dim hoge As New MyClass
    hoge.ClsMainProc
End Sub
クラスモジュール
' === MyClass ===
Option Explicit
Private Const MODULE_NAME As String = "MyClass"

Public Sub ClsMainProc()
    Const PROC_NAME As String = "ClsMainProc": Dim scopeGuard As Variant: Set scopeGuard = MyLogger.UsingTracer(MODULE_NAME, PROC_NAME)
    
    MyLogger.Log "処理3"
End Sub
出力されるログ(イミディエイトウインドウ)
Finished initializing Immediate Window writer.
[2025-05-22 20:24:35.891][Trace][MyModule.MainProc] >> Enter MainProc
[2025-05-22 20:24:35.898][INFO][MyModule.MainProc] 処理1
[2025-05-22 20:24:35.906][Trace][MyModule.SubProc < MyModule.MainProc] >> Enter SubProc
[2025-05-22 20:24:35.922][INFO][MyModule.SubProc < MyModule.MainProc] 処理2
[2025-05-22 20:24:35.922][Trace][MyClass.ClsMainProc < MyModule.SubProc < MyModule.MainProc] >> Enter ClsMainProc
[2025-05-22 20:24:35.930][INFO][MyClass.ClsMainProc < MyModule.SubProc < MyModule.MainProc] 処理3
[2025-05-22 20:24:35.930][Trace][MyClass.ClsMainProc < MyModule.SubProc < MyModule.MainProc] << Exit  ClsMainProc
[2025-05-22 20:24:35.938][Trace][MyModule.SubProc < MyModule.MainProc] << Exit SubProc
[2025-05-22 20:24:35.938][Trace][MyModule.MainProc] << Exit MainProc

Discussion