NSLog en dispositivos en iOS 10 / Xcode 8 parece truncar? Por qué?


¿Por qué la salida de la consola se muestra incompleta en Xcode 8 / iOS 10?

introduzca la descripción de la imagen aquí

Author: iPeta, 2016-09-20

5 answers

Una solución temporal, simplemente redefine todo NSLOG a printf en un archivo de encabezado global.

#define NSLog(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
 64
Author: xfdai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-21 01:55:29

En iOS 10 y Xcode 8, Apple cambió del viejo ASL (Registro del sistema de Apple) a un nuevo sistema de registro llamado Unified logging. NSLog de hecho, las llamadas están delegando a nuevas API os_log. (fuente: https://developer.apple.com/reference/os/logging):

Importante

El registro unificado está disponible en iOS 10.0 y versiones posteriores, macOS 10.12 y más tarde, tvOS 10.0 y más tarde, y watchOS 3.0 y posteriores, y reemplaza ASL (Apple System Logger) y el Syslog API. Históricamente, el registro los mensajes se escribieron en ubicaciones específicas en el disco, como /etc / system.registro. El sistema de registro unificado almacena mensajes en memoria y en un almacén de datos, en lugar de escribir en archivos de registro basados en texto.

Y

Importante

Las líneas de mensajes de registro mayores que la longitud máxima del mensaje del sistema se truncan cuando son almacenadas por el sistema de registro. Los mensajes completos son visible cuando se utiliza la herramienta de línea de comandos log para ver una transmisión en vivo de actividad. Tenga en cuenta, sin embargo, que la transmisión de datos de registro es un actividad costosa.

La limitación de "longitud máxima del mensaje del sistema" se revela en el encabezado del SDK como 1024 caracteres para las variables formateadas, como señaló @Hot_Leaks (fuente: <os/log.h>):

/*!  
 * @function os_log  
 *   
 * ...  
 *  
 * There is a physical cap of 1024 bytes per log line for dynamic content,  
 * such as %s and %@, that can be written to the persistence store.  
 * All content exceeding the limit will be truncated before it is  
 * written to disk.  
 *
 * ... 
 *
 */  
#define os_log(log, format, ...)    os_log_with_type(log, OS_LOG_TYPE_DEFAULT, format, ##__VA_ARGS__)

Dado que la limitación del tamaño del búfer parece estar codificada en libsystem_trace.dylib, no veo una forma de evitarlo, sino imprimir un literal de cadena en lugar de una variable formateada (%@), o dividir las variables de cadena formateadas a

printf funcionará durante la depuración, ya que el depurador (Xcode) muestra los flujos de salida / error del proceso, pero no se enviará al registro del dispositivo en sí. Esto significa que la solución de xfdai no le ayudará al usar otras aplicaciones de registro, como la aplicación Console de macOS, o con problemas emergentes en aplicaciones no depuradas (como la aplicación AppStore que se ejecuta en el dispositivo del cliente).


Extendiendo la respuesta de xfdai a aplicaciones desplegadas

En aplicaciones implementadas / compilaciones sin depuración, no hay forma de ver NSLogs o printfs.

La única forma de imprimir mensajes directamente en el registro del dispositivo (al que se puede acceder usando Xcode -> Window -> Devices, la Aplicación de consola de mac o utilidades de terceros como deviceconsole) es llamar a os_log API (que es el sucesor de ASL utilizado desde iOS 10).

Aquí hay un archivo de encabezado global que estoy usando para redefinir NSLog como una llamada para _os_log_internal en iOS 10:

#ifndef PrefixHeader_pch
#define PrefixHeader_pch

#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif

#import <os/object.h>
#import <os/activity.h>

/*
 *  System Versioning Preprocessor Macros
 */

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

// os_log is only supported when compiling with Xcode 8.
// Check if iOS version > 10 and the _os_log_internal symbol exists,
// load it dynamically and call it.
// Definitions extracted from #import <os/log.h>

#if OS_OBJECT_SWIFT3
OS_OBJECT_DECL_SWIFT(os_log);
#elif OS_OBJECT_USE_OBJC
OS_OBJECT_DECL(os_log);
#else
typedef struct os_log_s *os_log_t;
#endif /* OS_OBJECT_USE_OBJC */

extern struct os_log_s _os_log_default;

extern __attribute__((weak)) void _os_log_internal(void *dso, os_log_t log, int type, const char *message, ...);

// In iOS 10 NSLog only shows in device log when debugging from Xcode:
#define NSLog(FORMAT, ...) \
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10.0")) {\
    void(*ptr_os_log_internal)(void *, __strong os_log_t, int, const char *, ...) = _os_log_internal;\
    if (ptr_os_log_internal != NULL) {\
        _Pragma("clang diagnostic push")\
        _Pragma("clang diagnostic error \"-Wformat\"")\
        _os_log_internal(&__dso_handle, OS_OBJECT_GLOBAL_OBJECT(os_log_t, _os_log_default), 0x00, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);\
        _Pragma("clang diagnostic pop")\
    } else {\
        NSLog(FORMAT, ##__VA_ARGS__);\
    }\
} else {\
    NSLog(FORMAT, ##__VA_ARGS__);\
}

#endif /* PrefixHeader_pch */
 33
Author: Elist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-06 10:17:28

Es un iOS 10 solo "característica". Usa esto en su lugar:

printf("%s", [logString UTF8String]);
 7
Author: Tamás Cseh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-20 13:43:26

En iOS 10:

  1. printf() funciona dentro de la consola de Xcode, pero no funciona en el registro de consola del dispositivo.
  2. NSLog trunca en ambos lugares.

Lo que estoy haciendo por ahora es dividir mis NSLog cadenas en líneas y registrar cada línea individualmente.

- (void) logString: (NSString *) string
{
    for (NSString *line in [string componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]])
    {
        NSLog(@"%@", line);
    }
}

Esto funciona en la consola, pero no es fácil de leer.

 2
Author: Leslie Godwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-30 13:33:07

Puede utilizar este método. Divida cada 800 caracteres. O se puede fijar. NSLOG creo que trunca cada 1000 caracteres. Si la cadena es menor que 800 se utilizará un NSLog simple. Esto es útil para cadenas largas Json y utiliza la consola. printf usa la ventana de depuración de Xcode, no la consola.

    -(void) JSLog:(NSString*)logString{

            int stepLog = 800;
            NSInteger strLen = [@([logString length]) integerValue];
            NSInteger countInt = strLen / stepLog;

            if (strLen > stepLog) {
            for (int i=1; i <= countInt; i++) {
                NSString *character = [logString substringWithRange:NSMakeRange((i*stepLog)-stepLog, stepLog)];
                NSLog(@"%@", character);

            }
            NSString *character = [logString substringWithRange:NSMakeRange((countInt*stepLog), strLen-(countInt*stepLog))];
            NSLog(@"%@", character);
            } else {

            NSLog(@"%@", logString);
            }

    }
 1
Author: Andy Vene,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-16 14:01:52