Bagaimana cara mencetak nama metode dan nomor baris serta menonaktifkan NSLog secara kondisional?

446

Saya sedang melakukan presentasi tentang debugging di Xcode dan ingin mendapatkan informasi lebih lanjut tentang penggunaan NSLog secara efisien.

Secara khusus, saya punya dua pertanyaan:

  • apakah ada cara untuk dengan mudah NSLog nama metode / nomor baris saat ini?
  • apakah ada cara untuk "menonaktifkan" semua NSLogs dengan mudah sebelum kompilasi untuk kode rilis?
kendali
sumber
12
pertanyaan pertama di mana favorit (bintang) lebih unggul ... +1 ..
Fahim Parkar

Jawaban:

592

Berikut adalah beberapa makro yang berguna di sekitar NSLog yang sering saya gunakan:

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

Makro DLog hanya digunakan untuk output ketika variabel DEBUG diatur (-DDEBUG di flag C proyek untuk konfigurasi debug).

ALog akan selalu menampilkan teks (seperti NSLog biasa).

Output (mis. ALog (@ "Hello world")) akan terlihat seperti ini:

-[LibraryController awakeFromNib] [Line 364] Hello world
diederikh
sumber
Mengapa Anda memiliki sebuah ##? Saya pikir itu untuk menempelkan argumen bersama, tetapi Anda tidak menempel pada apa pun.
Casebash
1
Ini mencegah kemungkinan perluasan argumen secara
makro
Itu bisa terjadi dengan makro secara umum; beberapa makro menghasilkan banyak baris. Argumen lain untuk selalu menggunakan kawat gigi ;-).
diederikh
api hebat dan cocos2d memiliki pernyataan log yang sama.
Yoon Lee
Bagaimana hal itu (@"%s [Line %d] " fmt)menyebabkan fmtditambahkan ke string kontrol? Saya belum melihat sintaks ini selain makro debug ini.
Robert Altman
141

Saya telah mengambil DLogdan ALogdari atas, dan menambahkan ULogyang memunculkan UIAlertViewpesan.

Untuk meringkas:

  • DLogakan menampilkan seperti NSLoghanya ketika variabel DEBUG diatur
  • ALog akan selalu menampilkan suka NSLog
  • ULogakan menampilkan UIAlertViewsatu - satunya ketika variabel DEBUG diatur
#Jika DEBUG
# define DLog (fmt, ...) NSLog ((@ "% s [Line% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#lain
# define DLog (...)
#berakhir jika
#define ALog (fmt, ...) NSLog ((@ "% s [Line% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#Jika DEBUG
# define ULog (fmt, ...) {UIAlertView * alert = [[alokasi UIAlertView] initWithTitle: [NSString stringWithFormat: @ "% s \ n [Baris% d]", __PRETTY_FUNCTION__, __LINE__] pesan: [NSString stringWithFormat: fmt: , ## __ VA_ARGS__] delegasi: nil cancelButtonTitle: @ "Ok" otherButtonTitles: nil]; [tampilkan lansiran]; }
#lain
# define ULog (...)
#berakhir jika

Seperti inilah tampilannya:

Debug UIAlertView

+1 Diederik

