在 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 系统记录器)和 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,我没有看到解决办法,只能打印字符串文字而不是格式化变量(%@),或者拆分格式化字符串
printf 将在调试期间工作,因为调试器 (Xcode) 会显示进程的输出/错误流,但不会将其发送到设备日志本身。这意味着在使用其他日志应用程序(例如 macOS 的Console 应用程序)或在非调试应用程序(例如在客户设备上运行的 AppStore 应用程序)上出现问题时,xfdai 的解决方案将无法帮助您。
将 xfdai 的答案扩展到已部署的应用程序
在已部署的应用程序/非调试版本中,无法查看 NSLogs 或 printfs。
将消息直接打印到设备日志(可以使用 Xcode -> Window -> 设备、mac 的控制台应用程序或 3rd 方实用程序,例如 deviceconsole)的唯一方法是调用 os_log API(其中是自 iOS 10 以来使用的ASL 的继承者。
这是我用来在 iOS 10 上将 NSLog 重新定义为对 _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 */