Comment imprimer le nom de la méthode et le numéro de ligne et désactiver conditionnellement NSLog?

446

Je fais une présentation sur le débogage dans Xcode et je voudrais obtenir plus d'informations sur l'utilisation efficace de NSLog.

En particulier, j'ai deux questions:

  • existe-t-il un moyen de NSLog facilement le nom / numéro de ligne de la méthode actuelle?
  • existe-t-il un moyen de "désactiver" tous les NSLogs facilement avant de compiler le code de version?
rêne
la source
12
première question où les favoris (star) sont plus que des votes positifs ... +1 ..
Fahim Parkar

Réponses:

592

Voici quelques macros utiles autour de NSLog que j'utilise beaucoup:

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

La macro DLog est utilisée pour sortir uniquement lorsque la variable DEBUG est définie (-DDEBUG dans les indicateurs C du projet pour la configuration du débogage).

ALog affichera toujours du texte (comme le NSLog standard).

La sortie (par exemple ALog (@ "Hello world")) ressemblera à ceci:

-[LibraryController awakeFromNib] [Line 364] Hello world
deaderikh
la source
Pourquoi as-tu un ##? Je pensais que c'était pour coller des arguments ensemble, mais vous ne colliez à rien.
Casebash
1
Cela empêche une éventuelle macro expansion des arguments
deaderikh
Cela peut se produire avec les macros en général; certaines macros produisent plusieurs lignes. Juste un autre argument pour toujours utiliser des accolades ;-).
deaderikh
great et cocos2d api a la même instruction log.
Yoon Lee
Comment se fait-il que (@"%s [Line %d] " fmt)le fmtsoit ajouté à la chaîne de contrôle? Je n'ai pas vu cette syntaxe autre que cette macro de débogage.
Robert Altman
141

J'ai pris DLoget ALogd'en haut, et ajouté ULogce qui soulève un UIAlertViewmessage.

Résumer:

  • DLogaffichera comme NSLoguniquement lorsque la variable DEBUG est définie
  • ALog affichera toujours comme NSLog
  • ULogaffichera le UIAlertViewseul lorsque la variable DEBUG est définie
#ifdef DEBUG
# définir DLog (fmt, ...) NSLog ((@ "% s [Line% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#autre
# définir DLog (...)
#fin si
#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__] message: [NSString stringWithFormat: fmt , ## __ VA_ARGS__] délégué: nil cancelButtonTitle: @ "Ok" otherButtonTitles: nil]; [émission d'alerte]; }
#autre
# définir ULog (...)
#fin si

Voici à quoi ça ressemble:

Déboguer UIAlertView

+1 Diederik

Whitneyland
la source
Je vais également étendre mon code ALog + DLog avec ULog. Très utile.
neoneye
Ce code provoque une erreur de variable inutilisée dans Xcode 5.1 s'il ne fonctionne pas dans DEBUG :(
yonix
Pourquoi certaines des directives #define se terminent-elles par un point-virgule?
Monstieur
@Locutus Vous n'avez donc pas besoin de mettre un point-virgule après la DLogdéclaration. Ceci est utile car si vous l'avez fait, dans les versions, la version DLogest compilée pour rien, et il vous resterait un point-virgule pendant dans votre code. Ce n'est pas une erreur, mais cela peut générer un avertissement, selon vos paramètres, s'il suit un autre point-virgule.
Zev Eisenberg
74
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

Affiche le nom du fichier, le numéro de ligne et le nom de la fonction:

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

__FUNCTION__en C ++ montre le nom déformé __PRETTY_FUNCTION__montre le joli nom de la fonction, en cacao ils se ressemblent.

Je ne sais pas quelle est la bonne façon de désactiver NSLog, j'ai fait:

#define NSLog

Et aucune sortie de journalisation n'est apparue, mais je ne sais pas si cela a des effets secondaires.

stefanB
la source
20

Voici une grande collection de constantes de débogage que nous utilisons. Prendre plaisir.

// 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
Équipe de développement SEQOY
la source
19

Il y a une nouvelle astuce qu'aucune réponse ne donne. Vous pouvez utiliser à la printfplace NSLog. Cela vous donnera un journal propre:

Avec NSLogvous obtenez des choses comme ça:

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

Mais avec printfvous obtenez seulement:

Hello World

