Wednesday, February 5, 2014

A simple logger class for Unity projects


Today I thought I'd share a simple utility that you can drop into your own projects to help manage your debug (or release!) logs at run-time.

Unity's included Debug class has some great functionality for logging, raising warnings, and raising errors. Over the course of a project, however, regular use of these functions can lead to a very noisy log, which can make debugging tedious.

With that in mind, I set out to create a custom logger with the following requirements:
  • quick to drop into a new project
  • easy for a person to use for the first time
  • easily filterable at run-time
  • zero overhead outside of DEBUG builds

To address the first two constraints, I made the Logger class a static singleton that creates itself whenever any of the public logging functions are invoked. This also means that a new user simply needs to register their logging channel and start logging to start using the Logger in their project. Here's a simple example:

  Logger.register_channel(Log_Channel.Combat, Logger.E_Level.Verbose);

  Logger.log(Log_Channel.Combat, "Really super important combat event!");

By using Unity's Object.DontDestroyOnLoad() function on the Logger object, I guarantee that it will stick around for the lifetime of the game, even across level loads, unless you explicitly destroy it. This keeps lifetime management simple as well.

To be able to modify the filters at run-time, I set up a simple inspector (Logger_Inspector.cs) for the Logger class that generates drop-down lists for each channel that has been registered with the logger.  It looks a little something like this:


From here you can set the logging level of any channel directly allowing you to filter out exactly what you want, when you want it.

Finally, to make sure that none of our logging statements have any impact on our final release builds, every publicly accessible member function of the Logger class makes use of the System.Diagnostics.Conditional attribute.  Here's an example of what this looks like:

  [System.Diagnostics.Conditional("DEBUG")]
  public static void log(string channel, string message)
  {
    ...
  }

When the DEBUG flag is unset during compilation, none of the public Logger functions will be compiled and calls to these functions will become empty statements (or no-ops). This translates to zero memory and processing overhead in non-DEBUG builds, which makes programmers everywhere happy. Hats off to the .NET and Mono guys!

So that's about it. There's still a good bit of room for extension here: adding different log output streams, adding hierarchical filters, or anything else your heart may desire. You can find all the code and a containing .unityPackage file here in our public BitBucket repo.

I hope this code helps you out in your projects. Let us know what you think!

No comments:

Post a Comment