Як роздрукувати ім'я методу та номер рядка та умовно відключити NSLog?


446

Я веду презентацію про налагодження в Xcode і хотів би отримати більше інформації про ефективне використання NSLog.

Зокрема, у мене є два питання:

  • чи є спосіб легко NSLog назва поточного методу / номер рядка?
  • чи є спосіб легко "відключити" всі NSLogs перед компілюванням для випуску коду?

12
Перше питання, де фаворити (зірка) більше, ніж нагороди ... +1 ..
Фахім Паркар

Відповіді:


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
Це запобігає можливому розширенню аргументів
макросу

Це може статися з макросами взагалі; деякі макроси дають кілька ліній. Ще один аргумент завжди використовувати дужки ;-).
dieerikh

great і cocos2d api має схожий протокол заяви.
Йон Лі

Як це (@"%s [Line %d] " fmt)спричиняє fmtдодавання до контрольного рядка? Я не бачив цього синтаксису, крім цього макросу налагодження.
Роберт Альтман

141

Я взяв DLogі ALogзверху, і додав, ULogщо викликає UIAlertViewповідомлення.

Узагальнити:

  • DLogвидаватиметься як NSLogтільки тоді, коли встановлена ​​змінна DEBUG
  • ALog завжди буде видаватися як NSLog
  • ULogвідображатиметься UIAlertViewлише тоді, коли встановлена ​​змінна DEBUG
#ifdef DEBUG
# визначити DLog (fmt, ...) NSLog ((@ "% s [Рядок% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#else
# визначити DLog (...)
#endif
#define ALog (fmt, ...) NSLog ((@ "% s [рядок% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#ifdef DEBUG
# визначити ULog (fmt, ...) {UIAlertView * alert = [[UIAlertView alloc] initWithTitle: [NSString stringWithFormat: @ "% s \ n [Рядок% d]", __PRETTY_FUNCTION__, __LINE__] повідомлення: [NSString fringtithithithith , ## __ VA_ARGS__] делегат: nil cancelButtonTitle: @ "Добре" otherButtonTitles: nil]; [попередження шоу]; }
#else
# визначити ULog (...)
#endif

Ось як це виглядає:

Налагодження UIAlertView

+1 Дідерік


Я збираюся розширити свій код ALog + DLog і ULog. Дуже корисний.
neoneye

Цей код викликає невикористовувану помилку змінної у Xcode 5.1, якщо вона не працює в DEBUG :(
yonix

Чому деякі директиви #define закінчуються крапкою з комою?
Монсьє

@Locutus Отже, після DLogоператора не потрібно ставити крапку з комою . Це корисно, тому що якби ви, у версії версій, DLogзібрані ні до чого, і у вас залишиться блукаюча крапка з комою. Це не помилка, але вона може виносити попередження, залежно від ваших налаштувань, якщо це слід за іншою крапкою з комою.
Зев Айзенберг

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

Виводить ім'я файлу, номер рядка та ім'я функції:

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

__FUNCTION__в C ++ відображається згорблене ім’я, __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

Моя відповідь на це питання може допомогти, схоже на те, що приготував Дідерік. Ви також можете замінити виклик 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специфікатора формату, який Apple намагається знехтувати і уникнув -Wcstring-format-directiveпопередження Clang, що був нещодавно введений у 2015 році.
Jeff


11

Щоб доповнити відповіді вище, може бути досить корисним використання заміни NSLog в певних ситуаціях, особливо при налагодженні. Наприклад, позбавлення від усіх даних про дату та інформацію про ім’я / ідентифікатор / ідентифікатор кожного рядка може зробити результат більш зрозумілим та швидшим для завантаження.

Наведене нижче посилання містить досить багато корисної патрони для того, щоб зробити прості лісозаписи набагато приємнішими.

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! Дякую. Вгору проголосували ...
Чарльз Робертсон

@AddisDev Я справді не розумію, чому Apple не додає ці життєво важливі дані до NSLog () за замовчуванням? Bizarre ...
Чарльз Робертсон

8

Наприклад, це просто

- (недійсна) програмаWillEnterForeground: (UIApplication *) додаток {

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

Вихід: - [AppDelegate applicationWillEnterForeground:]


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

Таким чином, з поточними налаштуваннями для kLOGIFcategory та kLOGIFdetailLTEQ, подібний виклик

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. повідомляти про детальні деталі з номерами вищого рівня або лише про найважливіші дзвінки, позначені нижчими номерами

Я впевнений, що багато хто знайде це трохи непосильним, але про всяк випадок, коли хтось виявить, що це відповідає їхнім цілям.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.