Wie drucke ich den Methodennamen und die Zeilennummer aus und deaktiviere NSLog bedingt?

446

Ich mache eine Präsentation zum Debuggen in Xcode und möchte weitere Informationen zur effizienten Verwendung von NSLog erhalten.

Insbesondere habe ich zwei Fragen:

  • Gibt es eine Möglichkeit, den Namen / die Zeilennummer der aktuellen Methode einfach zu NSLog?
  • Gibt es eine Möglichkeit, alle NSLogs einfach zu "deaktivieren", bevor sie für den Release-Code kompiliert werden?
Zügel
quelle
12
erste Frage, wo Favoriten (Stern) mehr als positive Stimmen sind ... +1 ..
Fahim Parkar

Antworten:

592

Hier sind einige nützliche Makros rund um NSLog, die ich häufig benutze:

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

Das DLog-Makro wird nur verwendet, wenn die Variable DEBUG gesetzt ist (-DDEBUG in den C-Flags des Projekts für die Debug-Konfiguration).

ALog gibt immer Text aus (wie das normale NSLog).

Die Ausgabe (zB ALog (@ "Hallo Welt")) sieht folgendermaßen aus:

-[LibraryController awakeFromNib] [Line 364] Hello world
diederikh
quelle
Warum hast du ein ##? Ich dachte, es wäre zum Zusammenkleben von Argumenten gedacht, aber Sie kleben an nichts.
Casebash
1
Dies verhindert eine mögliche
Makroerweiterung
Das kann bei Makros im Allgemeinen passieren. Einige Makros ergeben mehrere Zeilen. Nur ein weiteres Argument, um immer geschweifte Klammern zu verwenden ;-).
Diederikh
great und cocos2d api hat die ähnliche log-Anweisung.
Yoon Lee
Wie kommt es, (@"%s [Line %d] " fmt)dass das fmtan die Steuerzeichenfolge angehängt wird? Ich habe diese Syntax außer diesem Debug-Makro nicht gesehen.
Robert Altman
141

Ich habe DLogund ALogvon oben genommen und hinzugefügt, ULogwas eine UIAlertViewNachricht auslöst.

Zusammenfassen:

  • DLogwird wie NSLognur ausgegeben, wenn die Variable DEBUG gesetzt ist
  • ALog wird immer wie ausgegeben NSLog
  • ULogwird UIAlertViewnur angezeigt, wenn die Variable DEBUG gesetzt ist
#ifdef DEBUG
# DLog definieren (fmt, ...) NSLog ((@ "% s [Zeile% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#sonst
# DLog definieren (...)
#endif
#define ALog (fmt, ...) NSLog ((@ "% s [Zeile% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#ifdef DEBUG
# define ULog (fmt, ...) {UIAlertView * alert = [[UIAlertView alloc] initWithTitle: [NSString stringWithFormat: @ "% s \ n [Zeile% d]", __PRETTY_FUNCTION__, __LINE__] message: [NSString stringWithFormat: fmt , ## __ VA_ARGS__] delegate: nil cancelButtonTitle: @ "Ok" otherButtonTitles: nil]; [Alarmshow]; }}
#sonst
# ULog definieren (...)
#endif

So sieht es aus:

Debuggen Sie UIAlertView

+1 Diederik

