Unity C# でメソッド名やクラス名を簡単にログ出力する方法

概要

Unityでは、swift等の他の言語にあるような「FILE」や「LINE」に相当するものはプリプロセッサが無いため、利用できません。
その為、例えば、メソッドがどのタイミングでコールされたかを埋め込むには以下のように記載しなければなりません。

Debug.Log("[" + this.GetType().FullName + "] " + System.Reflection.MethodBase.GetCurrentMethod().Name);

上記の実行結果は、例えば「AdManager」というクラスの、「RequestBanner」メソッドで記載すれば以下の様に出力されます。

[RequestBanner] AdManager

これで、やりたい要件は満たせては居るのですが、毎回このように記載するのは冗長なので、なんとか良いやり方は無いか?が今回のお題です。

MonoBehaviourの基底クラスを作って、StackFrameを使う

基底クラスを作成出来るのであれば、基底クラスにログを仕込んでそれを継承して利用することが出来ます。
後述のstaticと違うところは、わざわざクラス名(もしくは名前空間)を意識しなくても利用できる所です。

public class BaseMonoBehaviour : MonoBehaviour {
    /// <summary>
    /// 現在のClass名, Method名をログ出力します。
    /// 引数を付けた場合は、引数の中身を文字列として出力します。
    /// </summary>
    /// <param name="logMessage">ログ出力したい場合は文字列を指定</param>
    protected void LogCurrentMethod(string logMessage = "") {

        // 1つ前のフレームを取得
        System.Diagnostics.StackFrame objStackFrame = new System.Diagnostics.StackFrame(1);
 
        // 呼び出し元のメソッド名を取得する
        string methodName = objStackFrame.GetMethod().Name + "()";

        string msg = "";
        if (logMessage != "") {
            msg = " / [LogMessage] " + logMessage;
        }

        Debug.Log("*** [" + this.GetType().FullName + "] " + methodName + msg + " ***");
    }
}

上記を継承した、先で以下のように呼ぶだけで要件を満たせるようになります。

LogCurrentMethod();  // クラス名とメソッド名だけで良い場合
LogCurrentMethod("hoge"); // 上記と一緒にメッセージも表示したい場合

※usingディレクティブに敢えて「using System.Diagnostics」を指定していません。
指定してしまうと、このクラスを継承したクラスで「Debug.Log」を記載しようとした際に、「UnityEngine.Debug.Log」と指定しないといけなくなるためです。

上記の出力結果は以下です。

*** [AdManager] RequestBanner()***
*** [AdManager] RequestBanner() / [LogMessage] hoge ***

基底クラスが作れない場合は、static methodから呼ぶ

上述の基底クラスをstaticとして抜き出して実現します。
static化するにあたり、基底クラスと違うところは、クラス名をUnityEngineの

this.GetType().FullName

が使えない為、class名に関してもStackFrameから取得するようにします。
今回はUtilityクラスにstaticなログメソッドを宣言します。

class Utility {
    /// <summary>
    /// 現在のClass名, Method名をログ出力します。
    /// 引数を付けた場合は、引数の中身を文字列として出力します。
    /// </summary>
    /// <param name="logMessage">ログ出力したい場合は文字列を指定</param>
    public static void LogCurrentMethod(string logMessage = "") {

        // 1つ前のフレームを取得
        System.Diagnostics.StackFrame objStackFrame = new System.Diagnostics.StackFrame(1);
 
        // 呼び出し元のメソッド名を取得する
        string methodName = objStackFrame.GetMethod().Name + "()";

        // 呼び出し元のクラス名を取得する
        string className = objStackFrame.GetMethod().ReflectedType.FullName;

        string msg = "";
        if (logMessage != "") {
            msg = " / [LogMessage] " + logMessage;
        }

        Debug.Log("*** [" + className + "] " + methodName + msg + " ***");
    }
}

利用方法は以下の通りです。

Utility.LogCurrentMethod();

LogメソッドをWarning, Errorにも対応する

更に使い勝手を良くしてみましょう。
これまではInfo系ログだけ用意しましたが、ErrorやWarningも用意します。
上記で作成したメソッドにラッパーメソッドを用意して実現します。
※以下は、staticのケースでコードを載せていますが、基底クラスの場合もやり方は同じです。

public class Utilitys : MonoBehaviour {

    enum LogKind {
        info, warning, error
    }

    /// <summary>
    /// 現在のClass名, Method名をログ出力します。
    /// 引数を付けた場合は、引数の中身を文字列として出力します。
    /// </summary>
    /// <param name="logMessage">ログ出力したい場合は文字列を指定</param>
    public static void LogCurrentMethod(string logMessage = "") {
        Utilitys.OutputLogMethod(LogKind.info, logMessage);
    }

    public static void LogCurrentMethodWarning(string logMessage = "") {
        Utilitys.OutputLogMethod(LogKind.warning, logMessage);
    }

    public static void LogCurrentMethodError(string logMessage = "") {
        Utilitys.OutputLogMethod(LogKind.error, logMessage);
    }

    static void OutputLogMethod(LogKind kind, string logMessage = "") {
        // 2つ前のフレームを取得 (ラッパー経由の為2つ遡る)
        System.Diagnostics.StackFrame objStackFrame = new System.Diagnostics.StackFrame(2);
 
        // 呼び出し元のメソッド名を取得する
        string methodName = objStackFrame.GetMethod().Name + "()";

        // 呼び出し元のクラス名を取得する
        string className = objStackFrame.GetMethod().ReflectedType.FullName;

        string msg = "";
        if (logMessage != "") {
            msg = " / [LogMessage] " + logMessage;
        }

        string outputLogString = "*** [" + className + "] " + methodName + msg + " ***";

        switch (kind)
        {
            case LogKind.warning:
                Debug.LogWarning(outputLogString);
            break;
            case LogKind.error:
                Debug.LogError(outputLogString);
                break;
            default:
                Debug.Log(outputLogString);
                break;
        }
    }

}

最後に

2つのやり方を紹介しましたが、全てのクラスがMonoBehaviourを継承出来ない事を考えるとstaticに宣言して利用するのが汎用性が高いかもしれません。