C#

C# TraceListernerの派生クラスを作る

プログラムを作ってデバッグをするときに「トレース」はとてもよく使います。
Visual Studioなどでは「出力」ウィンドウなどに表示などをしてくれるのでとても助かります。

デバッグの時などでも、ときどきこういった情報を扱いやすくなるようにする機能があるので試してみます。

.NETには TraceListerner というオブジェクトが用意されています。TraceListernerとは、トレース出力を受け取って、それを他の場所に出力するオブジェクトです。
TraceListerner は、アプリケーションからのトレース情報を出力ストアに送り出すためのパイプのようなもので、トレース情報をトレースリスナに書き込むと、そのトレースリスナがトレース情報をターゲットメディアに出力してくれます。

 

TraceListernerの派生クラスを作る

まず TraceListerner の派生クラスを作成します。
派生クラスは、単純に受け取ったトレース情報をテキストファイルに出力するだけのものにします。
このクラスは「クラスライブラリ(DLL)」として作成します。

TraceListernerクラスは、”System.Diagnostics.TraceListener”なので、”using System.Diagnostics;”を用意します。
クラスのメンバに文字列を保持するための”StringCollection”クラスを使うので、”using System.Collections.Specialized;”も必要です。

TraceListernerクラスの派生クラスに必要なメソッド

public override void Write(string message)
public override void Write(object o)
public override void Write(string message, string category)
public override void Write(object o, string category)
public override void WriteLine(string message)
public override void WriteLine(object o)
public override void WriteLine(string message, string category)
public override void WriteLine(object o, string category)
public override void Fail(string message)
public override void Fail(string message, string detailMessage)
public override void Close()
public override void Flush()

 

こちらは、今回の派生クラス固有の実装です。
private void OutputFile()・・・ファイルに出力するためのメソッド
private void Output(string message)・・・トレース情報を保持するためのメソッド

 

