【问题标题】:NSLog on devices in iOS 10 / Xcode 8 seems to truncate? Why?iOS 10 / Xcode 8 设备上的 NSLog 似乎被截断了?为什么?
【发布时间】:2020-01-07 15:13:39
【问题描述】:

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

【问题讨论】:

  • 只是出于好奇,两张图片中“a”字符的数量是否相同?字符串到底有多长?
  • a的个数不一样,正好是1022
  • 所以第一个破折号是 1023?听起来只打印了前 1023 个字符。
  • 我打印的HTTP响应体不完整,以上只是一个例子
  • 尝试使用printf 而不是NSLog forums.developer.apple.com/message/161367#161367

标签: ios objective-c xcode ios10 xcode8


【解决方案1】:

漂亮的功能和线条

#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 答案

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案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 个字符限制,这非常有用。谢谢!
      【解决方案4】:

      在 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 所述(来源:&lt;os/log.h&gt;),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 */
      

      【讨论】:

      • 这是一个很好的答案。确认这不是错误。
      • 在我看来这是一个糟糕的实现。与其在每次调用 NSLog 时调用这些“if”语句和变量赋值,不如在启动时对其进行一次测试,并将其结果设置为可以在宏中引用的全局变量
      • @ishahak 我的目的是演示os_log API 的使用。欢迎您根据需要编辑代码。
      【解决方案5】:

      在 iOS 10 上:

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

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

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

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

      【讨论】:

        【解决方案6】:

        临时解决办法,只需在全局头文件中将所有NSLOG重新定义为printf即可。

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

        【讨论】:

        • 也为我工作:)
        • 我的 NSLog 输出截断了我的序列化 JSON NSData。很烦人。这对我也有用。如果粘贴到 .m 文件的顶部,我只是建议将分号去掉。
        • 如果你想要日期,你可以这样做: printf("%s %s\n", [[[NSDate date] description] UTF8String], [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String])
        【解决方案7】:

        这是 iOS 10 独有的“功能”。改用这个:

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

        【讨论】:

        • 如果我需要打印一个 NSDictionary 怎么办?这太疯狂了。
        • @DeepakSharma,xfdai 提供的解决方案适用于 NSDictionaries。
        猜你喜欢
        • 2017-01-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多