Làm cách nào để in tên phương thức và số dòng và vô hiệu hóa điều kiện NSLog?


446

Tôi đang trình bày về gỡ lỗi trong Xcode và muốn có thêm thông tin về cách sử dụng NSLog một cách hiệu quả.

Cụ thể, tôi có hai câu hỏi:

  • Có cách nào để dễ dàng NSLog tên / số dòng của phương thức hiện tại không?
  • Có cách nào để "vô hiệu hóa" tất cả NSLogs một cách dễ dàng trước khi biên dịch mã phát hành không?

12
câu hỏi đầu tiên trong đó mục yêu thích (ngôi sao) nhiều hơn thì tăng ... +1 ..
Fahim Parkar

Câu trả lời:


592

Dưới đây là một số macro hữu ích xung quanh NSLog tôi sử dụng rất nhiều:

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

Macro DLog được sử dụng để chỉ xuất ra khi biến DEBUG được đặt (-DDEBUG trong các cờ C của dự án để xác nhận gỡ lỗi).

ALog sẽ luôn xuất văn bản (như NSLog thông thường).

Đầu ra (ví dụ ALog (@ "Hello world")) sẽ trông như thế này:

-[LibraryController awakeFromNib] [Line 364] Hello world

Tại sao bạn có một ##? Tôi nghĩ rằng nó là để dán các đối số với nhau, nhưng bạn không dán vào bất cứ điều gì.
Casebash

1
Điều này ngăn cản khả năng mở rộng vĩ mô của các đối số
deatherikh

Điều đó có thể xảy ra với các macro nói chung; một số macro mang lại nhiều dòng. Chỉ cần một đối số khác để luôn luôn sử dụng niềng răng ;-).
deatherikh

api tuyệt vời và cocos2d có tuyên bố đăng nhập tương tự.
Yoon Lee

Làm thế nào mà nó (@"%s [Line %d] " fmt)khiến fmtcho được gắn vào chuỗi điều khiển? Tôi chưa thấy cú pháp này ngoài macro gỡ lỗi này.
Robert Altman

141

Tôi đã lấy DLogALogtừ trên xuống, và thêm vào ULogđó làm tăng một UIAlertViewtin nhắn.

Để tóm tắt:

  • DLogsẽ chỉ xuất ra NSLogkhi biến DEBUG được đặt
  • ALog sẽ luôn xuất ra như thế nào NSLog
  • ULogsẽ chỉ hiển thị UIAlertViewkhi biến DEBUG được đặt
#ifdef DEBUG
# xác định DLog (fmt, ...) NSLog ((@ "% s [Dòng% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#else
# xác định DLog (...)
#endif
#define ALog (fmt, ...) NSLog ((@ "% s [Dòng% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#ifdef DEBUG
# xác định ULog (fmt, ...) {UIAlertView * alert = [[UIAlertView alloc] initWithTitle: [NSString chuỗiWithFormat: @ "% s \ n [Line% d]", __PRETTY_FUNCTION__, __LINE__ , ## __ VA_ARGS__] đại biểu: nil hủyButtonTitle: @ "Ok" otherButtonTitle: nil]; [chương trình cảnh báo]; }
#else
# xác định ULog (...)
#endif

Đây là những gì nó trông giống như:

Gỡ lỗi UIAlertView

+1 Diederik


Tôi cũng sẽ mở rộng mã ALog + DLog của mình với ULog. Rất hữu ích.
neoneye

