Jak wydrukować nazwę metody i numer linii oraz warunkowo wyłączyć NSLog?

446

Robię prezentację na temat debugowania w Xcode i chciałbym uzyskać więcej informacji na temat wydajnego używania NSLog.

W szczególności mam dwa pytania:

  • czy istnieje sposób na łatwe NSLogowanie nazwy / numeru wiersza bieżącej metody?
  • Czy istnieje sposób na łatwe „wyłączenie” wszystkich NSLogów przed skompilowaniem kodu wersji?
wodza
źródło
12
pierwsze pytanie, gdzie faworyci (gwiazda) są więcej niż głosami pozytywnymi ... +1 ..
Fahim Parkar

Odpowiedzi:

592

Oto kilka przydatnych makr wokół NSLog, których często używam:

#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 służy do wyświetlania danych wyjściowych tylko wtedy, gdy ustawiona jest zmienna DEBUG (-DDEBUG w flagach C projektów dla potwierdzenia debugowania).

ALog zawsze wyświetla tekst (jak zwykły NSLog).

Dane wyjściowe (np. ALog (@ „Hello world”)) będą wyglądać następująco:

-[LibraryController awakeFromNib] [Line 364] Hello world
Dieterikh
źródło
Dlaczego masz ##? Myślałem, że to było do sklejania argumentów, ale nie przyklejasz się do niczego.
Casebash
1
Zapobiega to możliwemu rozszerzaniu się argumentów na
makropolecenia
Może się tak zdarzyć w przypadku makr; niektóre makra dają wiele linii. Kolejny argument, aby zawsze używać nawiasów klamrowych ;-).
dieerikh
interfejs API great i cocos2d ma podobną instrukcję dziennika.
Yoon Lee,
W jaki sposób (@"%s [Line %d] " fmt)powoduje fmtto dołączenie do ciągu kontrolnego? Nie widziałem tej składni innej niż makro debugowania.
Robert Altman
141

Wziąłem DLogi ALogz góry, i dodał, ULogco podnosi UIAlertViewwiadomość.

Podsumowując:

  • DLogwyświetli tak jak NSLogtylko wtedy, gdy ustawiona jest zmienna DEBUG
  • ALog zawsze będzie wyświetlać jak NSLog
  • ULogpokaże UIAlertViewtylko, gdy ustawiona jest zmienna DEBUG
#ifdef DEBUG
# zdefiniuj DLog (fmt, ...) NSLog ((@ "% s [Linia% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#jeszcze
# zdefiniuj DLog (...)
#endif
# zdefiniować ALog (fmt, ...) NSLog ((@ "% s [Line% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#ifdef DEBUG
# zdefiniuj ULog (fmt, ...) {UIAlertView * alert = [[Alokacja UIAlertView] initWithTitle: [NSString stringWithFormat: @ "% s \ n [Line% d]", __PRETTY_FUNCTION__, __LINE__] wiadomość: [NSString stringWithFormat: fmt , ## __ VA_ARGS__] delegat: zero anulujButtonTitle: @ „Ok” otherButtonTitles: zero]; [alert pokaż]; }
#jeszcze
# zdefiniuj ULog (...)
#endif

Tak to wygląda:

Debuguj UIAlertView

+1 Diederik

