My previous post about conditional logging received quite a few responses, with several people pointing out that littering your code with #ifdef DEBUG statements is both ugly and error prone. Karl Kraft, for example, created a new DebugLog class as a drop-in replacement for NSLog.
Since NSLog has been around for quite a while, there have been many solutions to this problem.
CocoaDev suggests this macro:
#if MY_DEBUG_FLAG #define DEBUG_OUTPUT( a ) NSLog( a ) #define DEBUG_OUTPUT1( a, b ) NSLog( a, b ) #else #define DEBUG_OUTPUT( a ) // (a) #define DEBUG_OUTPUT1( a, b ) // (a,b) #endif
Enumerating all the variants with different number of parameters gets a bit tedious, so with Objective-C 2.0 C99 you can use varargs macros:
#define DEBUG_OUTPUT(fmt, ...) NSLog(fmt, ## __VA_ARGS__)
As described by Fraser Hess in Dropping NSLog in release builds you can put this in your <AppName>_Prefix.pch file:
#ifdef DEBUG # define DLog(...) NSLog(__VA_ARGS__) #else # define DLog(...) /* */ #endif #define ALog(...) NSLog(__VA_ARGS__)
Use ALog for cases where you always want log output regardless of the state of the debug flag. I like this preprocessor macro approach because you don’t have to carry around extra classes in all your projects. Just a few lines of code to paste into one file in your project.
But I think we can improve it further.
The built-in macro __PRETTY_FUNCTION__ is pretty nifty in that it outputs the name of the class and the method where it’s running. Mark Damon Huges uses this in his Cocoa logging macro:
#ifdef DEBUG // DLOG takes a format argument (which must begin %s) and 0 or more args: // DLOG(@"%s"); // DLOG(@"%s: %d", x); #define DLOG(fmt, ...) NSLog(fmt, __PRETTY_FUNCTION__, ##__VA_ARGS__) #else #define DLOG(...) #endif
A drawback with this macro is that it requires you to add a “%s” to all your log statements, e.g. DLOG(@”%s foo”); instead of NSLog(@”foo”);. So it’s not a drop-in replacement for NSLog.
Let’s try to fix that.
#define DLog(fmt, ...) NSLog((@"%s " fmt), __PRETTY_FUNCTION__, ##__VA_ARGS__);
Now you can do DLog(@”foo”); as well as pass in parameters like DLog(@”value: %d”, x);
One final touch is to add the line number:
#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
Now if you’re lazy and you want to know that execution has reached a particular line, you can just use DLog();
So here is the complete listing of the macro:
// DLog is almost a drop-in replacement for NSLog // DLog(); // DLog(@"here"); // DLog(@"value: %d", x); // Unfortunately this doesn't work DLog(aStringVariable); you have to do this instead DLog(@"%@", aStringVariable); #ifdef DEBUG # define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); #else # define DLog(...) #endif // ALog always displays output regardless of the DEBUG setting #define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
Set the DEBUG variable in your project as described previously. Update: If your project is 3.0 you may need to set the DEBUG variable this way.
I’m sure there will be further improvements to this. Comments are open as always.