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 情報
- リポジトリ
-
ライセンス
- 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. 下記サイトへアクセスします。
リリースページ(通常「Releases」と表示されています)から最新版のアドインファイルをダウンロードします。 (ファイル名:VbaStackTraceLogger.xlam
) 整備中
2. VbaStackTraceLogger.xlam
)
VbaStackTraceLogger.xlam
をダウンロードします。
2. bin フォルダから
3. ダウンロードしたアドインファイルを任意のフォルダに保存します。
VBA プロジェクトからの参照設定
ロガーを利用したい Excel ファイル(.xlsm
などマクロ有効ブック)を開き、VBA エディタ(VBE)にてアドインへの参照設定を行います。
これにより、VBA コードからロガーの機能(関数やオブジェクト)を呼び出せるようになります。
1. ロガーを導入したい Excel ファイルを開き、VBE を起動します。
Excel の 開発 タブ
> Visual Basic
をクリックします。
開発タブがリボンに表示されていない場合
Excel の ファイル タブ
> オプション
をクリックします。
リボンのユーザー設定
を選択します。
画面右側のリストにある 開発
のチェックボックスをオンにし、OK
をクリックします。
2. 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.
と入力した時
・MyLogger.Log
と入力した時
・MyLogger.Log "message",
と入力した時
ロガーの初期化
ロガーアドインの機能を利用する前に、まずロガーを初期化する必要があります。
初期化処理では、ログの出力先やスタックトレース情報の出力有無といった、ロガーの動作をカスタマイズできます。
ロガーの設定をカスタマイズする
MyLogger.StartConfiguration
と記入することでロガーの設定を行えます。
StartConfiguration.Build
とすると、デフォルトの設定で初期化が行われます。
設定をカスタマイズする場合、Build
の手前にメソッドチェーンを足してください。
(具体例:StartConfiguration.EnableStackTrace.Build
)
デフォルトの設定について
必要最低限の機能のみになっています。
スタックトレース機能は無効化、ログ出力先は VBE のイミディエイトウインドウのみです。
設定可能な項目
プロシージャ名 | 説明 |
---|---|
DisableLogging |
ログ出力を無効化します。 |
EnableTagFiltering |
指定されたタグのログ出力を無効化します。 |
EnableStackTrace |
スタックトレース機能を有効化します。 |
DisableWriteToImmediate |
VBE のイミディエイトウィンドウへのログ出力を無効化します。 |
EnableWriteToExcelSheet |
エクセルシートへのログ出力を有効化します。 |
EnableTagFiltering
を使用した場合
次のメソッドチェーンは Add
と Apply
に限定されます。
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
この例では、モジュール
MyModule
のMainProc
プロシージャから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_NAME
と PROC_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 Sub
、Exit Sub
、End Function
、Exit Function
)それぞれに終了ログを記述する手間が省け、記述漏れも防げます。
ロガーの終了処理
VBA の一連の処理がすべて完了した後、ロガーの終了処理も忘れずに行ってください。
この終了処理では、エクセルの列幅の最終調整などを行っています。
1. MyLogger.Terminate を呼び出します。
メインプロシージャの最後など、すべてのログ関連処理が完了した箇所で以下を実行します。
MyLogger.Terminate
サンプルコード
本項では、ロガーの初期化・スタックトレース機能・ログ出力・ロガーの終了処理、を組み合わせた具体的なコード例を紹介します。
以下の例では、以下の様なモジュール構成・処理の流れとなっています。
- 標準モジュール
MyModule
- プロシージャ
UseCase
、MainProc
、SubProc
- プロシージャ
- クラスモジュール
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