วิธีการพิมพ์ชื่อวิธีการและหมายเลขบรรทัดและปิดการใช้งาน NSLog เงื่อนไข?


446

ฉันกำลังทำการนำเสนอเกี่ยวกับการดีบักใน Xcode และต้องการรับข้อมูลเพิ่มเติมเกี่ยวกับการใช้ NSLog อย่างมีประสิทธิภาพ

โดยเฉพาะฉันมีคำถามสองข้อ:

  • มีวิธีการอย่างง่ายดาย NSLog ชื่อ / หมายเลขบรรทัดของวิธีการในปัจจุบัน?
  • มีวิธี "ปิดการใช้งาน" NSLogs ทั้งหมดได้อย่างง่ายดายก่อนที่จะรวบรวมรหัสการปล่อยหรือไม่?

12
คำถามแรกที่รายการโปรด (ดาว) มีจำนวนเพิ่มมากขึ้นแล้ว ... +1 ..
Fahim Parkar

คำตอบ:


592

นี่คือมาโครที่มีประโยชน์รอบ ๆ NSLog ฉันใช้บ่อยมาก:

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

แมโคร DLog ใช้เพื่อแสดงผลเฉพาะเมื่อมีการตั้งค่าตัวแปร DEBUG (-DDEBUG ในแฟล็ก C ของโปรเจ็กต์สำหรับการยืนยันการดีบัก)

ALog จะส่งข้อความออกเสมอ (เช่น NSLog ทั่วไป)

ผลลัพธ์ (เช่น ALog (@ "Hello world")) จะมีลักษณะเช่นนี้:

-[LibraryController awakeFromNib] [Line 364] Hello world

ทำไมคุณมี ##? ฉันคิดว่ามันเป็นเพราะการมีปากเสียงกัน แต่คุณไม่ได้ติดอะไรเลย
Casebash

1
นี้จะช่วยป้องกันการขยายตัวเป็นไปได้แมโครของการขัดแย้ง
diederikh

ที่สามารถเกิดขึ้นได้กับมาโครทั่วไป มาโครบางตัวให้ผลหลายบรรทัด เป็นอีกอาร์กิวเมนต์หนึ่งที่ใช้วงเล็บ ;-) เสมอ
เสียชีวิต

great และ cocos2d api มีคำสั่งบันทึกที่คล้ายกัน
Yoon Lee

เป็น(@"%s [Line %d] " fmt)สาเหตุที่ทำให้fmtผนวกเข้ากับสายอักขระควบคุมได้อย่างไร ฉันไม่เห็นไวยากรณ์นี้นอกจากแมโครดีบักนี้
Robert Altman

141

ฉันได้รับDLogและALogจากด้านบนและเพิ่มULogที่เพิ่มUIAlertViewข้อความ

เพื่อสรุป:

  • DLogจะส่งออกเช่นNSLogเดียวกับเมื่อตั้งค่าตัวแปร DEBUG
  • ALog จะส่งออกเช่น NSLog
  • ULogจะแสดงUIAlertViewเฉพาะเมื่อมีการตั้งค่าตัวแปร DEBUG
#ifdef DEBUG
# define DLog (fmt, ... ) NSLog ((@ "% s [Line% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#อื่น
# define DLog (... )
endif #
#define ALog (fmt, ... ) NSLog ((@ "% s [สาย% 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__] , ## __ VA_ARGS__] ผู้รับมอบสิทธิ์: ไม่มียกเลิก ButtonTitle: @ "Ok" otherButtonTitles: nil]; [แสดงการแจ้งเตือน]; }
#อื่น
# define ULog (... )
endif #

นี่คือสิ่งที่ดูเหมือนว่า:

แก้ไขข้อผิดพลาด UIAlertView

+1 Diederik


ฉันจะขยายรหัส ALog + DLog ของฉันด้วย ULog ด้วย มีประโยชน์มาก.
neoneye

