¿Cómo imprimir el nombre del método y el número de línea y deshabilitar condicionalmente NSLog?

446

Estoy haciendo una presentación sobre depuración en Xcode y me gustaría obtener más información sobre el uso eficiente de NSLog.

En particular, tengo dos preguntas:

  • ¿hay alguna manera de NSLog fácilmente el nombre / número de línea del método actual?
  • ¿Hay alguna manera de "deshabilitar" todos los NSLogs fácilmente antes de compilar el código de lanzamiento?
rienda
fuente
12
primera pregunta donde los favoritos (estrella) son más que votos positivos ... +1 ..
Fahim Parkar

Respuestas:

592

Aquí hay algunas macros útiles sobre NSLog que uso mucho:

#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 se usa solo para generar salida cuando se establece la variable DEBUG (-DDEBUG en los indicadores C de los proyectos para la confirmación de depuración).

ALog siempre generará texto (como el NSLog normal).

La salida (por ejemplo, ALog (@ "Hello world")) se verá así:

-[LibraryController awakeFromNib] [Line 364] Hello world
diederikh
fuente
Por qué tienes un ##? Pensé que era para pegar argumentos juntos, pero no estás pegando a nada.
Casebash
1
Esto evita una posible expansión macro de los argumentos
diederikh
Eso puede suceder con las macros en general; Algunas macros producen múltiples líneas. Solo otro argumento para usar siempre llaves ;-).
diederikh
great y cocos2d api tiene la instrucción de registro similar.
Yoon Lee
¿Cómo es que (@"%s [Line %d] " fmt)causa fmtque se agregue a la cadena de control? No he visto esta sintaxis más que esta macro de depuración.
Robert Altman
141

Tomé DLogy ALogdesde arriba, y agregué lo ULogque genera un UIAlertViewmensaje.

Para resumir:

  • DLogsaldrá como NSLogsolo cuando se establece la variable DEBUG
  • ALog siempre saldrá como NSLog
  • ULogmostrará el UIAlertViewúnico cuando se establece la variable DEBUG
#ifdef DEBUG
# define DLog (fmt, ...) NSLog ((@ "% s [Line% d]" fmt), __PRETTY_FUNCTION__, __LINE__, ## __ VA_ARGS__);
#más
# define DLog (...)
#terminara 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__] mensaje: [NSString stringWithFormat: fmt , ## __ VA_ARGS__] delegado: nil cancelButtonTitle: @ "Ok" otherButtonTitles: nil]; [espectáculo de alerta]; }
#más
# define ULog (...)
#terminara si

Esto es lo que parece:

Depurar UIAlertView

+1 Diederik

