The best bet, taking into account performance considerations, is to simply have a means of wrapping up debug code in a way that can be "compiled out" as best as possible, and then left in as run-time selectable if not compiled out.
In C/C++ this generally means a macro (or a set of macros for different specific needs) that wraps calls to debug functions that internally check debug flags. In a language like C#, you can use the [Conditional()] attribute and the run-time debug flags internally.
In languages like JavaScript, which it appears you might be using, you can fall back on good ol' functional composition for the run-time behavior, and simply including different .js files for the "release mode" vs "debug mode" implementation.
Note that this is not object-oriented. I don't think you're actually looking for an object-oriented approach at all, though.
Consider a debug script like so:
// debugtools-release.js
function debug(expr) {}
// debugtools-debug.js
function debug(expr) {
if (window.debug_on && expr instanceof Function)
expr.call(this);
};
You can now wrap up any debug logic you want like so:
do_something();
debug(function(){
console.log("Did something");
graphics.drawDebugLine(foo.x, foo.y, bar.x, bar.y, Color.Yellow);
++some_debug_counter;
});
do_something_else();
All of that debug code will be skipped if debugging is turned off. Better, modern JS compilers might completely remove any overhead involved, giving you similar performance to using macros in C++. (I'm not sure if they do or not, you'd have to test to be sure.)
For simpler cases, like just printing out a console log message, you can of course create helpers for those cases. Something like:
// debugtools-release.jse
function debug_msg(msg) {}
// debugtools-debug.js
function debug_msg(msg) {
if (window.debug_on)
console.log.apply(this, arguments);
};
If you're of the mind, you could combine both the debug and debug_msg functions by checking argument types and the like (though I prefer having clearly differentiated functions for differing behavior myself):
function debug(expr) {
if (window.debug_on) {
if (expr instanceof Function)
expr.call(this);
else
console.log.apply(this, arguments);
}
}
The general functional composition technique works equally well for other languages that lack explicit compiler support for optionally disabling code and which have lambda/anonymous functions of some form. I've even used it in C++11 code, though I'm still unsure whether I prefer that or the macro approach (and for games, since we generally have to support older compilers, the C++11 approach is just not yet feasible).