これらの実装を行ってクラスは、下記のようになります。

    public class Listener : TraceListener
    {
        /// <summary>出力ファイルパス</summary>
        string outfile_path = string.Empty;
        /// <summary>文字列最大保持数</summary>
        protected const int MAX_COUNT = 1000;
        /// <summary>文字列データ本体</summary>
        private StringCollection strings;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Listener()
        {
            // コンストラクタ
            string path = System.Reflection.Assembly.GetEntryAssembly().Location;
            System.IO.FileInfo fi = new System.IO.FileInfo(path);
            outfile_path = System.IO.Path.Combine(fi.Directory.FullName, System.Configuration.ConfigurationSettings.AppSettings["TraceFileName"]);
            strings = new StringCollection();
            strings.Clear();
        }

        /// <summary>
        /// ファイル出力処理
        /// </summary>
        private void OutputFile()
        {
            try
            {
                Encoding enc = Encoding.UTF8;
                // 保持している内容をファイルに出力する
                System.IO.StreamWriter writer = new System.IO.StreamWriter(outfile_path, true, enc);
                foreach (string s in this.strings)
                {
                    writer.Write(s);
                }
                writer.Close();

                // リストはクリア
                strings.Clear();

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// ファイル出力処理
        /// </summary>
        /// <param name="message">出力するトレース情報</param>
        private void Output(string message)
        {
            try
            {
                // メッセージを保持する
                strings.Add(message);
                if (MAX_COUNT <= strings.Count)
                {
                    // 保持する最大数を超えた場合
                    // ファイルに出力する
                    this.OutputFile();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 指定したメッセージをリスナーに書き込みます。
        /// </summary>
        /// <param name="message">メッセージ</param>
        public override void Write(string message)
        {
            this.Output(message);
        }

        /// <summary>
        /// 指定したメッセージをリスナーに書き込みます。
        /// </summary>
        /// <param name="o">対象オブジェクト</param>
        public override void Write(object o)
        {
            this.Output(o.ToString());
        }

        /// <summary>
        /// 指定したメッセージとカテゴリ名をリスナーに書き込みます。
        /// </summary>
        /// <param name="message">メッセージ</param>
        /// <param name="category">カテゴリ名</param>
        public override void Write(string message, string category)
        {
            this.Output(message);
        }

        /// <summary>
        /// オブジェクトのToStringの値とカテゴリ名をリスナーに書き込みます。
        /// </summary>
        /// <param name="o">対象オブジェクト</param>
        /// <param name="category">カテゴリ名</param>
        public override void Write(object o, string category)
        {
            this.Output(o.ToString());
        }

        /// <summary>
        /// リスナーにメッセージと行終端記号を書き込む
        /// </summary>
        /// <param name="message">メッセージ</param>
        public override void WriteLine(string message)
        {
            this.Write( message + Environment.NewLine );
        }

        /// <summary>
        /// リスナーにメッセージと行終端記号を書き込む
        /// </summary>
        /// <param name="o">対象オブジェクト</param>
        public override void WriteLine(object o)
        {
            this.Write( o.ToString() + Environment.NewLine );
        }

        /// <summary>
        /// リスナーにメッセージと行終端記号を書き込む
        /// </summary>
        /// <param name="message">メッセージ</param>
        /// <param name="category">カテゴリ名</param>
        public override void WriteLine(string message, string category)
        {
            this.Write( ( message + Environment.NewLine ), category );
        }

        /// <summary>
        /// リスナーにメッセージと行終端記号を書き込む
        /// </summary>
        /// <param name="o">対象オブジェクト</param>
        /// <param name="category">カテゴリ名</param>
        public override void WriteLine(object o, string category)
        {
            this.Write( ( o.ToString() + Environment.NewLine ), category );
        }

        /// <summary>
        /// エラー メッセージを出力する
        /// </summary>
        /// <param name="message">メッセージ</param>
        public override void Fail(string message)
        {
            this.Write(message);
        }

        /// <summary>
        /// エラー メッセージと詳細エラー メッセージを出力する
        /// </summary>
        /// <param name="message">メッセージ</param>
        /// <param name="detailMessage">詳細エラー メッセージ</param>
        public override void Fail(string message, string detailMessage)
        {
            this.Write(message + Environment.NewLine);
        }

        /// <summary>
        /// 出力ストリームを終了する
        /// </summary>
        public override void Close()
        {
            this.OutputFile();
        }

        /// <summary>
        /// 出力バッファーをフラッシュする
        /// </summary>
        public override void Flush()
        {
            this.OutputFile();
        }
    }

 

TraceListernerの派生クラスを使うテストプログラム

TraceListernerの派生クラスを呼び出すためにSystem.DiagnosticsのTraceやDebugで出力を行います。

サンプルプログラムのコードは下記のようになります。

    public partial class frmMain : Form
    {
        public frmMain()
        {
            // コンストラクタ
            InitializeComponent();
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            // フォームをロードしたときの処理
            Debug.WriteLine("frmMain_Load");
        }

        private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            // フォームを閉じたときの処理
            Debug.WriteLine("frmMain_FormClosed");
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            // [Exit]ボタンを押したときの処理
            Debug.WriteLine("btnExit_Click");
            this.Close();
        }
    }

 

今回のポイント

今回のサンプルプログラムで重要になるのは、このソースコードより”App.config”というプログラムのconfigファイルのほうです。
通常のWindowsフォームアプリケーションのApp.configは、このようなものです。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup> 
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
</configuration>

 

ここに

<appSettings>
  <add key="TraceFileName" value="MyTrace.txt" />
</appSettings>

 

と、

<system.diagnostics>
  <trace autoflush="true" indentsize="2">
    <listeners>
      <add name="DBGTRACE" type="TraceListenerDLL.Listener,TraceListenerDLL"/>
    </listeners>
  </trace>
</system.diagnostics>

 

を追加して、下記のようなXMLファイルとなるようにします。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup> 
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
  <appSettings>
    <add key="TraceFileName" value="MyTrace.txt" />
  </appSettings>
  <system.diagnostics>
    <trace autoflush="true" indentsize="2">
      <listeners>
        <add name="DBGTRACE" type="TraceListenerDLL.Listener,TraceListenerDLL"/>
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

 

<appSettings>
  <add key="TraceFileName" value="MyTrace.txt" />
</appSettings>

 

<appSettings>部分は、トレース情報を出力するファイル名を指定するもので、今回の派生クラス固有の実装のための設定です。

<add key=”TraceFileName” value=”MyTrace.txt” />

のvalueに指定している”MyTrace.txt”が、出力されるファイル名です。

<system.diagnostics>
  <trace autoflush="true" indentsize="2">
    <listeners>
      <add name="DBGTRACE" type="TraceListenerDLL.Listener,TraceListenerDLL"/>
    </listeners>
  </trace>
</system.diagnostics>

 

<system.diagnostics>部分は、今回の派生クラスを呼び出して動作するようにするための設定です。

<add name=”DBGTRACE” type=”TraceListenerDLL.Listener,TraceListenerDLL”/>

この行にあるtypeの値部分は、今回作成したクラスライブラリのクラス名と名前空間です。

 

このテストプログラムをビルドして実行するときには、TraceListernerの派生クラスのあるクラスライブラリをテストプログラムの実行ファイルがあるフォルダに置いてから実行します。

 

実行結果

実行すると、Visual Studioの[出力]ウィンドウに、今までと同じような実装されたDebugの出力が下記のように出ます。

frmMain_Load
btnExit_Click
frmMain_FormClosed

また、テストプログラムの実行ファイルがあるフォルダに”MyTrace.txt”というファイルが出力されています。
この”MyTrace.txt”の中身は下記のようになりました。

frmMain_Load
btnExit_Click
frmMain_FormClosed

今回作成したDLLは、このDLLのためのプログラムを用意しなくても他のプログラムの実行ファイルと同じフォルダにDLLを置いてApp.config(実行環境の場合”<プログラム名>.exe.config”)に上記の設定を追加するだけでも使用できます。
(プロジェクト設定で「DEBUG定数の定義」や「TRACE定数の定義」にチェックが入っていないと機能しないので、ご注意ください)

 

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