C#でログ機能を実装(自作)

ログ機能を実装(自作)

C#にログ機能を実装(自作)

皆さんはアプリを作成した際に、ログなどを残していますでしょうか。
ログを残すことで、エラーが発生した際などに何が原因だったのか調査しやすかったり、逆に想定通りの挙動をしているか確認することにも使うことができます。

ネットを探せば便利なログ出力のライブラリは多数存在しますが、今回はログファイルを出力する(作成する)クラスを自作で作成してみたいと思います。
なぜわざわざ自作をするのかですが、別に特別難しい処理を実装するわけではないので、自らコードを書いてみて勉強したいという意図があります。


ログ出力の方法を考える

まずはログの出力方法ですが、ざっくり2種類あると思っています。
アプリ実行後にログファイルを出力する方法」と「継続的に1つのファイルに書き込んでいく方法」です。
ざっくり上記2つの処理イメージを整理していきます。

  • アプリ実行後にログファイルを出力する方法
  • これはstaticのListを用意しておき、メッセージをListに追加していきます。
    アプリ処理の最後にListのデータをテキストファイルなどに書き込んで保存します。
    この方法のメリットは、アプリの実行ごとにログファイルを作成するので、ファイル名などに処理日時を埋め込んでおけば調査などが容易になります。
    デメリットとしては、ちゃんと例外処理などを実装していないとファイル出力処理が実行できずログが確認できない状態になります。

  • 継続的に1つのファイルに書き込んでいく方法
  • この方法は、書き込むファイルを指定しておくことで処理が行われるたびにファイルにメッセージが表示される方法です。
    常に書き込んでいくので、対象ファイルの有無を確認する処理を組み込み、存在すれば上書き、存在しない場合は新規作成するような動きになります。
    メリットとしては、常にログが書き込まれ確認できる状態になっているためリアルタイムに確認を行うことが可能です。長時間稼働するアプリであっても停止させることなく確認ができます。
    デメリットですが、同じファイルに書き込み続けるので、ローテート処理を別で実装しないと過去のログを追うのが難しいところです。

ざっくり2種類の方法について処理イメージとメリデメを整理したので、それぞれ実装していきたいと思います。


アプリ実行後にログファイルを出力する方法

この方法では、staticのListを用意しているので、このListにメッセージを溜め込んで行きます。
Listは任意のタイミングでも初期化できるように、インスタンス化用のメソッドも用意しておきます。

public static List logList = new List();

public static void LogListInstance()
{
    logList = new List();
}

直接Listにaddしても良いのですが、実行時間やレベル(InfoやError)などを表示させたいので、ListにAddするメソッドを作成します。
メソッド呼び出し時に現在時間を取得し、レベルと合わせてカンマ区切りの文字列を用意。最後にListにAddします。

public static void WriteLogInfo(string text,  [CallerMemberName] string callerMethodName = "")
{
    DateTime dt = DateTime.Now;
    logList.Add(new string[]{ dt.ToString("yyyy/MM/dd HH:mm:ss"), "Info", callerMethodName, text });
}

public static void WriteLogError(string text,  [CallerMemberName] string callerMethodName = "")
{
    DateTime dt = DateTime.Now;
    logList.Add(new string[]{ dt.ToString("yyyy/MM/dd HH:mm:ss"), "Error", callerMethodName, text });
}
引数には書き込みたい文字列を格納するstringクラスを指定しています。
もう一つの「CallerMemberName」というのは呼び出しているメソッド名を取得するためのものです。メインクラスで使用しているとうまく機能しないのですが、ほかクラスなどから呼んでいるとちゃんとメソッド名を自動取得してくれます。
これは引数として入力する必要はありません。

最後にListのデータをファイルに書き込み保存する処理を作成します。
これは「StreamWriter」を使った一般的な方法を使用します。

private static void WriteFile(string path, string fileName)
{
    string filePath = path + @"/" + fileName + ".csv"; 
    using (StreamWriter sw = new StreamWriter(filePath, false, Encoding.UTF8))
    {
        foreach(string[] datas in logList)
        {
            sw.WriteLine(String.Format("{0},{1},{2},{3}", datas[0], datas[1], datas[2], datas[3]));
        }
    }
}
引数はファイルパスとファイル名を指定しております。
しかし、ファイル名は指定しても出力先までは指定しない場合もあるかと思い、このメソッドはあくまで実処理としてインターフェイスのメソッドは別で作成します。(なのでprivate)

引数が2パターンのメソッドを用意。ファイルパスを入力しないメソッドでは、アプリ実行しているカレントディレクトリを自動取得する処理をはさみ、WriteFileメソッドに受け渡します。

public static void MakeLogFile(string path, string fileName)
{
    WriteFile(path, fileName);
}

public static void MakeLogFile(string fileName)
{
    string path = Directory.GetCurrentDirectory();
    WriteFile(path, fileName);
} 

以上で「アプリ実行後にログファイルを出力する方法」のクラスは作成完了です。
コードの全量はGitにあげているので参考にしてみてください。
IntermittentLogger.cs


継続的に1つのファイルに書き込んでいく方法

