一个临时解决方案,只需要在全局头文件中重新定义所有的 NSLOG
为 printf
。
#define NSLog(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
在iOS 10和Xcode 8中,苹果公司从传统的ASL
(Apple System Log)转向了一种名为Unified logging
的新日志系统。实际上,NSLog
调用委托给新的os_log
API。(出处:https://developer.apple.com/reference/os/logging):
重要提示
Unified logging可在iOS 10.0及更高版本、macOS 10.12及更高版本、tvOS 10.0及更高版本和watchOS 3.0及更高版本中使用,取代了ASL(Apple System Logger)和Syslog APIs。历史上,日志消息被写入到磁盘上特定的位置,例如/etc/system.log。统一日志系统将消息存储在内存和数据存储中,而不是写入基于文本的日志文件。
还有:
重要提示
当日志系统存储大于系统最大消息长度的日志消息时,它们会被截断。使用日志命令行工具查看实时活动流时,完整的消息是可见的。但请记住,流式日志数据是一项昂贵的活动。
SDK头文件中显示,格式化变量的“系统最大消息长度”限制为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__)
由于缓冲区大小限制似乎已经硬编码到 libsystem_trace.dylib
中,我看不到除了打印字符串文字(%@
)或将格式化字符串变量分成 < 1024 字符串之外的其他解决方法。
printf
在调试期间可以使用,因为调试器(Xcode)显示进程的输出 / 错误流,但它不会被发送到设备日志本身。这意味着 xfdai 的解决方案不能帮助您使用其他日志应用程序(如 macOS 的 Console
应用程序),或者在非调试应用程序(例如运行在客户设备上的 AppStore 应用程序)上出现问题时。
将 xfdai 的答案扩展到部署应用程序
在部署的应用程序 / 非调试构建中,无法查看 NSLog
或 printf
。
唯一的直接将消息打印到设备日志(可使用 Xcode -> 窗口 -> 设备、mac 的 Console 应用程序或第三方实用程序(如 deviceconsole)访问)的方法是调用 os_log
API(自 iOS 10 起使用的 ASL
的后继者)。
这是我正在使用的全局头文件,将 NSLog
重定义为在 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 */
os_log
API。您可以根据需要编辑代码。 - Elist这是一个仅适用于iOS 10的“功能”。请使用以下替代方法:
printf("%s", [logString UTF8String]);
-(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);
}
}
printf()
在Xcode的控制台中可以使用,但无法在设备的控制台日志中使用。NSLog
在两个地方都被截断。NSLog
字符串分成几行,并逐行记录每一行。- (void) logString: (NSString *) string
{
for (NSString *line in [string componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]])
{
NSLog(@"%@", line);
}
}
漂亮的函数和行号
#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])
extension String {
/// Splits the string into chunks to accommodate a specified maximum length,
/// considering line breaks as the preferred splitting points.
///
/// - Parameters:
/// - maxLength: The maximum length of each output chunk. The default value is 1000.
/// - Returns: An array of `Substring` chunks.
///
/// - Note: The function iterates through the original string and creates chunks of text
/// based on the specified maximum length. If a line break character is found
/// within the chunk, the split occurs at the line break. If no line break
/// character is present, the function tries to split at the last space character
/// before the maxLength. If no space is found, the chunk is split at the
/// maxLength. The line break character or space character (if used for
/// splitting) is dropped from the output.
///
/// - Complexity: The time complexity is O(n), where n is the number of characters
/// in the string. The function iterates through the string once to create the
/// chunks.
public func splittedForLogger(maxLength: Int = 1000) -> [Substring] {
var chunks: [Substring] = []
var currentIndex = self.startIndex
while currentIndex < self.endIndex {
let remainingLength = self.distance(from: currentIndex, to: self.endIndex)
let chunkLength = min(maxLength, remainingLength)
let nextIndex = self.index(currentIndex, offsetBy: chunkLength)
let chunk = self[currentIndex..<nextIndex]
if chunkLength == remainingLength {
/// Last chunk
chunks.append(chunk)
break
}
/// Attempt to find the last line break character within the chunk
/// If not found, attempt to find the last space character
/// If neither line break nor space character is found, split at the maxLength
let splitIndex = chunk.lastIndex { character in
CharacterSet.newlines.contains(character.unicodeScalars.first ?? .init(0))
} ?? chunk.lastIndex { character in
CharacterSet.whitespaces.contains(character.unicodeScalars.first ?? .init(0))
} ?? chunk.endIndex
let splitChunk = self[currentIndex..<splitIndex]
chunks.append(splitChunk)
currentIndex = splitIndex < chunk.endIndex ? self.index(after: splitIndex) : nextIndex
}
return chunks
}
@inlinable public func forEachLoggerChunk(
maxLength: Int = 1000,
_ body: (Substring) throws -> Void
) rethrows {
try self
.splittedForLogger(maxLength: maxLength)
.forEach(body)
}
}
logger.debug("\(someLongString)")
someLongString.forEachLoggerChunk { logger.debug("\($0)") }
结果:
这并不提供良好的输出,但会在控制台上打印长日志所需的所有必要信息。
func Log(_ logString: String?) {
if logString?.isEmpty ?? false { return }
NSLog("%@", logString!)
Log(String(logString!.dropFirst(1024)))
}
printf
代替NSLog
。https://forums.developer.apple.com/message/161367#161367 - TonyMkenu