【Unity】デバッグログをカスタマイズする

Unity標準のデバッグログ出力は、次のようなコードで行う事が多いでしょう。

Debug.Log("デバッグログ");

Unityコンソールには以下のように出力されます。

これを以下のようにカスタマイズする事を本記事の目標とします。

スポンサーリンク

デバッグログ出力の実体

Unity標準のデバッグログ出力は、次のプロパティに格納されたオブジェクトが行っています。

Debug.unityLogger.logHandler

ここでlogHandlerはILogHandler型です。ILogHandlerはインタフェースクラスで、以下2つのメソッドを持っています。

public interface ILogHandler
{
    void LogFormat(LogType logType, Object context, string format, params object[] args);
    void LogException(Exception exception, Object context);
}

LogFormatメソッドには、Debug.Logが呼ばれたときに行う処理を定義します。
LogExceptionメソッドには、例外処理が発生した時に行う処理を定義します。

logHandlerプロパティには、あらかじめILogHandlerを実装したインスタンスが格納されています。

このlogHandlerは書き換え可能なプロパティなので、ILogHandlerを実装した独自クラスのインスタンスを指定すればログ出力処理を自在に書き換える事ができます。

ログ出力のコールバック

Debug.Log()呼び出しによるメッセージは、コールバックから検知することができます。

Application.logMessageReceived
Application.logMessageReceivedThreaded

両者の違いは、Application.logMessageReceivedメインスレッドからのDebug.Log呼び出しのみの検知、Application.logMessageReceivedThreadedメイン以外のスレッドからのDebug.Log呼び出しも検知できる点です。

例えば、

  • uGUIへのテキスト出力などUnityオブジェクトにアクセスする場合はApplication.logMessageReceived
  • ファイル書き出しなど別スレッドでも問題ない場合はApplication.logMessageReceivedThreaded

といった具合に用途によって使い分けるのが良いでしょう。

Debug.unityLogger.logHandlerを置き換える方法と違い、既存の処理をそのままに独自の処理を付加したい場合は、コールバックを受け取って処理する方法がお手軽です。

デバッグログのカスタマイズ例

以上を踏まえて、デバッグログをカスタマイズする例をご紹介します。

メッセージを加工する

出力するメッセージの前にログタイプの文字列を付加してみたいと思います。

メッセージの加工は、ログ出力処理を完全に置き換える事になるため、Debug.unityLogger.logHandlerプロパティに独自のログ出力オブジェクトで上書きする必要がありそうです。

ただし、Unityコンソールに出力する部分はそのまま流用したいので、加工したメッセージを元のlogHandlerオブジェクトに渡すようにします。

実装例

まず、Debug.unityLogger.logHandlerに設定するためのILogHandlerインタフェースを実装したクラスを作成します。

CustomLogHandler.cs
using System;
using UnityEngine;

public class CustomLogHandler : ILogHandler
{
    private readonly ILogHandler _logHandler;

    public CustomLogHandler(ILogHandler logHandler)
    {
        _logHandler = logHandler;
    }

    public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args)
    {
        _logHandler.LogFormat(logType, context, $"[{logType}] {format}", args);
    }

    public void LogException(Exception exception, UnityEngine.Object context)
    {
        _logHandler.LogException(exception, context);
    }
}

コンストラクタで元のILogHandlerオブジェクトを渡せるようにして、内部的に保持しておきます。
そして、LogFormatメソッドのformatの先頭にログタイプの文字列を付加し、これを元のオブジェクトのLogFormatメソッドに渡します。

Debug.unityLogger.logHandlerの書き換えは、MonoBehaviourスクリプトで行うようにすれば良いでしょう。

LogModifier.cs
using UnityEngine;

public class LogModifier : MonoBehaviour
{
    private ILogHandler _defaultLogHandler;
    private ILogHandler _logHandler;

    private void Awake()
    {
        _defaultLogHandler = Debug.unityLogger.logHandler;
        _logHandler = new CustomLogHandler(_defaultLogHandler);
        Debug.unityLogger.logHandler = _logHandler;
    }

    private void OnDestroy()
    {
        if (_defaultLogHandler != null)
            Debug.unityLogger.logHandler = _defaultLogHandler;
    }
}

これをLogModifier.csとして保存し、適当なゲームオブジェクトにアタッチします。

最後に、適当なログを出力するスクリプトTestLog.csを作成して、こちらもゲームオブジェクトにアタッチして準備完了です。

TestLog.cs
using UnityEngine;
public class TestLog : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("Test Log!");
        Debug.LogWarning("Test Warning!");
        Debug.LogError("Test Error!");
        throw new UnityException("Test Exception!");
    }
}

実行結果

ログの前にログタイプの文字列が付加されました!

uGUIキャンバスにログ出力

今度は、uGUIキャンバスのテキストにログ内容を出力してみたいと思います。

これは元のログ出力はそのままに追加の処理を行いたいため、Application.logMessageReceivedが望ましいでしょう。Application.logMessageReceivedThreadedを使わないのは、uGUIテキストはメインスレッドからしかアクセスできないので、別スレッドのコールバックを拾うと不都合が起きるためです。

実装例

uGUIテキストにログメッセージを追加する処理を登録するスクリプトを用意します。

CanvasLogger.cs
using System;
using UnityEngine;
using UnityEngine.UI;

public class CanvasLogger : MonoBehaviour
{
    [SerializeField] private Text _text;

    private void Awake()
    {
        Application.logMessageReceived
            += (condition, trace, type)
                => _text.text += condition + Environment.NewLine;
    }
}

これをCanvasLogger.csとして保存し、ゲームオブジェクトにアタッチします。
そして、適当なCanvasとTextを作成し、CanvasLoggerにTextにアタッチします。

実行結果

キャンバスにログが出力されるようになりました!

まとめ

今回はログ出力をカスタマイズするほんの一例をご紹介しました。
カスタマイズする手段が公式に用意されているため、方法さえ分かれば様々な応用が出来るでしょう。

参考サイト

スポンサーリンク