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


73

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

在此处输入图片说明


出于好奇,两个图像中“ a”字符的数量是否相同?字符串到底有多长?
热门点击

“ a”的数量不相同,恰好是1022
iPeta

那么到第一个破折号是1023吗?听起来只打印了前1023个字符。
热门点击

我打印的HTTP响应正文不完整,以上仅是一个示例
iPeta 16'9

Answers:


73

治标不治本,只是重新定义都NSLOGprintf在全球头文件。

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

也为我工作:)
Ankit

2
我的NSLog输出正在截断序列化的JSON NSData。很烦人。这也对我有用。如果粘贴到您的.m文件顶部,我建议不要使用分号。
卡尔·海恩

如果需要日期,可以执行以下操作:printf(“%s%s \ n”,[[[NSDate date] description] UTF8String],[[NSString stringWithFormat:FORMAT,## __ VA_ARGS__] UTF8String])
matt bezark '18

49

在iOS 10和Xcode 8中,Apple从旧版本ASL(Apple System Log)切换到了名为的新日志系统Unified loggingNSLog调用实际上是委派给新的os_logAPI。(来源: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。统一日志记录系统将消息存储在内存和数据存储中,而不是写入基于文本的日志文件。

重要

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

@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__)

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

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


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

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

将消息直接打印到设备日志(可以使用Xcode-> Window->设备,mac的Console App或诸如deviceconsole之类的第三方工具访问)的唯一方法是调用os_logAPI(ASL自iOS 10开始使用) )。

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

5
这是一个很好的答案。确认这不是错误。
d00dle

1
我认为这是一个糟糕的实现。与其在每次NSLog调用时都调用这些“ if”语句和变量赋值,不如在启动时对其进行一次更好的测试,并将其结果设置为可在宏中引用的全局变量
ishahak

1
@ishahak我的目的是演示os_logAPI的用法。欢迎您根据需要编辑代码。
Elist,2016年

10

这是仅iOS 10的“功能”。使用此代替:

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

3
如果我需要打印NSDictionary怎么办?这太疯狂了。
Deepak Sharma

@ DeepakSharma,xfdai提供的解决方案适用于NSDictionaries。
pir800

3

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

    }

如果您需要超过设备控制台日志中的1024个字符的限制,则此方法非常有用。谢谢!
AndrewJC

2

在iOS 10上:

  1. printf() 在Xcode的控制台内运行,但在设备的控制台日志上不起作用。
  2. NSLog 在两个地方都截断。

我现在要做的是将NSLog字符串分成几行,并分别记录每行。

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

这在控制台上有效,但不容易阅读。


0

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

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

0

从@xfdai答案,添加漂亮的功能和行

有日期

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.