Categories
Code Development

Creating a Custom Debugger for Unity

One of the most important things that I’ve learned as a developer is that debug and trace statements are an invaluable tool. Although Unity’s default debugger works fine and even supports rich-text and custom styling out of the box, I wanted to take a few hours and make it work better for me. Specifically I want to implement consistent color schemes and text treatments for the different log levels, add some standard messaging, and package this into something I can port to future projects. Now I am not looking to replace what Unity already provides, what I am going to do is add a set of helper methods that accomplish the following things:

Visibility by way of Pretty Colors

Here is an example of some basic debug statements in Unity for reference

using UnityEngine;

public class GameManager : MonoBehaviour
{
    void Start()
    {
        Debug.Log("Log Test");
        Debug.LogWarning("Warn Test");
        Debug.LogError("Error Test");
        Debug.LogException(new System.Exception("Oh noes!"));
    }
}
Unity console with default debug statements
Functional but a bit bland

I know a lot of console interfaces support custom color schemes but it’s not something I typically take advantage of due to time constraints or sheer laziness. A red error message is much easier to pick out when scanning through a console log a few hundred lines deep while you’re kicking yourself trying to figure out what broke your latest build. This project is all about improving my process so lets get on the right page from the jump.

Consistent Messages

When I look at a logging statement I really want to know a few key things:

  • What is the severity? Intended or unexpected?
  • What is the message? Well…yeah, kinda the point here.
  • Where did it come from? Class, method, line number!

We can addressed the first point with our strategic use of color and the second one is a given. The third point however can be accomplished a number a ways and over the years I’ve implemented a few of them from passing in a string value (please don’t), using reflection, and walking up the stack trace. Don’t get me wrong, they worked for their intended purposes but not without compromise.

Expanded details at the bottom

The information I want is already visible in the Unity console when you select a logging statement but I would rather have it available at a glance. Although this may only save me a fraction of a second, it adds up very quickly over hundreds of hours of development.

This implementation is going to use some attributes made available in the System.Runtime.CompilerServices assembly. I poked around on Stack Overflow, as I usually do when looking for advice, and found that this method not only addresses my issues but is also very performant.

How I went about accomplishing this

I created a managed plugin for Unity that is a .NET Standard 2.0 class library. I plan to grow this library as I do more development but for now it contains a single static class named Trace. Here is a sample from that class that demonstrates what I am doing:

using UnityEngine;
using System.Runtime.CompilerServices;
using System.IO;

namespace BAUnity
{
    public static class Trace
    {
        public static int FontSize = 12;
        private const string COLOR_INFO = "#56d6bf";

        // Info
        public static void Info(
           object message,
           [CallerMemberName] string memberName = "",
           [CallerFilePath] string sourceFilePath = "",
           [CallerLineNumber] int sourceLineNumber = 0)
        {
            Debug.Log(
                ApplyStyles(
                    FormatCallerAttributes(sourceFilePath, memberName, sourceLineNumber),
                    message,
                    COLOR_INFO));
        }

        private static object ApplyStyles(string context, object message, string color)
        {
            object log =
                "<size=" + FontSize.ToString() + ">"
                + "<color=" + color + ">"
                + "<b>[" + context + "]</b>: "
                + message
                + "</color>"
                + "</size>";

            return log;
        }

        // Concatenate and format the caller attributes string
        private static string FormatCallerAttributes(string path, string member, int line)
        {
            return Path.GetFileNameWithoutExtension(path) + ":" + member + ":" + line;
        }

    }
}

The caller attributes are passed in automatically so that really only leaves the actual message that I want to log. I am taking those attributes and formatting them into something that tells me what I want to know at a glance. I am then combining that piece and the message and wrapping them in some style tags that Unity can understand. The final step is to pass that to the Unity Debug.Log method.

There is nothing that complicated going on here but the results, I feel are worth the extra effort:

using UnityEngine;
using BAUnity;

public class GameManager : MonoBehaviour
{
    void Start()
    {
        Trace.Start();
        Trace.Log("Log Test");
        Trace.Info("Info Test");
        Trace.Warn("Warn Test");
        Trace.Error("Error Test");
        Trace.Exception("Oh noes!");
        Trace.End();
    }
}

The library can be found on my GitHub page linked here or in the footer.