Whitneyland
quelle
Ich werde meinen ALog + DLog-Code auch mit ULog erweitern. Sehr hilfreich.
Neoneye
Dieser Code verursacht einen nicht verwendeten Variablenfehler in Xcode 5.1, wenn er nicht in DEBUG ausgeführt wird :(
yonix
Warum enden einige der # define-Direktiven mit einem Semikolon?
Monstieur
@Locutus Sie müssen also kein Semikolon nach der DLogAnweisung setzen. Dies ist nützlich, da in Release-Builds DLognichts kompiliert wird und in Ihrem Code ein baumelndes Semikolon verbleibt. Dies ist kein Fehler, kann jedoch abhängig von Ihren Einstellungen eine Warnung auslösen, wenn ein anderes Semikolon verwendet wird.
Zev Eisenberg
74
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

Gibt Dateinamen, Zeilennummer und Funktionsnamen aus:

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

__FUNCTION__in C ++ zeigt verstümmelter Name __PRETTY_FUNCTION__zeigt schönen Funktionsnamen, in Kakao sehen sie gleich aus.

Ich bin mir nicht sicher, wie NSLog richtig deaktiviert werden kann. Ich habe Folgendes getan:

#define NSLog

Es wurde keine Protokollausgabe angezeigt, ich weiß jedoch nicht, ob dies Nebenwirkungen hat.

stefanB
quelle
20

Hier eine große Sammlung von Debug-Konstanten, die wir verwenden. Genießen.

// 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 Entwicklungsteam
quelle
19

Es gibt einen neuen Trick, den keine Antwort gibt. Sie können printfstattdessen verwenden NSLog. Dadurch erhalten Sie ein sauberes Protokoll:

Mit NSLogIhnen bekommen solche Dinge:

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

Aber mit printfdir bekommst du nur:

Hello World

Verwenden Sie diesen Code

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

Meine Antwort auf diese Frage könnte helfen, sieht aus wie die, die Diederik sich ausgedacht hat. Möglicherweise möchten Sie den Aufruf auch NSLog()durch eine statische Instanz Ihrer eigenen benutzerdefinierten Protokollierungsklasse ersetzen. Auf diese Weise können Sie ein Prioritätsflag für Debug- / Warn- / Fehlermeldungen hinzufügen, Nachrichten an eine Datei oder Datenbank sowie an die Konsole senden oder so ziemlich alles, was Sie sich sonst noch vorstellen können.

#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
quelle
Weil Sie dem %sFormatbezeichner ausgewichen sind, den Apple zu verachten versucht, und die -Wcstring-format-directive2015 neu eingeführte Clang-Warnung vermieden haben .
Jeff
13

Wenn Sie alle NSLogs deaktivieren, können Sie für jemanden, der gegen MACROS allergisch ist, Folgendes kompilieren:

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

Und verwenden Sie es fast wie NSLog:

SJLog(@"bye bye NSLogs !");

Aus diesem Blog: https://whackylabs.com/logging/ios/2011/01/19/ios-moving-in-and-out-of-nslogs/

Chunkyguy
quelle
11

Um die obigen Antworten zu ergänzen, kann es in bestimmten Situationen, insbesondere beim Debuggen, sehr nützlich sein, einen Ersatz für NSLog zu verwenden. Wenn Sie beispielsweise alle Informationen zu Datum und Prozessname / ID in jeder Zeile entfernen, kann die Ausgabe besser lesbar und schneller gestartet werden.

Der folgende Link bietet eine Menge nützlicher Munition, um die einfache Protokollierung zu vereinfachen.

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

Quinn Taylor
quelle
11

Es ist einfach, Ihre vorhandenen NSLogs so zu ändern, dass Zeilennummer und Klasse angezeigt werden, von denen sie aufgerufen werden. Fügen Sie Ihrer Präfixdatei eine Codezeile hinzu:

#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
AddisDev
quelle
3
Das ist toll! Wie würden Sie das schnell tun?
uplearnedu.com
@AddisDev Ich mag das am besten. Sehr sauber und einfach. Ich benutze nur NSLog. Ich habe sowieso keine Ahnung, was DLog & ULog sind! Vielen Dank. Up gewählt ...
Charles Robertson
@AddisDev Ich verstehe wirklich nicht, warum Apple diese lebenswichtigen Daten nicht standardmäßig zu NSLog () hinzufügt. Bizarr ...
Charles Robertson
8

Es ist zum Beispiel einfach

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

    NSLog(@"%s", __PRETTY_FUNCTION__);

}}

Ausgabe: - [AppDelegate applicationWillEnterForeground:]

Venkat Reddy
quelle
5

Aufbauend auf den obigen Antworten habe ich Folgendes plagiiert und erfunden. Außerdem wurde die Speicherprotokollierung hinzugefügt.

#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
quelle
4

Neuzugang bei DLog. Deaktivieren Sie das Debug nur aus der freigegebenen Anwendung, anstatt es vollständig zu entfernen. Wenn der Benutzer Probleme hat, die ein Debuggen erfordern würden, teilen Sie ihm einfach mit, wie das Debuggen in der freigegebenen Anwendung aktiviert werden soll, und fordern Sie Protokolldaten per E-Mail an.

Kurzversion: Erstellen Sie eine globale Variable (ja, faule und einfache Lösung) und ändern Sie DLog wie folgt:

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

Längere Antwort bei Jomnius iLessons iLearned: Dynamische Debug-Protokollierung in freigegebenen Anwendungen

JOM
quelle
3

Seit einiger Zeit verwende ich eine Seite mit Makros, die von mehreren oben übernommen wurden. Mein Fokus liegt auf der Anmeldung in der Konsole, wobei der Schwerpunkt auf kontrollierter und gefilterter Ausführlichkeit liegt . Wenn Ihnen viele Protokollzeilen nichts ausmachen, Sie aber Stapel von ihnen einfach ein- und ausschalten möchten, ist dies möglicherweise hilfreich.

Zuerst ersetze ich optional NSLog durch printf, wie oben von @Rodrigo beschrieben

#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

Als nächstes schalte ich die Anmeldung ein oder aus.

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

Definieren Sie im Hauptblock verschiedene Kategorien , die Modulen in Ihrer App entsprechen. Definieren Sie auch eine Protokollierungsstufe , über der Protokollierungsaufrufe nicht aufgerufen werden. Dann definieren verschiedene Aromen von NSLog Ausgang

#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

Mit den aktuellen Einstellungen für kLOGIFcategory und kLOGIFdetailLTEQ kann ein Anruf wie

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

wird gedruckt, aber das wird nicht

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

noch wird

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

Wenn Sie die Einstellungen für einen einzelnen Protokollaufruf überschreiben möchten, verwenden Sie eine negative Ebene:

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

Ich finde, dass die wenigen zusätzlichen Zeichen für die Eingabe jeder Zeile so viel wert sind, wie ich kann

  1. Schalten Sie eine ganze Kategorie von Kommentaren ein oder aus (z. B. melden Sie nur die mit Modell gekennzeichneten Anrufe).
  2. Bericht über feine Details mit höheren Nummern oder nur die wichtigsten Anrufe mit niedrigeren Nummern

Ich bin mir sicher, dass viele dies als etwas übertrieben empfinden werden, aber nur für den Fall, dass jemand findet, dass es zu ihren Zwecken passt.

cate
quelle