Mã này gây ra lỗi biến không được sử dụng trong Xcode 5.1 nếu không chạy trong DEBUG :(
yonix 18/03 '

Tại sao một số chỉ thị #define kết thúc bằng dấu chấm phẩy?
Monstieur

@Locutus Vì vậy, bạn không cần phải đặt dấu chấm phẩy sau DLogtuyên bố. Điều này rất hữu ích vì nếu bạn đã làm, trong các bản dựng phát hành, DLogđược biên dịch thành không có gì và bạn sẽ bị bỏ lại một dấu chấm phẩy lơ lửng trong mã của mình. Đây không phải là một lỗi, nhưng nó có thể đưa ra một cảnh báo, tùy thuộc vào cài đặt của bạn, nếu nó đi theo một dấu chấm phẩy khác.
Zev Eisenberg

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

Xuất ra tên tệp, số dòng và tên hàm:

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

__FUNCTION__trong C ++ hiển thị tên xéo cho thấy tên __PRETTY_FUNCTION__hàm đẹp, trong ca cao trông chúng giống nhau.

Tôi không chắc cách vô hiệu hóa NSLog là gì, tôi đã làm:

#define NSLog

Và không có đầu ra đăng nhập xuất hiện, tuy nhiên tôi không biết nếu điều này có bất kỳ tác dụng phụ.


20

Đây là một bộ sưu tập lớn các hằng số gỡ lỗi mà chúng ta sử dụng. Thưởng thức.

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

Có một mẹo mới mà không có câu trả lời nào đưa ra. Bạn có thể sử dụng printfthay thế NSLog. Điều này sẽ cung cấp cho bạn một bản ghi sạch:

Với NSLogbạn có được những thứ như thế này:

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

Nhưng với printfbạn chỉ nhận được:

Hello World

Sử dụng mã này

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

16

Câu trả lời của tôi cho câu hỏi này có thể giúp ích, có vẻ như nó giống với món Diederik đã nấu chín. Bạn cũng có thể muốn thay thế cuộc gọi NSLog()bằng một phiên bản tĩnh của lớp ghi nhật ký tùy chỉnh của riêng bạn, theo cách đó bạn có thể thêm cờ ưu tiên cho thông báo gỡ lỗi / cảnh báo / lỗi, gửi tin nhắn đến tệp hoặc cơ sở dữ liệu cũng như bảng điều khiển hoặc khá nhiều bất cứ điều gì khác mà bạn có thể nghĩ đến.

#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

Bởi vì bạn đã tránh %sđược trình xác định định dạng mà Apple đang cố gắng từ chối và tránh -Wcstring-format-directivecảnh báo Clang mới được giới thiệu vào năm 2015.
Jeff


11

Để bổ sung cho các câu trả lời ở trên, có thể khá hữu ích khi sử dụng thay thế cho NSLog trong một số trường hợp nhất định, đặc biệt là khi gỡ lỗi. Ví dụ: loại bỏ tất cả thông tin tên / id ngày và quy trình trên mỗi dòng có thể giúp đầu ra dễ đọc hơn và khởi động nhanh hơn.

Liên kết sau đây cung cấp khá nhiều đạn hữu ích để ghi nhật ký đơn giản đẹp hơn nhiều.

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


11

Thật dễ dàng để thay đổi NSLogs hiện tại của bạn để hiển thị số dòng và lớp mà chúng được gọi. Thêm một dòng mã vào tệp tiền tố của bạn:

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

3
Điều đó thật tuyệt! Làm thế nào bạn sẽ làm điều này trong nhanh chóng?
uplearnedu.com 22/1/2015

@AddisDev Tôi thích điều này nhất. Rất sạch sẽ và đơn giản. Tôi chỉ sử dụng NSLog. Tôi không biết DLog & ULog là gì! Cảm ơn. Đã bình chọn ...
Charles Robertson

@AddisDev Tôi thực sự không hiểu tại sao Apple không thêm dữ liệu cực kỳ quan trọng này vào NSLog () theo mặc định? Kỳ quái ...
Charles Robertson

8

Nó là đơn giản, ví dụ

- (void) applicationWill EntryForeground: (UIApplication *) application {

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

Đầu ra: - [Ứng dụng AppDelegateWill EntryForeground:]


5

dựa trên các câu trả lời trên, đây là những gì tôi đạo văn và đưa ra. Cũng thêm ghi nhật ký bộ nhớ.

#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

Bổ sung mới cho DLog. Thay vì loại bỏ hoàn toàn gỡ lỗi khỏi ứng dụng được phát hành, chỉ vô hiệu hóa nó. Khi người dùng gặp sự cố, yêu cầu gỡ lỗi, chỉ cần cho biết cách bật gỡ lỗi trong ứng dụng đã phát hành và yêu cầu dữ liệu nhật ký qua email.

Phiên bản ngắn: tạo biến toàn cục (có, giải pháp đơn giản và lười biếng) và sửa đổi DLog như thế này:

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

Câu trả lời dài hơn tại Jomas iLessons iLearned: Cách thực hiện ghi nhật ký gỡ lỗi động trong ứng dụng đã phát hành


3

Trong một thời gian, tôi đã sử dụng một trang web macro được thông qua từ một số ở trên. Tôi tập trung vào việc đăng nhập vào Bảng điều khiển, với sự nhấn mạnh vào tính dài dòng được kiểm soát và lọc ; nếu bạn không bận tâm nhiều dòng nhật ký nhưng muốn dễ dàng bật và tắt hàng loạt chúng, thì bạn có thể thấy điều này hữu ích.

Đầu tiên, tôi tùy ý thay thế NSLog bằng printf như được mô tả bởi @Rodrigo ở trên

#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

Tiếp theo, tôi chuyển sang đăng nhập hoặc tắt.

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

Trong khối chính, xác định các danh mục khác nhau tương ứng với các mô-đun trong ứng dụng của bạn. Cũng xác định một mức ghi nhật ký trên đó các cuộc gọi đăng nhập sẽ không được gọi. Sau đó xác định các hương vị khác nhau của đầu ra 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

Do đó, với các cài đặt hiện tại cho kLOGIFc Category và kLOGIFdetailLTEQ, một cuộc gọi như

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

sẽ in nhưng điều này sẽ không

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

cũng sẽ không

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

Nếu bạn muốn ghi đè cài đặt cho một cuộc gọi nhật ký riêng lẻ, hãy sử dụng mức âm:

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

Tôi thấy một vài ký tự thêm vào mỗi dòng có giá trị như tôi có thể sau đó

  1. Bật hoặc tắt toàn bộ danh mục nhận xét (ví dụ: chỉ báo cáo những cuộc gọi được đánh dấu Model)
  2. báo cáo chi tiết tốt với số cấp cao hơn hoặc chỉ các cuộc gọi quan trọng nhất được đánh dấu bằng số thấp hơn

Tôi chắc chắn nhiều người sẽ thấy điều này hơi quá mức, nhưng chỉ trong trường hợp ai đó thấy nó phù hợp với mục đích của họ ..

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.