รหัสนี้ทำให้เกิดข้อผิดพลาดตัวแปรที่ไม่ได้ใช้ใน Xcode 5.1 หากไม่ได้ทำงานใน DEBUG :(
yonix

เหตุใดคำสั่ง #define บางอันจึงลงท้ายด้วยเครื่องหมายอัฒภาค?
Monstieur

@Locutus ดังนั้นคุณไม่ต้องใส่เครื่องหมายอัฒภาคหลังจากDLogคำสั่ง สิ่งนี้มีประโยชน์เพราะถ้าคุณสร้างคอมDLogไพล์ในรีลีสคุณจะไม่ได้อะไรเลยและคุณจะเหลือเซมิโคลอนห้อยอยู่ในโค้ดของคุณ นี่ไม่ใช่ข้อผิดพลาด แต่มันอาจส่งสัญญาณเตือนขึ้นอยู่กับการตั้งค่าของคุณหากเป็นไปตามเซมิโคลอนอื่น
Zev Eisenberg

74
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

ส่งออกชื่อไฟล์หมายเลขบรรทัดและชื่อฟังก์ชั่น:

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

__FUNCTION__ใน C ++ แสดงชื่อ mangled แสดงชื่อ__PRETTY_FUNCTION__ฟังก์ชั่นที่ดีในโกโก้พวกเขาดูเหมือนกัน

ฉันไม่แน่ใจว่าวิธีที่เหมาะสมในการปิดใช้งาน NSLog คืออะไร:

#define NSLog

และไม่มีการบันทึกผลลัพธ์ปรากฏขึ้น แต่ฉันไม่รู้ว่าสิ่งนี้มีผลข้างเคียงหรือไม่


20

นี่คือหนึ่งคอลเลกชันใหญ่ของค่าคงที่ดีบักที่เราใช้ สนุก.

// 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

19

มีเคล็ดลับใหม่ที่ไม่มีคำตอบให้ คุณสามารถใช้แทนprintf NSLogสิ่งนี้จะให้บันทึกที่สะอาด:

เมื่อNSLogคุณได้สิ่งนี้:

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

แต่เมื่อprintfคุณได้รับเท่านั้น:

Hello World

ใช้รหัสนี้

#ifdef DEBUG
    #define NSLog(FORMAT, ...) fprintf(stderr,"%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
    #define NSLog(...) {}              
#endif

16

คำตอบของฉันสำหรับคำถามนี้อาจช่วยได้ดูเหมือนว่ามันจะคล้ายกับ Diederik ที่ปรุงขึ้นมา คุณอาจต้องการแทนที่การโทรNSLog()ด้วยอินสแตนซ์แบบคงที่ของคลาสการบันทึกที่กำหนดเองของคุณด้วยวิธีนี้คุณสามารถเพิ่มการตั้งค่าสถานะลำดับความสำคัญสำหรับข้อความดีบั๊ก / เตือน / ผิดพลาดส่งข้อความไปยังไฟล์หรือฐานข้อมูลเช่นเดียวกับคอนโซลหรือ ไม่ว่าคุณจะคิดยังไง

#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

เพราะคุณหลบ%sระบุรูปแบบที่แอปเปิ้ลพยายามที่จะเลิกและหลีกเลี่ยง-Wcstring-format-directiveการเตือนเสียงดังกราวเพิ่งแนะนำในปี 2015
เจฟฟ์

13

ปิดใช้งาน NSLogs ทั้งหมดสำหรับบางคนที่แพ้ MACROS นี่คือสิ่งที่คุณสามารถรวบรวมได้:

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

และใช้งานได้เหมือน NSLog:

SJLog(@"bye bye NSLogs !");

จากบล็อกนี้: https://whackylabs.com/logging/ios/2011/01/19/ios-moving-in-and-out-of-nslogs/


11

เพื่อเติมเต็มคำตอบข้างต้นมันค่อนข้างมีประโยชน์ที่จะใช้การแทนที่ NSLog ในบางสถานการณ์โดยเฉพาะเมื่อทำการดีบั๊ก ตัวอย่างเช่นการกำจัดข้อมูลวันที่และกระบวนการ / ชื่อ id ในแต่ละบรรทัดสามารถทำให้เอาต์พุตอ่านได้ง่ายขึ้นและบูตได้เร็วขึ้น

ลิงค์ต่อไปนี้ให้กระสุนที่มีประโยชน์เล็กน้อยสำหรับการบันทึกที่ง่ายกว่ามาก

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


11

ง่าย ๆ ในการเปลี่ยน NSLogs ที่มีอยู่ของคุณเพื่อแสดงหมายเลขบรรทัดและคลาสที่ถูกเรียก เพิ่มรหัสหนึ่งบรรทัดในไฟล์คำนำหน้าของคุณ:

#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)

3
มันเยี่ยมมาก! คุณจะทำสิ่งนี้อย่างรวดเร็วได้อย่างไร
uplearnedu.com

@AddisDev ฉันชอบสิ่งนี้ที่สุด สะอาดและเรียบง่ายมาก ฉันใช้ NSLog เท่านั้น ฉันไม่รู้ว่า DLog & ULog คืออะไร! ขอบคุณ โหวตขึ้น ...
Charles Robertson

@AddisDev ฉันไม่เข้าใจจริงๆว่าทำไม Apple ไม่เพิ่มข้อมูลที่สำคัญอย่างยิ่งนี้ให้กับ NSLog () ตามค่าเริ่มต้น ที่แปลกประหลาด ...
Charles Robertson

8

มันง่ายสำหรับตัวอย่าง

- (เป็นโมฆะ) applicationWillEnterForeground: (UIApplication *) แอปพลิเคชัน {

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

เอาท์พุท: - [แอปพลิเคชันแทนแอปพลิเคชัน WillEnterForeground:]


5

สร้างคำตอบข้างบนนี่คือสิ่งที่ฉันลอกเลียนแบบและเกิดขึ้น เพิ่มการบันทึกหน่วยความจำด้วย

#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

4

ใหม่นอกเหนือจาก DLog แทนที่จะลบการแก้ปัญหาออกจากแอปพลิเคชันที่ปล่อยออกมาให้ปิดการใช้งานเท่านั้น เมื่อผู้ใช้มีปัญหาซึ่งจะต้องแก้จุดบกพร่องเพียงบอกวิธีการเปิดใช้งานการแก้ปัญหาในแอปพลิเคชันที่นำออกใช้และขอข้อมูลบันทึกผ่านทางอีเมล์

เวอร์ชั่นสั้น: สร้างตัวแปรทั่วโลก (ใช่, ขี้เกียจและแก้ปัญหาง่ายๆ) และแก้ไข DLog ดังนี้:

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

คำตอบอีกต่อไปที่ Jomnius iLessons iLearned: วิธีการบันทึกการดีบักแบบไดนามิกในแอปพลิเคชันที่นำออกใช้


3

บางครั้งฉันก็ใช้ไซต์ของมาโครที่นำมาใช้จากหลายด้านบน ฉันมุ่งเน้นไปที่การเข้าสู่ระบบในคอนโซลโดยเน้นการควบคุมและกรองคำฟุ่มเฟื่อย ; หากคุณไม่ได้สนใจบรรทัดบันทึกข้อมูลจำนวนมาก แต่ต้องการสลับเป็นชุดเปิดหรือปิดได้อย่างง่ายดายคุณอาจพบว่ามีประโยชน์

ก่อนอื่นฉันเลือกแทนที่ NSLog ด้วย printf ตามที่อธิบายโดย @Rodrigo ด้านบน

#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

ต่อไปฉันจะเปิดหรือปิดการบันทึก

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

ในบล็อกหลักกำหนดหมวดหมู่ต่างๆที่เกี่ยวข้องกับโมดูลในแอปของคุณ กำหนดระดับการบันทึก ด้านบนซึ่งการโทรบันทึกจะไม่ถูกเรียก จากนั้นกำหนดรสชาติต่างๆของเอาต์พุต NSLog

#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

ดังนั้นด้วยการตั้งค่าปัจจุบันสำหรับ kLOGIF หมวดหมู่และ kLOGIF รายละเอียด LTEQ การโทรเช่น

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

จะพิมพ์ แต่สิ่งนี้จะไม่

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

และจะไม่

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

หากคุณต้องการแทนที่การตั้งค่าสำหรับการโทรแต่ละรายการให้ใช้ระดับลบ:

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

ฉันพบว่าอักขระพิเศษไม่กี่ตัวที่พิมพ์แต่ละบรรทัดมีค่าเท่าที่ฉันจะทำได้

  1. สลับหรือปิดหมวดหมู่ความคิดเห็นทั้งหมด (เช่นรายงานเฉพาะสายที่ทำเครื่องหมายรุ่น)
  2. รายงานรายละเอียดอย่างละเอียดพร้อมตัวเลขระดับที่สูงขึ้นหรือเพียงแค่การโทรที่สำคัญที่สุดที่มีหมายเลขต่ำกว่า

ฉันแน่ใจว่าหลายคนจะพบว่ามันเกินความจริงเล็กน้อย แต่ในกรณีที่มีคนพบว่ามันเหมาะสมกับวัตถุประสงค์ของพวกเขา ..

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.