ログファイルに出力

VBAの変数の値や実行状態を確認する際に「Debug.Print」を利用してイミディエイトウィンドウに結果を出力することができます。

ただし、イミディエイトウィンドウは過去の出力内容は消されていきますし、出力内容や確認したい内容が多い場合には不向きです。ウィンドウも小さいため見やすいものではありません。

そういう場合は、外部のテキストファイル(ログファイル)に出力して扱いやすくしましょう。

以下にログファイルに書き込みを行うLogクラスと、その利用サンプルのコードを紹介します。

事前準備

以下のコードではFileSystemObjectを参照設定していることが前提になっています。

VBAの画面を開き、ツールメニュー→参照設定 で「Microsoft Scripting Runtime」にチェックを付けてください。

FileSystemObjectを利用する際に多くのサイトではCreateObject関数を使う方法がよく紹介されていますが、処理速度が遅いのでここでは参照設定を使う方法を採用しています。

Logクラス

以下のログファイル出力クラスのコードはコピペしてそのまま使えます。

利用する場合は以下のコードをクラスモジュールとして追加してください。クラス名は「Log」です。

汎用的に利用する場合はPersonal.xlsbのクラスモジュールとして追加してください。

Logクラスの構成説明

Logクラスの構成について説明します。

SYSTEMTIME構造体 Win32APIのGetLocalTime関数の引数に利用します。
GetLocalTime システム日時を取得するWin32APIの関数です。ミリ秒を使いたかったため利用しています。ログファイルに書き込む際の日時文字列とバックアップファイル作成時のファイル名に付与する日時を取得する際に利用します。ミリ秒の精度は10から20ミリ秒程度とあまり良くありません。
モジュール変数 クラス全体で使用する変数をモジュール変数として定義しています。
Class_Initialize Logクラスのコンストラクタ(インスタンス作成時の処理)です。特にやることはないので処理はありません。
Class_Terminate Logクラスのデストラクタ(インスタンス解放時の処理)です。特にやることはないので処理はありません。
Init 書き込み先のログファイル名の指定とログファイルに書き込める最大サイズを指定します。最大サイズは省略可能で、省略時は約1MBにしています。通常は書き込み先のログファイルが変わることは無いと思いますが、処理途中で書き込み先を変更したい場合は再度呼び出すことが可能です。
WriteLog ログファイルに指定文字列を書き込みます。ログファイルを格納するフォルダが無い場合や既に別のアプリケーションでログファイルが開かれてロック(書き込み不可)状態になっている場合はイミディエイトウィンドウにその内容を出力してログファイルへの書き込みを行いません。Init関数で指定されたログファイル最大サイズを超えている場合はバックアップファイルを作成し、その後新しくログファイルを作成して書き込みを行います。
GetDateTime GetLocalTime関数を用いてシステム日時を取得します。取得後にYYYY/MM/DD hh:mm:ss.mmmの書式にします。
Rename ログファイル名を変更します。変更後の名前は「元の名前+現在日時(_YYYYMMDD_hhmmss.mmm)+拡張子」になります。

Logクラスの利用方法

Logクラスの利用方法を以下のコードをふまえながら説明します。

2行目 最初にLogクラス用の変数を用意します。このときにNewを指定します。
6行目 Init関数を使い、書き込み先のログファイルパスを指定します。Init関数は通常このときに一度だけ利用します。5行目のようにログファイルの最大サイズを指定することも可能です。省略時は約1MBを最大サイズとしています。ログファイルがそのサイズを超えているとファイル名をバックアップ用に変更して再度新たに書き込みを行います。
8行目 WriteLog関数で書き込み文字列を指定することでログファイルに書き込まれます。ログファイルには処理時間と書き込み文字列が1行で書き込まれ、改行コードが設定されます。
17行目 再度Init関数を使い、書き込み先を変更できます。これ以降のWriteLog関数は変更後の書き込み先のログファイルに書き込みます。
18行目 変更後の書き込み先に書き込みます。

実行結果
2つのファイルが出力されます。
logtest.log

b.log

改善点

このLogクラスには処理速度の点で改善点があります。

WriteLog関数は呼び出す度にファイルのオープンとクローズを行っています。ファイルのオープン処理は結構時間が掛かる処理のため、そこで時間をロスしています。

ただし、処理途中でログファイルを確認する場合などはこちらの方式であれば常に最新状態のログが確認できる利点もあります。

もし処理速度を優先させたい場合はWriteLog関数内でのオープン処理とクローズ処理をやめて、Init関数とRename関数でオープン、Rename関数とデストラクタでクローズするようにするとオープンとクローズが最初と最後にしか行われないようになるため処理速度は上がります。