Improve Unity’s Logging with a Simple Utility Class
Updated May 4, 2020. The first version of this post had a minimal class to show the concept and left completion of the class to the reader. It raised more questions than it answered for beginner developers so I developed the logger utility further and explained it in more detail now. You can download and use the completed class from it github repository.
Today we are going to explore a simple yet powerful utility class. I use it in almost all my Unity projects. Let’s start by looking at what gets printed in Editor console when we use the extension method from the class:
On top of the “Hello World!” log message, we now get ThreadID, class name, object’s name and game time. These 4 pieces of information are what I have found useful for my projects, but for each project, you can modify the list depending on your needs.
The source code of the project is available in its github repository.
Including it in your projects is as easy as putting UnityObjectLoggerExt.cs
file under the Assets
folder of your Unity project. Now you can try this.LogLog
, this.LogWarn
and this.LogError
from within your MonoBehaviours by adding using NoSuchStudio.Common
to the top of your file. The method becomes available through C#’s extension methods.
A few notes about the utility class
- If you are lazy like me and don’t want to type
this.
and just typeLogLog("Hello World!")
, you can subclassMonoBehaviourWithLogger
instead ofMonoBehaviour
.
- The method automatically sets the context for the log message. Clicking on the messages logged by
this.LogLog
or its variantsLogWarn
andLogError
in Editor will select the issuing object from the hierarchy view. - The methods support formatting even though it is not included in their names. So you can write the following just fine:
LogLog("enemy count {0}", ec);
What’s the big deal?
All the extra information our extension method prints out are available to us when we want to write a log command. Why not just include them in our call to Debug.Log
like below?
Debug.LogFormat(LogType.Log, this, "{0}{1}{2}{3} Hello World!", Thread.CurrentThread.ManagedThreadId, Time.time, GetType().Name, name)
Brevity
The obvious reason is how much less we’d have to type. Compare this to the one above:
this.LogLog("Hello World!");
Have a Common Path for Our Logs
The main reason is however not brevity! Let’s assume we have been using Debug.Log
for all our logs. Then we hit a concurrency bug. Even though we are logging messages in various places in our code, we don’t have the thread id for those log messages. Here is the problem! To add the thread id to our log messages we have to modify every relevant line of code that contains Debug.Log
. It could mean we’d have to modify dozens of lines in multiple files!
Having a common code path for log messages lets us add / remove common tags to our ALL our log messages easily. With this utility class, we would add the thread id only to a single file and be done with out change. Later on we could remove the changes to go back to just printing the message provided to the call.
Filter Generation of Log Messages
UnityObjectLoggerExt
enables us to set the log level per class. We can set the log level for messages logged from MyType
class with a line of code like this:
UnityObjectLoggerExt.GetLoggerByType<MyType>().logger.filterLogType = LogType.Error;
It is nice to be able to hide log messages from classes that have been thoroughly tested. You would normally delete or comment out the Log lines from those files. Later to track down a bug you might have to come back and re-enable them. If you get used to managing the logs with the filters like above, you won’t have to worry about messing with the log lines in your code.
End Note
Hope you find this useful for your debugging and organizing the logs in your Unity code. I am a software engineer turned game developer. Follow me to learn more about programming with C# and making games with Unity.
Happy Coding!