Whitneyland
sumber
Saya akan memperpanjang kode ALog + DLog saya dengan ULog juga. Sangat berguna.
neoneye
Kode ini menyebabkan kesalahan variabel yang tidak terpakai di Xcode 5.1 jika tidak berjalan di DEBUG :(
yonix
Mengapa beberapa arahan #define diakhiri dengan tanda titik koma?
Monstieur
@Locutus Jadi Anda tidak perlu meletakkan tanda koma setelah DLogpernyataan. Ini berguna karena jika Anda melakukannya, dalam rilis build, DLogdikompilasi menjadi nol, dan Anda akan dibiarkan dengan titik koma menggantung di kode Anda. Ini bukan kesalahan, tapi itu bisa menimbulkan peringatan, tergantung pada pengaturan Anda, jika itu mengikuti titik koma lain.
Zev Eisenberg
74
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

Output nama file, nomor baris, dan nama fungsi:

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

__FUNCTION__di C ++ menunjukkan nama hancur menunjukkan nama __PRETTY_FUNCTION__fungsi yang bagus, dalam kakao mereka terlihat sama.

Saya tidak yakin apa cara yang tepat untuk menonaktifkan NSLog, saya lakukan:

#define NSLog

Dan tidak ada output logging muncul, namun saya tidak tahu apakah ini memiliki efek samping.

Stefan
sumber
20

Di sini satu koleksi besar konstanta debug yang kami gunakan. Nikmati.

// 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
Tim Pengembangan SEQOY
sumber
19

Ada trik baru yang tidak diberikan jawaban. Anda dapat menggunakan printfsebagai gantinya NSLog. Ini akan memberi Anda log bersih:

Dengan NSLogAnda mendapatkan hal-hal seperti ini:

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

Tetapi dengan printfAnda hanya mendapatkan:

Hello World

Gunakan kode ini

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

Jawaban saya untuk pertanyaan ini mungkin membantu, sepertinya itu mirip dengan yang Diederik masak. Anda mungkin juga ingin mengganti panggilan NSLog()dengan turunan statis dari kelas pembuatan log kustom Anda sendiri, dengan cara itu Anda dapat menambahkan bendera prioritas untuk pesan debug / peringatan / kesalahan, mengirim pesan ke file atau database serta konsol, atau cukup banyak hal lain yang bisa kamu pikirkan.

#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
Marc Charbonneau
sumber
Karena Anda menghindari %sformat specifier yang Apple coba hentikan dan hindari -Wcstring-format-directiveperingatan Dentang yang baru diperkenalkan pada tahun 2015.
Jeff
13

Menonaktifkan semua NSLogs, bagi seseorang yang alergi terhadap MACROS, berikut adalah sesuatu yang dapat Anda kompilasi juga:

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

Dan, gunakan hampir seperti NSLog:

SJLog(@"bye bye NSLogs !");

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

chunkyguy
sumber
11

Untuk melengkapi jawaban di atas, akan sangat berguna untuk menggunakan pengganti NSLog dalam situasi tertentu, terutama saat debugging. Misalnya, menyingkirkan semua tanggal dan memproses informasi nama / id pada setiap baris dapat membuat output lebih mudah dibaca dan lebih cepat untuk boot.

Tautan berikut memberikan sedikit amunisi yang berguna untuk membuat pencatatan sederhana menjadi lebih baik.

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

Quinn Taylor
sumber
11

Sangat mudah untuk mengubah NSLogs Anda saat ini untuk menampilkan nomor baris dan kelas dari mana mereka dipanggil. Tambahkan satu baris kode ke file awalan Anda:

#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
AddisDev
sumber
3
Ini bagus! bagaimana Anda melakukan ini dengan cepat?
uplearnedu.com
@AddisDev Saya suka ini yang terbaik. Sangat bersih & sederhana. Saya hanya menggunakan NSLog. Saya tidak tahu apa itu DLog & ULog! Terima kasih. Terpilih ...
Charles Robertson
@AddisDev Saya benar-benar tidak mengerti mengapa Apple tidak menambahkan data yang sangat penting ini ke NSLog () secara default? Aneh ...
Charles Robertson
8

Sederhana, misalnya

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

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

Output: - [AppDelegate applicationWillEnterForeground:]

Venkat Reddy
sumber
5

membangun di atas jawaban di atas, inilah yang saya jiplakan dan datang dengan. Juga menambahkan pendataan memori.

#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
Dickey Singh
sumber
4

Tambahan baru untuk DLog. Alih-alih menghapus debug sepenuhnya dari aplikasi yang dirilis, hanya menonaktifkannya. Ketika pengguna memiliki masalah, yang akan membutuhkan debugging, cukup beri tahu cara mengaktifkan debug dalam aplikasi yang dirilis dan minta data log melalui email.

Versi singkat: buat variabel global (ya, solusi malas dan sederhana) dan ubah DLog seperti ini:

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

Jawaban yang lebih panjang di Jomnius iLessons iLearned: Bagaimana Melakukan Pembuktian Debug Dinamis dalam Aplikasi yang Dirilis

JOM
sumber
3

Untuk beberapa waktu saya telah menggunakan situs macro yang diadopsi dari beberapa di atas. Tambang fokus pada penebangan di Konsol, dengan penekanan pada verbositas terkontrol & difilter ; jika Anda tidak keberatan banyak baris log tetapi ingin dengan mudah mengaktifkan & menonaktifkan batch-nya, maka Anda mungkin menemukan ini berguna.

Pertama, saya secara opsional mengganti NSLog dengan printf seperti yang dijelaskan oleh @Rodrigo di atas

#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

Selanjutnya, saya mengaktifkan atau menonaktifkan logging.

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

Di blok utama, tentukan berbagai kategori yang terkait dengan modul di aplikasi Anda. Tentukan juga level logging di mana panggilan logging tidak akan dipanggil. Kemudian tentukan berbagai rasa dari output 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

Dengan demikian, dengan pengaturan saat ini untuk kLOGIFcategory dan kLOGIFdetailLTEQ, panggilan seperti

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

akan mencetak tetapi ini tidak akan

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

tidak akan

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

Jika Anda ingin mengganti pengaturan untuk panggilan log individual, gunakan level negatif:

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

Saya menemukan beberapa karakter tambahan mengetik setiap baris bernilai seperti yang saya bisa

  1. Mengaktifkan atau menonaktifkan seluruh kategori komentar (misalnya, hanya melaporkan panggilan-panggilan yang ditandai Model)
  2. melaporkan detail halus dengan nomor tingkat yang lebih tinggi atau hanya panggilan paling penting yang ditandai dengan angka yang lebih rendah

Saya yakin banyak orang akan menemukan ini sedikit berlebihan, tetapi kalau-kalau ada orang yang menemukan itu sesuai dengan tujuan mereka ..

cate
sumber