web-dev-qa-db-fra.com

NSLog sur les appareils sous iOS 10 / Xcode 8 semble être tronqué? Pourquoi?

Pourquoi la sortie de la console est-elle incomplète dans Xcode 8/iOS 10?

enter image description here

67
iPeta

Une solution temporaire, il suffit de redéfinir tous les NSLOG en printf dans un fichier d’en-tête global.

#define NSLog(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
70
xfdai

Dans iOS 10 et Xcode 8, Apple est passé du bon vieux ASL (journal système Apple) à un nouveau système de journalisation appelé Unified logging. NSLog les appels délèguent en fait à de nouvelles API os_log. (source: https://developer.Apple.com/reference/os/logging ):

Important

La journalisation unifiée est disponible dans iOS 10.0 et versions ultérieures, macOS 10.12 et versions ultérieures, tvOS 10.0 et versions ultérieures, et watchOS 3.0 et versions ultérieures, et remplace ASL (Apple System Logger) et les API Syslog. Historiquement, les messages de journal étaient écrits dans des emplacements spécifiques sur le disque, tels que /etc/system.log. Le système de journalisation unifiée stocke les messages en mémoire et dans un magasin de données, au lieu d'écrire dans des fichiers journaux textuels.

Et

Important

Les lignes de message de journal supérieures à la longueur de message maximale du système sont tronquées lorsqu’elles sont stockées par le système de journalisation. Les messages complets sont visibles lorsque vous utilisez l'outil de ligne de commande du journal pour afficher un flux d'activité en direct. Cependant, gardez à l'esprit que la diffusion de données de journaux est une activité coûteuse.

La limite de "longueur maximale du message du système" est indiquée dans l'en-tête du SDK comme étant de 1024 caractères pour les variables formatées, comme indiqué par @Hot_Leaks (source: <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__)

Étant donné que la limitation de la taille de la mémoire tampon semble être codée en dur dans libsystem_trace.dylib, Je ne vois pas le moyen de la contourner mais d'imprimer un littéral de chaîne au lieu d'une variable mise en forme (%@), Ou fractionnée. les variables de chaîne formatées à <1024 chaînes.

printf fonctionnera pendant le débogage, car le débogueur (Xcode) affiche les flux de sortie/erreur du processus, mais il ne sera pas envoyé au journal du périphérique. Cela signifie que la solution de xfdai ne vous aidera pas lors de l'utilisation d'autres applications de journalisation telles que l'application Console de macOS ou lors de l'apparition de problèmes sur des applications non déboguées (telles que l'application AppStore exécutée sur le périphérique du client).


Étendre la réponse de xfdai aux applications déployées

Dans les applications déployées/les versions sans débogage, il n’ya aucun moyen de voir NSLogs ou printfs.

Le seul moyen de faire imprimer les messages directement dans le journal du périphérique (auquel on peut accéder à l'aide de Xcode -> Fenêtre -> Périphériques, de la console App Mac ou d'utilitaires tiers tels que deviceconsole ) est d'appeler os_log API's (qui est le successeur de ASL utilisé depuis iOS 10).

Voici un fichier d'en-tête global que j'utilise pour redéfinir NSLog comme un appel à _os_log_internal Sur 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 */
40
Elist

C'est une "fonctionnalité" iOS 10 uniquement. Utilisez ceci à la place:

printf("%s", [logString UTF8String]);
9
Tamás Cseh

Sur iOS 10:

  1. printf() fonctionne dans la console de Xcode mais pas dans le journal de la console du périphérique.
  2. NSLog tronque aux deux endroits.

Ce que je fais pour l'instant est de scinder mes chaînes NSLog en lignes et de consigner chaque ligne individuellement.

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

Cela fonctionne sur la console, mais n'est pas facile à lire.

2
Leslie Godwin

Vous pouvez utiliser cette méthode. Fractionner tous les 800 caractères. Ou peut être réglé. NSLOG je pense tronquer tous les 1000 caractères. Si la chaîne est inférieure à 800, un simple NSLog sera utilisé. Ceci est utile pour les chaînes longues Json et utilise la console. printf utilise la fenêtre de débogage Xcode et non la console.

    -(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);
            }

    }
2
Andy Vene