Utilisez ce code

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

Ma réponse à cette question pourrait aider, elle ressemble à celle que Diederik a concoctée. Vous pouvez également remplacer l'appel à NSLog()une instance statique de votre propre classe de journalisation personnalisée, de cette façon, vous pouvez ajouter un indicateur de priorité pour les messages de débogage / avertissement / erreur, envoyer des messages à un fichier ou une base de données ainsi qu'à la console, ou à peu près tout ce que vous pouvez penser.

#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
la source
Parce que vous avez esquivé le %sspécificateur de format qu'Apple essaie de déprécier et a évité l' -Wcstring-format-directiveavertissement Clang nouvellement introduit en 2015.
Jeff
11

Pour compléter les réponses ci-dessus, il peut être très utile d'utiliser un remplacement pour NSLog dans certaines situations, en particulier lors du débogage. Par exemple, se débarrasser de toutes les informations de date et de nom / id de processus sur chaque ligne peut rendre la sortie plus lisible et plus rapide à démarrer.

Le lien suivant fournit un certain nombre de munitions utiles pour rendre la journalisation simple beaucoup plus agréable.

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

Quinn Taylor
la source
11

Il est facile de modifier vos NSLogs existants pour afficher le numéro de ligne et la classe à partir desquels ils sont appelés. Ajoutez une ligne de code à votre fichier de préfixe:

#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
AddisDev
la source
3
C'est bien! comment feriez-vous cela rapidement?
uplearnedu.com
@AddisDev J'aime le mieux. Très propre et simple. J'utilise uniquement NSLog. Je n'ai aucune idée de ce que sont DLog et ULog de toute façon! Merci. Up voté ...
Charles Robertson
@AddisDev Je ne comprends vraiment pas pourquoi Apple n'ajoute pas ces données vitales à NSLog () par défaut? Bizarre ...
Charles Robertson
8

C'est simple, par exemple

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

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

Sortie: - [AppDelegate applicationWillEnterForeground:]

Venkat Reddy
la source
5

en s'appuyant sur les réponses ci-dessus, voici ce que j'ai plagié et trouvé. Ajout de la journalisation de la mémoire.

#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
la source
4

Nouvel ajout à DLog. Au lieu de supprimer totalement le débogage de l'application publiée, désactivez-la uniquement. Lorsque l'utilisateur a des problèmes, qui nécessiteraient un débogage, dites simplement comment activer le débogage dans une application publiée et demander les données du journal par e-mail.

Version courte: créez une variable globale (oui, solution paresseuse et simple) et modifiez DLog comme ceci:

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

Réponse plus longue à Jomnius iLessons iLearned: comment effectuer une journalisation de débogage dynamique dans une application publiée

JOM
la source
3

Depuis quelque temps, j'utilise un site de macros adopté à partir de plusieurs ci-dessus. Mine se concentre sur la journalisation dans la console, en mettant l'accent sur la verbosité contrôlée et filtrée ; si cela ne vous dérange pas beaucoup de lignes de journal mais que vous souhaitez facilement activer et désactiver des lots, alors vous pourriez trouver cela utile.

Tout d'abord, je remplace éventuellement NSLog par printf comme décrit par @Rodrigo ci-dessus

#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

Ensuite, j'active ou désactive la connexion.

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

Dans le bloc principal, définissez différentes catégories correspondant aux modules de votre application. Définissez également un niveau de journalisation au- dessus duquel les appels de journalisation ne seront pas appelés. Ensuite , définir différentes saveurs de sortie 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

Ainsi, avec les paramètres actuels de kLOGIFcategory et kLOGIFdetailLTEQ, un appel comme

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

imprimera mais ce ne sera pas

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

ni

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

Si vous souhaitez remplacer les paramètres d'un appel de journal individuel, utilisez un niveau négatif:

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

Je trouve que les quelques caractères supplémentaires de chaque ligne valent autant que je peux

  1. Activer ou désactiver une catégorie entière de commentaires (par exemple, signalez uniquement les appels marqués Modèle)
  2. signaler les détails avec des numéros de niveau supérieur ou simplement les appels les plus importants marqués de numéros inférieurs

Je suis sûr que beaucoup trouveront cela un peu exagéré, mais juste au cas où quelqu'un le trouverait adapté à leurs besoins ..

cate
la source