Как распечатать название метода и номер строки и условно отключить 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
diederikh
источник
Почему у тебя ##? Я думал, что это было для склеивания аргументов, но ты ничего не склеиваешь.
Casebash
1
Это предотвращает возможное
макроразложение
Это может произойти с макросами в целом; некоторые макросы дают несколько строк. Еще один аргумент, чтобы всегда использовать фигурные скобки ;-).
diederikh
Великий и Cocos2D API имеет аналогичные записи журнала.
Юн Ли
Как это , что (@"%s [Line %d] " fmt)приводит к тому , который fmtдолжен быть добавлен к строке управления? Я не видел этот синтаксис, кроме этого макроса отладки.
Роберт Альтман
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__);
#else
# определить 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__] сообщение: [NSString stringWithFormat: fmt , ## __ VA_ARGS__] делегат: nil cancelButtonTitle: @ "Ok" otherButtonTitles: nil]; [оповещение]; }
#else
# определить ULog (...)
#endif

Вот как это выглядит:

Отладка UIAlertView

+1 Дидерик

whitneyland
источник
Я собираюсь расширить свой код 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

И никаких результатов регистрации не было обнаружено, однако я не знаю, имеет ли это какие-либо побочные эффекты.

stefanB
источник
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
Команда разработчиков SEQOY
источник
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 году.
Джефф
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/

chunkyguy
источник
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__)
AddisDev
источник
3
Это замечательно! как бы вы сделали это по-быстрому?
uplearnedu.com
@AddisDev Мне нравится это больше всего. Очень чисто и просто. Я использую только NSLog. Я не знаю, что такое DLog & ULog! Спасибо. За проголосовали ...
Чарльз Робертсон
@AddisDev Я действительно не понимаю, почему Apple не добавляет эти жизненно важные данные в NSLog () по умолчанию? Странно ...
Чарльз Робертсон
8

Это просто, например

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

    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. отчет о мелкой детализации с номерами более высокого уровня или только о самых важных вызовах, отмеченных номерами более низкого уровня

Я уверен, что многие найдут это немного излишним, но на всякий случай, если кто-то найдет, что это соответствует их целям ..

Cate
источник