この方法では、書き込むファイルを指定しておき処理が行われるたびメッセージを追記していく方法です。
そのため対象のファイルパスとファイル名を変数で所持しておきます。

public static string directory = Environment.CurrentDirectory;
public static string fileName = "log";

この変数を用いてファイルに書き込んでいくのですが、ファイルが存在しない場合はエラーになってしまします。
そのため、対象のファイルが存在するかどうかを確認し、存在しない場合は新規作成しておく必要があります。

private static void CheckFileExist()
{
    string path = directory + "/" + fileName + ".log";
    if (!Directory.Exists(directory))
    {
        Directory.CreateDirectory(directory);
        using (FileStream fs = File.Create(path));
    }
    else
    {
        if(!System.IO.File.Exists(path))
        {
            using (FileStream fs = File.Create(path)) ;
        }
    }
}

上記のファイルチェックは書き込む直前に実施する必要があります。
そのため、書き込み用のメソッド内で呼び出しをするようにしておきます。

public static void WriteLogInfo(string text, [CallerMemberName] string callerMethodName = "")
{
    CheckFileExist();

    DateTime dt = DateTime.Now;
    Encoding enc = Encoding.GetEncoding("UTF-8");

    string path = directory + "/" + fileName + ".log";
    using (StreamWriter writer = new StreamWriter(path, true, enc))
    {
        writer.WriteLine(String.Format("{0},{1},{2},{3}", dt.ToString("yyyy/MM/dd HH:mm:ss"), "Info", callerMethodName, text));
    }            
}

public static void WriteLogError(string text, [CallerMemberName] string callerMethodName = "")
{
    CheckFileExist();

    DateTime dt = DateTime.Now;
    Encoding enc = Encoding.GetEncoding("UTF-8");

    string path = directory + "/" + fileName + ".log";
    using (StreamWriter writer = new StreamWriter(path, true, enc))
    {
        writer.WriteLine(String.Format("{0},{1},{2},{3}", dt.ToString("yyyy/MM/dd HH:mm:ss"), "Error", callerMethodName, text));
    }            
}
なお、書き込みは「StreamWriter」を使うのですが、第2引数をtrueにしておかないと書き込むことができないので注意が必要です。
また、呼び出しもとのメソッド名を取得するために「CallerMemberName」を指定しておきます。

以上で「継続的に1つのファイルに書き込んでいく方法」のクラスは作成完了です。
こちらもコード全量をGitにあげているので参考にしてみてください。
ContinuousLogger.cs


作成したクラスを使用する

今回2つログ用のクラスを作成しました。
実際に動かして見ましょう。

  • IntermittentLogger.cs
  • まず、アプリ開始後にInstanceメソッドを呼び出し、ログ用のListをインスタンス化しておきます。
    サンプルとしていくつかInfoとErrorでログをためます。その後ファイル書き出しを行うので、MakeLogFileを呼び出してログファイルを作成します。

    using CLogSharp;
    
    // InIntermittentLogger
    IntermittentLogger.LogListInstance();
    
    IntermittentLogger.WriteLogInfo("InIntermittentLogger : Test message");
    IntermittentLogger.WriteLogError("InIntermittentLogger : Error test message");
    
    IntermittentLogger.MakeLogFile("/root/dotnetApp/TestApp", "log");                

    実行後に指定したディレクトリを確認すると、log.csvが作成されています。
    中身を確認すると、2メッセージ書き込まれており、正しくログ書き出しができていることが確認できます。

    なお同じディレクトリ、同じファイル名を指定して再度実行すると、ファイルごと上書きされます。

  • ContinuousLogger.cs
  • これは任意ですが、ログの吐き出しディレクトリとファイル名を指定しておきます。
    先程と同じくいくつかメッセージを書き込みます。

    using CLogSharp;
    
    // ContinuousLogger
    ContinuousLogger.directory = "/root/dotnetApp/TestApp";
    
    ContinuousLogger.WriteLogInfo("ContinuousLogger : Test message");
    ContinuousLogger.WriteLogError("ContinuousLogger : Error test message");

    実行後に指定したディレクトリを確認すると、log.logが作成されています。
    中身を確認すると、2メッセージが書き込まれており、ログが書き込めていることが確認できます。

    なおこちらはログファイルが既に存在している場合には、そのまま追加で書き込むようになっています。
    そのため再度アプリを実行すると、先程書き込んだログの後に追加で2メッセージが書き込まれていることが確認できます。

これで2パターンのログ書き出しクラスを自作することができました。
あとはこれを自分のアプリに追加すればよいだけになります。


さいごに

今回は2種類のログ書き出しクラスを作成してみました。
一般的にNuGetなどで取得できるライブラリに比較すると機能が足りていなかったりしますが、最低限の機能はつけられたと思います。
作成しておいてなんですが、ContinuousLoggerの方が使いやすい気がしています。

デバックなどにも利用できるので、ぜひ参考にしてみてください。
今回はこのへんで、ではまた!

コメント

このブログの人気の投稿

PowerAppsで座席表を作成する

Power AutomateでTeamsのキーワードをトリガーにする

Power Automateで文字列抽出