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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39584707/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 09:17:20  来源:igfitidea点击:

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

iosobjective-cxcodeios10xcode8

提问by iPeta

Why the console output shows incomplete in Xcode 8 / iOS 10?

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

enter image description here

在此处输入图片说明

采纳答案by xfdai

A temporary solution, just redefine all NSLOGto printfin a global header file.

一个临时解决方案,只需在全局头文件中重新定义 allNSLOG即可printf

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

回答by Elist

In iOS 10 & Xcode 8, Apple switched from the good old ASL(Apple System Log) to a new logging system called Unified logging. NSLogcalls are in fact delegating to new os_logAPI's. (source: https://developer.apple.com/reference/os/logging):

在 iOS 10 和 Xcode 8 中,Apple 从旧的ASL(Apple System Log)切换到了一个名为Unified logging. NSLog调用实际上是委托给新​​的os_logAPI。(来源: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.

重要的

统一日志记录适用于 iOS 10.0 及更高版本、macOS 10.12 及更高版本、tvOS 10.0 及更高版本以及 watchOS 3.0 及更高版本,并取代 ASL(Apple System Logger)和 Syslog API。历史上,日志消息被写入磁盘上的特定位置,例如 /etc/system.log。统一日志系统将消息存储在内存和数据存储中,而不是写入基于文本的日志文件。

And

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.

重要的

当日志系统存储时,大于系统最大消息长度的日志消息行将被截断。使用日志命令行工具查看活动的实时流时,可以看到完整的消息。但是请记住,流式传输日志数据是一项昂贵的活动。

The "system's maximum message length" limitation is revealed in the SDK's header to be 1024 characters for formatted variables, as noted by @Hot_Leaks (source: <os/log.h>):

正如@Hot_Leaks(来源:)所指出的,“系统的最大消息长度”限制在 SDK 的标头中显示为 1024 个字符的格式化变量<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__)

Since the buffer size limitation seems to be hard-coded into libsystem_trace.dylib, I don't see a way around it but to print a string literal instead of a formatted variable (%@), or split the formatted string variables to < 1024 strings.

由于缓冲区大小限制似乎被硬编码到 中libsystem_trace.dylib,我没有找到解决方法,只能打印字符串文字而不是格式化变量 ( %@),或者将格式化字符串变量拆分为 < 1024 个字符串。

printfwill work during debugging, since the debugger (Xcode) shows the process's out / error streams, but it will not be sent to the device log itself. This means that xfdai's solution will not help you when using other log applications such as macOS's ConsoleApp, or with issue's emerging on non-debugged applications (such as AppStore application running on customer's device).

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



Extending xfdai's answer to deployed applications

将 xfdai 的答案扩展到已部署的应用程序

In deployed applications / non-debug builds, there's no way to see either NSLogs or printfs.

在已部署的应用程序/非调试版本中,无法看到NSLogs 或printfs。

The only way to have messages printed directly to the device log (which can be accessed using Xcode -> Window -> Devices, mac's Console App or 3rd party utilities such as deviceconsole) is calling os_logAPI's (which is the successor of ASLused since iOS 10).

将消息直接打印到设备日志的唯一方法(可以使用 Xcode -> Window -> Devices、mac 的控制台应用程序或第 3 方实用程序(例如deviceconsole)进行访问)是调用os_logAPI(这是ASL自 iOS 10 以来使用的继承者))。

Here's a global header file I'm using to redefine NSLogas a call to _os_log_internalon iOS 10:

这是我用来重新定义NSLog_os_log_internal对 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 */

回答by Tamás Cseh

It's an iOS 10 only "feature". Use this instead:

这是 iOS 10 唯一的“功能”。改用这个:

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

回答by Leslie Godwin

On iOS 10:

在 iOS 10 上:

  1. printf()works inside Xcode's console but doesn't work on the device's console log.
  2. NSLogtruncates in both places.
  1. printf()在 Xcode 的控制台内工作,但不适用于设备的控制台日志。
  2. NSLog在两个地方截断。

What I'm doing for now is splitting my NSLogstrings into lines and logging each line individually.

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

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

This works on the console, but isn't easy to read.

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

回答by Andy Vene

You can use this method. Split every 800 chars. Or can be set. NSLOG i think truncate every 1000 chars. If string is less than 800 will use a simple NSLog. This is useful for Json long strings and uses the console. printf uses Xcode debug window not the console.

您可以使用此方法。每 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);
            }

    }

回答by catanore

This doesn't provide a nice output, but prints all necessary information for long logs, even on console.

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

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

回答by Giang

from @xfdai answer, add pretty function and line

来自@xfdai 的回答,添加漂亮的功能和线条

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

with date

有日期

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