Whitneyland
fuente
También voy a extender mi código ALog + DLog con ULog. Muy útil.
neoneye
Este código causa un error variable no utilizado en Xcode 5.1 si no se ejecuta en DEBUG :(
yonix
¿Por qué algunas de las directivas #define terminan con punto y coma?
Monstieur
@Locutus Para que no tenga que poner un punto y coma después de la DLogdeclaración. Esto es útil porque si lo hiciera, en las versiones de lanzamiento, DLogse compila a nada, y se quedaría con un punto y coma colgando en su código. Esto no es un error, pero podría generar una advertencia, dependiendo de su configuración, si sigue a otro punto y coma.
Zev Eisenberg
74
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

Nombre de archivo de salida, número de línea y nombre de función:

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

__FUNCTION__en C ++ muestra el nombre destrozado __PRETTY_FUNCTION__muestra un buen nombre de función, en cacao se ven iguales.

No estoy seguro de cuál es la forma correcta de deshabilitar NSLog, lo hice:

#define NSLog

Y no apareció ninguna salida de registro, sin embargo, no sé si esto tiene algún efecto secundario.

stefanB
fuente
20

Aquí una gran colección de constantes de depuración que utilizamos. Disfrutar.

// 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
Equipo de desarrollo de SEQOY
fuente
19

Hay un nuevo truco que ninguna respuesta da. Puedes usar printfen su lugar NSLog. Esto le dará un registro limpio:

Con NSLog usted obtienes cosas como esta:

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

Pero con printfusted solo obtiene:

Hello World

Usa este código

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

Mi respuesta a esta pregunta podría ayudar, parece que es similar a la que Diederik preparó. También es posible que desee reemplazar la llamada NSLog()con una instancia estática de su propia clase de registro personalizada, de esa manera puede agregar una marca de prioridad para mensajes de depuración / advertencia / error, enviar mensajes a un archivo o base de datos, así como a la consola, o prácticamente cualquier otra cosa que se te ocurra.

#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
fuente
Debido a que eludió el %sespecificador de formato que Apple está tratando de desaprobar y evitó la -Wcstring-format-directiveadvertencia de Clang recientemente presentada en 2015.
Jeff
11

Para complementar las respuestas anteriores, puede ser bastante útil usar un reemplazo para NSLog en ciertas situaciones, especialmente al depurar. Por ejemplo, deshacerse de toda la información de fecha / nombre de proceso / id en cada línea puede hacer que la salida sea más legible y más rápida de iniciar.

El siguiente enlace proporciona bastante munición útil para hacer que el registro simple sea mucho más agradable.

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

Quinn Taylor
fuente
11

Es fácil cambiar sus NSLogs existentes para mostrar el número de línea y la clase desde la que se llaman. Agregue una línea de código a su archivo de prefijo:

#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
AddisDev
fuente
3
¡Esto es genial! ¿Cómo harías esto rápidamente?
uplearnedu.com
@AddisDev Me gusta más esto. Muy limpio y sencillo. Solo uso NSLog. ¡No tengo idea de qué son DLog y ULog de todos modos! Gracias. Hasta votado ...
Charles Robertson
@AddisDev Realmente no entiendo por qué Apple no agrega estos datos de vital importancia a NSLog () de forma predeterminada. Extraño ...
Charles Robertson
8

Es simple, por ejemplo

- (nulo) applicationWillEnterForeground: (UIApplication *) aplicación {

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

Salida: - [AppDelegate applicationWillEnterForeground:]

Venkat Reddy
fuente
5

Sobre la base de las respuestas anteriores, esto es lo que plagiado y se me ocurrió. También se agregó el registro de memoria.

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

Nueva incorporación a DLog. En lugar de eliminar totalmente la depuración de la aplicación lanzada, solo desactívela. Cuando el usuario tiene problemas, lo que requeriría una depuración, solo diga cómo habilitar la depuración en la aplicación lanzada y solicite los datos de registro por correo electrónico.

Versión corta: cree una variable global (sí, una solución perezosa y simple) y modifique DLog de esta manera:

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

Respuesta más larga en Jomnius iLessons iLearned: Cómo hacer un registro dinámico de depuración en una aplicación lanzada

JOM
fuente
3

Durante algún tiempo he estado usando un sitio de macros adoptado de varios anteriores. El enfoque de la mina es iniciar sesión en la consola, con énfasis en la verbosidad controlada y filtrada. ; Si no le importan muchas líneas de registro pero desea activar y desactivar fácilmente lotes de ellas, puede que le resulte útil.

Primero, opcionalmente reemplazo NSLog con printf como se describe anteriormente en @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

A continuación, enciendo o apago el inicio de sesión.

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

En el bloque principal, defina varias categorías correspondientes a los módulos en su aplicación. También defina un nivel de registro por encima del cual no se llamarán las llamadas de registro. Luego defina varios sabores de salida 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

Por lo tanto, con la configuración actual de kLOGIFcategory y kLOGIFdetailLTEQ, una llamada como

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

imprimirá pero esto no

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

ni lo hará

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

Si desea anular la configuración de una llamada de registro individual, use un nivel negativo:

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

Creo que los pocos caracteres adicionales de escribir cada línea valen lo que puedo.

  1. Active o desactive una categoría completa de comentarios (por ejemplo, solo informe las llamadas marcadas como Modelo)
  2. informe sobre detalles finos con números de nivel superior o solo las llamadas más importantes marcadas con números más bajos

Estoy seguro de que a muchos les parecerá una exageración, pero en caso de que a alguien le convenga.

cate
fuente