0. Introduction
Debug.Log(), the first thing you would think of when you want to print something in console. Later, you find out that it also writes information to player's local log file, which is cool, you can now tell a player that has encountered an issue to send you that log file. 
Everything's working fine, until one day you take a peek at the profiler. (If you haven't, you should do it now).
That Debug.Log is taking considerable time, towing your frame rate under 30fps.
It isn't surprising, because it writes to a file, I/O is always slow. What's more, remember the extra text, aka Stack Trace that is under each debug message? Extracting stack trace also takes time, and  that is also extra content you need to print out, when you just want to print something simple.
Now, how to reduce the impact of Debug.Log ?
I. The Straightforward
The easiest thing you could do is to wrap your Debug.Log invocation with #if UNITY_EDITOR and #endif. This way, the message stays in the Editor, in a built game those invocations are gone.
But it is tedious because you have to remember doing this every time you invoke Debug.Log. 
Now if you are just starting your project, you could create your own debug class and remember to use your own version. It could be something like this:
public static class CustomDebug{
        public static void Log(string message){
                #if UNITY_EDITOR
                Debug.Log(message);
                #endif
        }
}
The charm of this approach is that if you want to do more filtering or something else, you could always modify the custom class. The caveat, is of course, you have to do this pre-emptively, otherwise replacing every single invocation is not going to be fun. 
II. The Foxy
If you didn't know how evil Debug.Log before reading this, you might already be screaming. "I am using Debug.Log all over my project, and I really, really don't want to modify every single one of them, is there a way out?"
There actually is. 
If you decompile Debug class, you can find a public static ILogger unityLogger, and Debug.Log actually invokes the methods in this unityLogger. The sad thing is that it doesn't have a setter, so you can't just alter it by supplying a custom ILogger.
But wait, if you inspect Logger, you will find it actually uses ILogHandler's implementation to determine how logging works.
And...this thing has a setter! So you can swap in your custom implementation of ILogHandler to alter the behavior of all your Debug.Log calls. The Logger class also expose logEnabled and filterLogType so that you can turn off log or only print out critical errors, even if you don't swap out the default ILogHandler implementation.
III. The Strange
Now if you want to keep the default behavior but turn off stack trace, things will become trickier. 
In the default implementation of ILogHandler, there actually is a place you could supply LogOption, which, conveniently, has LogOption.NoStackTrace. But that gets defaulted to LogOption.None. So if you don't do some sneaky thing, you can't turn stack trace off, because those methods are internal, and the class is sealed.
And by sneaky thing, I mean Reflection.
The basic idea is to create a custom class, which stores the default implementations, and before calling these default implementations by cached reflection delegates, check if we need to enable stack trace.
Here is an example of a custom ILogHandler that, still keeps the default behavior but checks if stack trace is enabled:

Don't just take this snippet as-is, this is just for demonstration, you could do better.

With a little bit more effort you can turn off stack trace by log level, and do all sorts of crazy stuff while keeping the default one. 
Back to Top