Whitneyland
źródło
Zamierzam również rozszerzyć mój kod ALog + DLog o ULog. Bardzo przydatne.
neoneye
Ten kod powoduje błąd nieużywanej zmiennej w Xcode 5.1, jeśli nie działa w DEBUG :(
yonix
Dlaczego niektóre z dyrektyw #define kończą się średnikiem?
Monstieur 16.04.15
@Locutus Nie musisz wstawiać średnika po DLoginstrukcji. Jest to przydatne, ponieważ jeśli tak zrobiłeś, w kompilacjach wersji DLogjest skompilowany do zera, a w kodzie pozostanie ci wiszący średnik. To nie jest błąd, ale może wygenerować ostrzeżenie, w zależności od ustawień, jeśli nastąpi po innym średniku.
Zev Eisenberg
74
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

Wyświetla nazwę pliku, numer linii i nazwę funkcji:

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

__FUNCTION__ w C ++ pokazuje zniekształconą nazwę __PRETTY_FUNCTION__ pokazuje ładną nazwę funkcji, w kakao wyglądają tak samo.

Nie jestem pewien, jaki jest właściwy sposób wyłączenia NSLog, zrobiłem:

#define NSLog

I nie pojawił się żaden wynik rejestrowania, ale nie wiem, czy ma to jakieś skutki uboczne.

stefanB
źródło
20

Oto jedna duża kolekcja stałych debugowania, których używamy. Cieszyć się.

// 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
Zespół programistów SEQOY
źródło
19

Istnieje nowa sztuczka, której nie daje odpowiedź. Można użyć printfzamiastNSLog . To da ci czysty log:

Z NSLogciebie dostać takie rzeczy:

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

Ale z printftobą otrzymujesz tylko:

Hello World

Użyj tego kodu

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

Moja odpowiedź na to pytanie może pomóc, wygląda na to, że jest podobna do tej, którą ugotował Diederik. Możesz także zastąpić wywołanie NSLog()statycznym wystąpieniem własnej niestandardowej klasy rejestrowania, w ten sposób możesz dodać flagę priorytetu dla komunikatów debugowania / ostrzeżeń / błędów, wysyłać wiadomości do pliku lub bazy danych, a także do konsoli lub właściwie cokolwiek jeszcze możesz wymyślić.

#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
źródło
Ponieważ uniknąłeś %sspecyfikatora formatu, który Apple stara się wycofać, i uniknąłeś -Wcstring-format-directiveostrzeżenia Clanga nowo wprowadzonego w 2015 roku.
Jeff
11

W celu uzupełnienia powyższych odpowiedzi użyteczne może być użycie zamiennika NSLog w niektórych sytuacjach, szczególnie podczas debugowania. Na przykład pozbywanie się daty i informacji o nazwie / identyfikatorze procesu w każdej linii może sprawić, że dane wyjściowe będą bardziej czytelne i szybciej się uruchomią.

Poniższy link zawiera całkiem przydatną amunicję, dzięki której proste logowanie jest znacznie przyjemniejsze.

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

Quinn Taylor
źródło
11

Łatwo jest zmienić istniejące listy NSLog, aby wyświetlały numer linii i klasę, z której są wywoływane. Dodaj jeden wiersz kodu do pliku prefiksu:

#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
AddisDev
źródło
3
To jest świetne! jak zrobiłbyś to szybko?
uplearnedu.com
@AddisDev Lubię to najbardziej. Bardzo czysty i prosty. Używam tylko NSLog. Zresztą nie mam pojęcia, czym są DLog i ULog! Dzięki. W głosowaniu ...
Charles Robertson,
@AddisDev Naprawdę nie rozumiem, dlaczego Apple domyślnie nie dodaje tych niezwykle ważnych danych do NSLog ()? Dziwaczne ...
Charles Robertson,
8

To jest na przykład proste

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

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

Dane wyjściowe: - [AppDelegate applicationWillEnterForeground:]

Venkat Reddy
źródło
5

opierając się na powyższych odpowiedziach, oto co plagiatowałem i wymyśliłem. Dodano także rejestrowanie pamięci.

#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
źródło
4

Nowy dodatek do DLog. Zamiast całkowicie usuwać debugowanie z wydanej aplikacji, wyłącz ją. Gdy użytkownik ma problemy, które wymagałyby debugowania, po prostu powiedz, jak włączyć debugowanie w wydanej aplikacji i zażądaj danych dziennika za pośrednictwem poczty e-mail.

Krótka wersja: utwórz zmienną globalną (tak, leniwe i proste rozwiązanie) i zmodyfikuj DLog w następujący sposób:

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

Dłuższa odpowiedź w Jomnius iLessons iLearned: Jak wykonywać dynamiczne rejestrowanie debugowania w wydanej aplikacji

JOM
źródło
3

Od pewnego czasu korzystam z witryny makr przyjętych z kilku powyższych. Skupiam się na logowaniu w konsoli, z naciskiem na kontrolowaną i filtrowaną gadatliwość ; jeśli nie masz nic przeciwko wielu wierszom dziennika, ale chcesz łatwo włączać i wyłączać ich partie, może to być przydatne.

Po pierwsze, opcjonalnie zastępuję NSLog printf, jak opisano powyżej przez @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

Następnie włączam lub wyłączam logowanie.

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

W głównym bloku zdefiniuj różne kategorie odpowiadające modułom w swojej aplikacji. Określ także poziom rejestrowania , powyżej którego rejestrowanie połączeń nie będzie wywoływane. Następnie zdefiniuj różne smaki wyników 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

Zatem przy obecnych ustawieniach dla kLOGIFcategory i kLOGIFdetailLTEQ, połączenie takie jak

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

wydrukuje, ale nie będzie

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

nie będzie

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

Jeśli chcesz zastąpić ustawienia dla pojedynczego połączenia dziennika, użyj poziomu ujemnego:

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

Uważam, że kilka dodatkowych znaków wpisywania każdej linii jest wartych, jak mogę

  1. Włącz lub wyłącz całą kategorię komentarza (np. Zgłaszaj tylko połączenia oznaczone jako Model)
  2. melduj o szczegółach za pomocą numerów wyższego poziomu lub tylko najważniejszych połączeń oznaczonych niższymi numerami

Jestem pewien, że wielu uzna to za przesadę, ale na wypadek, gdyby ktoś uznał, że pasuje to do ich celów ...

Cate
źródło