web-dev-qa-db-ja.com

iOS 10 / Xcode 8のデバイス上のNSLogは切り捨てられるようですか?なぜ?

Xcode 8/iOS 10でコンソール出力が不完全と表示されるのはなぜですか?

enter image description here

67
iPeta

一時的な解決策は、すべてのNSLOGをグローバルヘッダーファイルのprintfに再定義するだけです。

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

IOS 10およびXcode 8では、Apple古き良きASL(Appleシステムログ)からUnified loggingという新しいログシステムに切り替えられました。NSLog呼び出しは実際に委任されます。新しいos_log APIへ(ソース: https://developer.Apple.com/reference/os/logging ):

重要

統合ログは、iOS 10.0以降、macOS 10.12以降、tvOS 10.0以降、watchOS 3.0以降で使用でき、ASL(Apple System Logger)およびSyslog APIに優先します。これまで、ログメッセージは/etc/system.logなどのディスク上の特定の場所に書き込まれていました。統合ログシステムは、テキストベースのログファイルに書き込むのではなく、メモリとデータストアにメッセージを保存します。

そして

重要

システムの最大メッセージ長を超えるログメッセージ行は、ロギングシステムによって保存されるときに切り捨てられます。ログコマンドラインツールを使用してアクティビティのライブストリームを表示すると、完全なメッセージが表示されます。ただし、ログデータのストリーミングは高価なアクティビティであることに注意してください。

「システムの最大メッセージ長」制限は、@ Hot_Leaks(出典:<os/log.h>)で示されているように、SDKのヘッダーでフォーマットされた変数の1024文字であることが明らかになっています。

/*!  
 * @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__)

バッファサイズの制限はlibsystem_trace.dylibにハードコーディングされているように見えるため、回避策はありませんが、フォーマットされた変数(%@)の代わりに文字列リテラルを出力するか、フォーマットされた文字列変数を<1024文字列に分割します。

デバッガー(Xcode)はプロセスのout/errorストリームを表示するため、printfはデバッグ中に機能しますが、デバイスログ自体には送信されません。つまり、macOSのConsole Appなどの他のログアプリケーションを使用する場合、またはデバッグされていないアプリケーション(顧客のデバイスで実行されるAppStoreアプリケーションなど)で問題が発生する場合、xfdaiのソリューションは役に立ちません。


デプロイされたアプリケーションへのxfdaiの答えの拡張

デプロイされたアプリケーション/非デバッグビルドでは、NSLogsまたはprintfsを表示する方法はありません。

メッセージをデバイスログに直接印刷する唯一の方法(Xcode->ウィンドウ->デバイス、macのコンソールアプリ、または deviceconsole などのサードパーティユーティリティを使用してアクセスできます)は、os_log API(これはiOS 10以降で使用されているASLの後継です。

IOS 10で_os_log_internalへの呼び出しとしてNSLogを再定義するために使用しているグローバルヘッダーファイルを次に示します。

#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

IOS 10専用の「機能」です。代わりにこれを使用してください:

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

IOS 10の場合:

  1. printf()はXcodeのコンソール内では機能しますが、デバイスのコンソールログでは機能しません。
  2. NSLogは両方の場所で切り捨てられます。

私が今やっているのは、NSLog文字列を行に分割し、各行を個別に記録することです。

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

これはコンソールで機能しますが、読みやすいものではありません。

2
Leslie Godwin

この方法を使用できます。 800文字ごとに分割します。または設定できます。 NSLOG私は1000文字ごとに切り捨てると思います。文字列が800未満の場合、単純なNSLogが使用されます。これは、Jsonの長い文字列に役立ち、コンソールを使用します。 printfは、コンソールではなくXcodeデバッグウィンドウを使用します。

    -(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