【C#】ちょっと確認したい時に使える手抜きロガーを書いた

# 0. INDEX
- 概要
- ちょっと確認したい時に使える手抜きロガーを書いた
- あとがき

# 1. 概要
Excelアドインの開発をやってると、コンソール出力が一筋縄にはいかず、かといってlog4netなどの本格的なロガーを入れても設定ファイル周りでちょっと不便に思う事がある。
そもそも、そんな御大層なものを入れるまでもなく、社内システムの開発中だけ使える程度のお手軽なのが欲しかったので書いた。

# 2. ちょっと確認したい時に使える手抜きロガーを書いた

## 2.1. 動作イメージ
https://paiza.io/projects/wItSeWuNHzgQX6DBGKxi4A

## 2.2. ソース
オリジナルはこっちで。（[GitHub](https://github.com/mahny-b/JunkLogger)）

コメント入れるとものすごく長くなるので記事では適用に削ってます。

```cs:Logger.cs
using System;
using System.Text;
using System.IO;
using System.Runtime.CompilerServices;

class Logger
{
    private enum LogLevel
    {
        Trace,
        Debug,
        Info,
        Warn,
        Error,
        Fatal,
        /// ログ出力をしない
        None,
    }
    /// 簡易的なものだし、ファイルハンドラ取り合っても面白くないのでシングルトンですよ
    private static Logger Instance = new Logger();

    /// ログレベル
    private readonly LogLevel Level;

    /// ログファイルパス
    private readonly string LogFilePath;

    /// <summary>
    /// 疑似的なロギングを行うユーティリティ
    /// </summary>
    private Logger()
    {
        // ★設定はここで自由に書き換えてくだし
        this.Level = LogLevel.Debug;

        // ログ出力先。デフォルトではデスクトップに出力します
        string LogDirPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
        LogFilePath = Path.Combine(LogDirPath, "Application.log");
    }

    private static void Output(LogLevel Level, string Message, string AssemblyName, string Method, int Line)
    {
        if (Level < Instance.Level)
        {
            // 設定レベル未満は出力しない。Noneは最強なので常に出力しない
            return;
        }
        string FilePath = string.IsNullOrEmpty(AssemblyName) ? AssemblyName : Path.GetFileName(AssemblyName).Replace(".cs", "");
        string Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
        string LevelStr = Level.ToString().ToUpper();

        // ".ctor"はコンストラクタで拾ってしまう文字列。滅殺
        string MethodStr = Method == ".ctor" ? "<Init>" : Method;
        string Contents = $"{Timestamp} [{LevelStr,-5}] ({FilePath}#{MethodStr}:{Line,3}) - {Message}{Environment.NewLine}";

        if (!File.Exists(Instance.LogFilePath))
        {
            StringBuilder Usage = new StringBuilder("コマンドプロンプトで下記コマンドを実行するとログ追記を監視出来ます。").Append(Environment.NewLine)
                .Append($"powershell -c \"Get-Content -Path \'{Instance.LogFilePath}\' -Wait -Encoding UTF8 -Tail 5\"").Append(Environment.NewLine);
            File.AppendAllText(Instance.LogFilePath, Usage.ToString());
        }
        File.AppendAllText(Instance.LogFilePath, Contents, Encoding.UTF8);
    }

    public static void Trace(string Message = "", [CallerFilePath] string AssemblyName = "", [CallerMemberName] string Method = "", [CallerLineNumber] int Line = 0)
    {
        Output(LogLevel.Trace, Message, AssemblyName, Method, Line);
    }

    public static void Debug(string Message = "", [CallerFilePath] string AssemblyName = "", [CallerMemberName] string Method = "", [CallerLineNumber] int Line = 0)
    {
        Output(LogLevel.Debug, Message, AssemblyName, Method, Line);
    }

    public static void Info(string Message = "", [CallerFilePath] string AssemblyName = "", [CallerMemberName] string Method = "", [CallerLineNumber] int Line = 0)
    {
        Output(LogLevel.Info, Message, AssemblyName, Method, Line);
    }

    public static void Warn(string Message = "", [CallerFilePath] string AssemblyName = "", [CallerMemberName] string Method = "", [CallerLineNumber] int Line = 0)
    {
        Output(LogLevel.Warn, Message, AssemblyName, Method, Line);
    }

    public static void Error(string Message = "", [CallerFilePath] string AssemblyName = "", [CallerMemberName] string Method = "", [CallerLineNumber] int Line = 0)
    {
        Output(LogLevel.Error, Message, AssemblyName, Method, Line);
    }

    public static void Fatal(string Message = "", [CallerFilePath] string AssemblyName = "", [CallerMemberName] string Method = "", [CallerLineNumber] int Line = 0)
    {
        Output(LogLevel.Fatal, Message, AssemblyName, Method, Line);
    }
}
```

## 2.3. 出力方法
Loggerクラスを任意の場所においてこんな感じに書くと…

```cs
class Program
{
    static void Main(string[] args)
    {
        Logger.Trace("Trace");
        Logger.Debug("Debug");
        Logger.Info("Info");
        Logger.Warn("Warn");
        Logger.Error("Error");
    }
}
```


既定ではデスクトップに出力し、中身はこんな感じに**タイムスタンプ、レベル、ファイル、関数、行番号、メッセージ**を出力してくれます。
既定のログレベルは**Debugなので、Traceのログは出ていない**ですね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92447/ac11780a-88eb-4968-d07d-a7a15c47a60b.png)

## 2.4. 設定方法
簡易ロガーなので、Loggerクラスにハードコーディングします。
もし、更なる柔軟性を求めるならlog4net等の本格派ロガーの導入を検討してください。

```cs
private Logger()
{
    this.Level = LogLevel.Debug; // この列挙型を書き換える。Noneだと出力無し

    string LogDirPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); // 出力フォルダ
    LogFilePath = Path.Combine(LogDirPath, "Application.log"); // 出力ファイル名
}
```


# 3. あとがき
今回は、Excelアドイン等ちょっと特殊な、でも本格システムというには軽めの開発でちょこっと使えるロガーを書いてみました。
最初はlog4netを入れたのですが、log4net.configの取り回しとの相性がちょっと悪く（単に私の知識不足だと思いますが…）、調査に時間を取るくらいなら**「もういいや！面倒クセェ！！作っちまえ！！１」**とキレた結果でございました。

行番号等を取るのに`[CallerLineNumber]`とか書くんだーと知らない知識が多かったので結果的に良かったです。
（ちなみに最初はStackTrace経由かなと思ってちょっとハマってましたｗ）

以上ですﾉｼ