iOS 10 / Xcode 8 中的设备上的 NSLog 似乎被截断了?为什么?

NSLog on devices in iOS 10 / Xcode 8 seems to truncate? Why?

为什么控制台输出显示 Xcode 8 / iOS 10 不完整?

只有 iOS 10 "feature"。改用这个:

printf("%s", [logString UTF8String]);

临时解决方案,只需在全局头文件中重新定义所有NSLOGprintf即可。

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

在 iOS 10:

  1. printf() 在 Xcode 的控制台内工作,但在设备的控制台日志上不起作用。
  2. NSLog 两处截断。

我现在正在做的是将我的 NSLog 字符串拆分成行并单独记录每一行。

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

这适用于控制台,但不容易阅读。

你可以用这个方法。每 800 个字符拆分一次。或者可以设置。 NSLOG 我认为每 1000 个字符截断一次。 如果字符串小于 800 将使用简单的 NSLog。 这对于 Json 长字符串很有用,并使用控制台。 printf 使用 Xcode 调试 window 而不是控制台。

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

    }

在 iOS 10 和 Xcode 8 中,Apple 从旧的 ASL(Apple 系统日志)切换到名为 Unified logging 的新日志系统。 NSLog 呼叫实际上委托给了新的 os_log API。 (来源:https://developer.apple.com/reference/os/logging):

Important

Unified logging is available in iOS 10.0 and later, macOS 10.12 and later, tvOS 10.0 and later, and watchOS 3.0 and later, and supersedes ASL (Apple System Logger) and the Syslog APIs. Historically, log messages were written to specific locations on disk, such as /etc/system.log. The unified logging system stores messages in memory and in a data store, rather than writing to text-based log files.

Important

Log message lines greater than the system’s maximum message length are truncated when stored by the logging system. Complete messages are visible when using the log command-line tool to view a live stream of activity. Bear in mind, however, that streaming log data is an expensive activity.

"system’s maximum message length" 限制在 SDK 的 header 中显示为格式化变量的 1024 个字符,如 @Hot_Leaks 所述(来源:<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__)

由于缓冲区大小限制似乎是 hard-coded 到 libsystem_trace.dylib,我看不到解决它的方法,只能打印字符串文字而不是格式化变量(%@), 或将格式化字符串变量拆分为 < 1024 个字符串。

printf 将在调试期间工作,因为调试器 (Xcode) 显示进程的输出/错误流,但它不会发送到设备日志本身。这意味着 xfdai 的解决方案将无法帮助您使用其他日志应用程序,例如 macOS 的 Console App,或者 non-debugged 应用程序(例如 AppStore)出现问题客户设备上的应用程序 运行)。


扩展 xfdai 对已部署应用程序的回答

在已部署的应用程序/non-debug 构建中,无法看到 NSLogs 或 printfs。

将消息直接打印到设备日志的唯一方法(可以使用 Xcode -> Window -> 设备,mac 的控制台应用程序或第三个访问deviceconsole 等派对实用程序正在调用 os_log API 的(这是自 iOS 10 以来使用的 ASL 的继任者)。

这是我用来重新定义 NSLog 的全局 header 文件,作为对 iOS 10 上 _os_log_internal 的调用:

#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 */

这不会提供很好的输出,但会打印长日志的所有必要信息,即使在控制台上也是如此。

func Log(_ logString: String?) {
    if logString?.isEmpty ?? false { return }
    NSLog("%@", logString!)
    Log(String(logString!.dropFirst(1024)))
}

漂亮的功能和线条

#define NSLog(FORMAT, ...) printf("%s:%d %s\n", __PRETTY_FUNCTION__,__LINE__,[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String])

日期

#define NSLog(FORMAT, ...) printf("%s %s:%d %s\n", [[[NSDate date] description] UTF8String],__PRETTY_FUNCTION__,__LINE__,[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String])

完善@xfdai的回答