How to print out the method name and line number and conditionally disable NSLog?
Asked Answered
T

13

452

I'm doing a presentation on debugging in Xcode and would like to get more information on using NSLog efficiently.

In particular, I have two questions:

  • is there a way to easily NSLog the current method's name / line number?
  • is there a way to "disable" all NSLogs easily before compiling for release code?
Turnout answered 9/6, 2009 at 9:44 Comment(1)
first question where favorites (star) are more then upvotes... +1..Vaporific
R
598

Here are some useful macros around NSLog I use a lot:

#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__)

The DLog macro is used to only output when the DEBUG variable is set (-DDEBUG in the projects's C flags for the debug confirguration).

ALog will always output text (like the regular NSLog).

The output (e.g. ALog(@"Hello world") ) will look like this:

-[LibraryController awakeFromNib] [Line 364] Hello world
Rivera answered 9/6, 2009 at 10:37 Comment(15)
Why do you have a ##? I thought that it was for gluing arguments together, but you aren't gluing to anything.Gibbons
This prevents possible macro expansion of the argumentsRivera
That can happen with macros in general; some macros yield multiple lines. Just another argument to always use braces ;-).Rivera
great and cocos2d api has the similar log statement.Gnarl
How is it that (@"%s [Line %d] " fmt) causes the fmt to be appended to the control string? I haven't seen this syntax other than is this debug macro.Thoron
@zoul what if the compiler doesn't? I see some people defining DLog(...) as {} or if (0) Conway
I'm late to this conversation, but I don't like macros with trailing ; characters, so much so that I've downvoted your comment only because of this. It is both simpler and more communicative to make DLog look like EVERY OTHER STATEMENT in c/c++/obj-c and require a trailing ; on its use. Doing this makes DLog() and NSLog() that much more interchangeable (as it stands, if you lexically replace all DLog's with NSLog, you're going to have to add back in all those ;s that you avoided entering and that's just wrong)Risinger
(ok, I wanted to downvote it, but I don't have enough rep so think of this as a verbal downvote ;))Risinger
Why not place the macro into a 'do{ ... } while(NO)'? This would prevent nasty bugsBreathtaking
This code causes an unused variable error in Xcode 5.1 if not running in DEBUG :(Teuton
@Teuton this is intended. Use #ifdef DEBUG to declare your variable if it's only needed in DEBUG. Or ignore the warning.Replevy
What is the best way to implement this in Xcode 6 projects given that pch files are no longer implemented by default? And the issues raised in this thread #24159148Hone
I don't know why, but in my case I kept getting this build error:Called object of type NSString is not a function or function pointer So I had to remove the parenthesis enclosing the fmt expression: #define ALog(fmt, ...) NSLog(@"%s [Line %d] " fmt, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)Bordure
any example for swift?Sally
This is by far, the most useful tip I have ever found in my years of iOS development. Now, is there a way to remove the <Warning>: test in front of it?Scruffy
N
142

I've taken DLog and ALog from above, and added ULog which raises a UIAlertView message.

To summarize:

  • DLog will output like NSLog only when the DEBUG variable is set
  • ALog will always output like NSLog
  • ULog will show the UIAlertView only when the DEBUG variable is set
#ifdef DEBUG
#   define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#   define DLog(...)
#endif
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#ifdef DEBUG
#   define ULog(fmt, ...)  { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"%s\n [Line %d] ", __PRETTY_FUNCTION__, __LINE__] message:[NSString stringWithFormat:fmt, ##__VA_ARGS__]  delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; }
#else
#   define ULog(...)
#endif

This is what it looks like:

Debug UIAlertView

+1 Diederik

Nejd answered 10/8, 2011 at 20:57 Comment(4)
I'm going to extend my ALog+DLog code with ULog as well. Very useful.Fleabane
This code causes an unused variable error in Xcode 5.1 if not running in DEBUG :(Teuton
Why do some of the the #define directives end with a semicolon?Leonor
@Locutus So you don't have to put a semicolon after the DLog statement. This is useful because if you did, in release builds, DLog is compiled to nothing, and you would be left with a dangling semicolon in your code. This is not an error, but it might throw a warning, depending on your settings, if it follows another semicolon.Zondra
C
74
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

Outputs file name, line number, and function name:

/proj/cocoa/cdcli/cdcli.m 121 managedObjectContext managedObjectContext

__FUNCTION__ in C++ shows mangled name __PRETTY_FUNCTION__ shows nice function name, in cocoa they look the same.

I'm not sure what is the proper way of disabling NSLog, I did:

#define NSLog

And no logging output showed up, however I don't know if this has any side effects.

Crinite answered 9/6, 2009 at 10:32 Comment(0)
I
20

Here one big collection of debug constants that we use. Enjoy.

// Uncomment the defitions to show additional info.

//  #define DEBUG

//  #define DEBUGWHERE_SHOWFULLINFO

//  #define DEBUG_SHOWLINES
//  #define DEBUG_SHOWFULLPATH
//  #define DEBUG_SHOWSEPARATORS
//  #define DEBUG_SHOWFULLINFO


// Definition of DEBUG functions. Only work if DEBUG is defined.
#ifdef DEBUG 

    #define debug_separator() NSLog( @"────────────────────────────────────────────────────────────────────────────" );

    #ifdef DEBUG_SHOWSEPARATORS
        #define debug_showSeparators() debug_separator();
    #else
        #define debug_showSeparators()
    #endif

    /// /// /// ////// ///// 

    #ifdef DEBUG_SHOWFULLPATH
        #define debug_whereFull() debug_showSeparators(); NSLog(@"Line:%d : %s : %s", __LINE__,__FILE__,__FUNCTION__); debug_showSeparators(); 
    #else
        #define debug_whereFull() debug_showSeparators(); NSLog(@"Line:%d : %s : %s", __LINE__,[ [ [ [NSString alloc] initWithBytes:__FILE__ length:strlen(__FILE__) encoding:NSUTF8StringEncoding] lastPathComponent] UTF8String ] ,__FUNCTION__); debug_showSeparators(); 
    #endif

    /// /// /// ////// ///// 

    #define debugExt(args,...) debug_separator(); debug_whereFull(); NSLog( args, ##__VA_ARGS__); debug_separator();

    /// /// /// ////// ///// Debug Print Macros

    #ifdef DEBUG_SHOWFULLINFO
        #define debug(args,...) debugExt(args, ##__VA_ARGS__);
    #else
        #ifdef DEBUG_SHOWLINES
            #define debug(args,...) debug_showSeparators(); NSLog([ NSString stringWithFormat:@"Line:%d : %@", __LINE__, args ], ##__VA_ARGS__); debug_showSeparators();
        #else
            #define debug(args,...) debug_showSeparators(); NSLog(args, ##__VA_ARGS__); debug_showSeparators();
        #endif
    #endif

    /// /// /// ////// ///// Debug Specific Types

    #define debug_object( arg ) debug( @"Object: %@", arg );
    #define debug_int( arg ) debug( @"integer: %i", arg );
    #define debug_float( arg ) debug( @"float: %f", arg );
    #define debug_rect( arg ) debug( @"CGRect ( %f, %f, %f, %f)", arg.origin.x, arg.origin.y, arg.size.width, arg.size.height );
    #define debug_point( arg ) debug( @"CGPoint ( %f, %f )", arg.x, arg.y );
    #define debug_bool( arg )   debug( @"Boolean: %@", ( arg == YES ? @"YES" : @"NO" ) );

    /// /// /// ////// ///// Debug Where Macros

    #ifdef DEBUGWHERE_SHOWFULLINFO
        #define debug_where() debug_whereFull(); 
    #else
        #define debug_where() debug(@"%s",__FUNCTION__); 
    #endif

    #define debug_where_separators() debug_separator(); debug_where(); debug_separator();

    /// /// /// ////// /////

#else
    #define debug(args,...) 
    #define debug_separator()  
    #define debug_where()   
    #define debug_where_separators()  
    #define debug_whereFull()   
    #define debugExt(args,...)
    #define debug_object( arg ) 
    #define debug_int( arg ) 
    #define debug_rect( arg )   
    #define debug_bool( arg )   
    #define debug_point( arg )
    #define debug_float( arg )
#endif
Inextinguishable answered 4/11, 2009 at 17:35 Comment(1)
Where and how do you implement this?Euhemerism
H
20

There are a new trick that no answer give. You can use printf instead NSLog. This will give you a clean log:

With NSLog you get things like this:

2011-11-03 13:43:55.632 myApp[3739:207] Hello Word

But with printf you get only:

Hello World

Use this code

#ifdef DEBUG
    #define NSLog(FORMAT, ...) fprintf(stderr,"%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
    #define NSLog(...) {}              
#endif
Hydrophobia answered 3/11, 2011 at 15:49 Comment(0)
B
16

My answer to this question might help, looks like it's similar to the one Diederik cooked up. You may also want to replace the call to NSLog() with a static instance of your own custom logging class, that way you can add a priority flag for debug/warning/error messages, send messages to a file or database as well as the console, or pretty much whatever else you can think of.

#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, 
              [[NSString stringWithUTF8String:__FILE__] lastPathComponent], 
              __LINE__, 
              [NSString stringWithFormat:(s), 
              ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif
Ballew answered 9/6, 2009 at 12:28 Comment(1)
Because you dodged %s format specifier which Apple is trying to deprecate and avoided -Wcstring-format-directive Clang warning newly introduced in 2015.Abrahan
L
13

Disabling all NSLogs, for somebody allergic to MACROS, here is something that you can compile too:

void SJLog(NSString *format,...)
{
    if(LOG)
    {   
        va_list args;
        va_start(args,format);
        NSLogv(format, args);
        va_end(args);
    }
}

And, use it almost like NSLog:

SJLog(@"bye bye NSLogs !");

From this blog: https://whackylabs.com/logging/ios/2011/01/19/ios-moving-in-and-out-of-nslogs/

Leonoraleonore answered 19/1, 2011 at 23:21 Comment(0)
N
12

It's easy to change your existing NSLogs to display line number and class from which they are called. Add one line of code to your prefix file:

#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
Novelistic answered 2/3, 2013 at 0:29 Comment(3)
This is great! how would you do this in swift?Euhemerism
@Novelistic I like this the best. Very clean & simple. I only use NSLog. I have no idea what DLog & ULog are anyway! Thanks. Up voted...Moslem
@Novelistic I really do not understand why Apple do not add this vitally important data to NSLog() by default? Bizarre...Moslem
J
11

To complement the answers above, it can be quite useful to use a replacement for NSLog in certain situations, especially when debugging. For example, getting rid of all the date and process name/id information on each line can make output more readable and faster to boot.

The following link provides quite a bit of useful ammo for making simple logging much nicer.

http://cocoaheads.byu.edu/wiki/a-different-nslog

Jural answered 10/6, 2009 at 6:11 Comment(0)
O
9

It is simple,for Example

-(void)applicationWillEnterForeground:(UIApplication *)application {

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

Output: -[AppDelegate applicationWillEnterForeground:]

Onionskin answered 29/1, 2015 at 7:18 Comment(0)
W
5

building on top of above answers, here is what I plagiarized and came up with. Also added memory logging.

#import <mach/mach.h>

#ifdef DEBUG
#   define DebugLog(fmt, ...) NSLog((@"%s(%d) " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#   define DebugLog(...)
#endif


#define AlwaysLog(fmt, ...) NSLog((@"%s(%d) " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);


#ifdef DEBUG
#   define AlertLog(fmt, ...)  { \
    UIAlertView *alert = [[UIAlertView alloc] \
            initWithTitle : [NSString stringWithFormat:@"%s(Line: %d) ", __PRETTY_FUNCTION__, __LINE__]\
                  message : [NSString stringWithFormat : fmt, ##__VA_ARGS__]\
                 delegate : nil\
        cancelButtonTitle : @"Ok"\
        otherButtonTitles : nil];\
    [alert show];\
}
#else
#   define AlertLog(...)
#endif



#ifdef DEBUG
#   define DPFLog NSLog(@"%s(%d)", __PRETTY_FUNCTION__, __LINE__);//Debug Pretty Function Log
#else
#   define DPFLog
#endif


#ifdef DEBUG
#   define MemoryLog {\
    struct task_basic_info info;\
    mach_msg_type_number_t size = sizeof(info);\
    kern_return_t e = task_info(mach_task_self(),\
                                   TASK_BASIC_INFO,\
                                   (task_info_t)&info,\
                                   &size);\
    if(KERN_SUCCESS == e) {\
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; \
        [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; \
        DebugLog(@"%@ bytes", [formatter stringFromNumber:[NSNumber numberWithInteger:info.resident_size]]);\
    } else {\
        DebugLog(@"Error with task_info(): %s", mach_error_string(e));\
    }\
}
#else
#   define MemoryLog
#endif
Whitver answered 9/1, 2013 at 2:3 Comment(0)
H
4

New addition to DLog. Instead of totally removing debug from released application, only disable it. When user has problems, which would require debugging, just tell how to enable debug in released application and request log data via email.

Short version: create global variable (yes, lazy and simple solution) and modify DLog like this:

BOOL myDebugEnabled = FALSE;
#define DLog(fmt, ...) if (myDebugEnabled) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);

Longer answer at Jomnius iLessons iLearned: How to Do Dynamic Debug Logging in Released Application

Harberd answered 7/9, 2011 at 5:21 Comment(0)
T
3

For some time I've been using a site of macros adopted from several above. Mine focus on logging in the Console, with the emphasis on controlled & filtered verbosity; if you don't mind a lot of log lines but want to easily switch batches of them on & off, then you might find this useful.

First, I optionally replace NSLog with printf as described by @Rodrigo above

#define NSLOG_DROPCHAFF//comment out to get usual date/time ,etc:2011-11-03 13:43:55.632 myApp[3739:207] Hello Word

#ifdef NSLOG_DROPCHAFF
#define NSLog(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#endif

Next, I switch logging on or off.

#ifdef DEBUG
#define LOG_CATEGORY_DETAIL// comment out to turn all conditional logging off while keeping other DEBUG features
#endif

In the main block, define various categories corresponding to modules in your app. Also define a logging level above which logging calls won't be called. Then define various flavours of NSLog output

#ifdef LOG_CATEGORY_DETAIL

    //define the categories using bitwise leftshift operators
    #define kLogGCD (1<<0)
    #define kLogCoreCreate (1<<1)
    #define kLogModel (1<<2)
    #define kLogVC (1<<3)
    #define kLogFile (1<<4)
    //etc

    //add the categories that should be logged...
    #define kLOGIFcategory kLogModel+kLogVC+kLogCoreCreate

    //...and the maximum detailLevel to report (use -1 to override the category switch)
    #define kLOGIFdetailLTEQ 4

    // output looks like this:"-[AppDelegate myMethod] log string..."
    #   define myLog(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s " format), __PRETTY_FUNCTION__, ##__VA_ARGS__);}

    // output also shows line number:"-[AppDelegate myMethod][l17]  log string..."
    #   define myLogLine(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s[l%i] " format), __PRETTY_FUNCTION__,__LINE__ ,##__VA_ARGS__);}

    // output very simple:" log string..."
    #   define myLogSimple(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"" format), ##__VA_ARGS__);}

    //as myLog but only shows method name: "myMethod: log string..."
    // (Doesn't work in C-functions)
    #   define myLog_cmd(category,detailLevel,format,...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%@: " format), NSStringFromSelector(_cmd), ##__VA_ARGS__);}

    //as myLogLine but only shows method name: "myMethod>l17: log string..."
    #   define myLog_cmdLine(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%@>l%i: " format), NSStringFromSelector(_cmd),__LINE__ , ##__VA_ARGS__);}

    //or define your own...
   // # define myLogEAGLcontext(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s>l%i (ctx:%@)" format), __PRETTY_FUNCTION__,__LINE__ ,[EAGLContext currentContext], ##__VA_ARGS__);}

#else
    #   define myLog_cmd(...)
    #   define myLog_cmdLine(...)
    #   define myLog(...)
    #   define myLogLine(...)
    #   define myLogSimple(...)
    //#   define myLogEAGLcontext(...)
#endif

Thus, with current settings for kLOGIFcategory and kLOGIFdetailLTEQ, a call like

myLogLine(kLogVC, 2, @"%@",self);

will print but this won't

myLogLine(kLogGCD, 2, @"%@",self);//GCD not being printed

nor will

myLogLine(kLogGCD, 12, @"%@",self);//level too high

If you want to override the settings for an individual log call, use a negative level:

myLogLine(kLogGCD, -2, @"%@",self);//now printed even tho' GCD category not active.

I find the few extra characters of typing each line are worth as I can then

  1. Switch an entire category of comment on or off (e.g. only report those calls marked Model)
  2. report on fine detail with higher level numbers or just the most important calls marked with lower numbers

I'm sure many will find this a bit of an overkill, but just in case someone finds it suits their purposes..

Twelvemonth answered 12/2, 2014 at 13:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.