热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

捕获NSLog日志小记

既往不恋,纵情向前原文链接一、NSLog概述1、NSLog是什么NSLog是一个C函数,函数声明如下:Logsanerrormessage

既往不恋,纵情向前

原文链接

一、NSLog概述

1、NSLog是什么
  • NSLog是一个C函数,函数声明如下:

//Logs an error message to the Apple System Log facility.
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;

  • 根据苹果的文档介绍,NSLog的作用是输出信息到标准的Error控制台和 苹果的日志系统(ASL,Apple System Log)里面(iOS 10之前)。

  • iOS10之后,苹果使用新的统一日志系统(Unified Logging System)来记录日志,全面取代ASL的方式,此种方式,是把日志集中存放在内存和数据库里,并提供单一、高效和高性能的接口去获取系统所有级别的消息传递。

  • 新的统一日志系统没有ASL那样的接口可以让我们取出全部日志。

2、NSLog日常使用
  • NSLog在调试阶段,日志会输出到到Xcode中,而在iOS真机上,它会输出到系统的/var/log/syslog这个文件中。

  • 在日常开发中,很多人喜欢使用NSLog来输出调试信息,但是都知道NSLog是比较消耗性能呢,NSLog输出的内容或次数多了之后,甚至会影响App的体验。

  • 于是乎,比较常见的手段是,线上不使用NSLog,DEBUG下才真正使用NSLog。

#if DEBUG
#define MYLOG(fmt, ...) NSLog((@"%s [Line %d] " fmt), PRETTY_FUNCTION, LINE, ##VA_ARGS);
#else
#define MYLOG(fmt,...) {}
#endif

3、常见的日记收集框架
  • 日志收集主要用了两个开源框架来实现:PLCrashReporter与CocoaLumberjack。PLCrashReporter主要用来崩溃日志收集,CocoaLumberjack是用来收集非崩溃日志。
  • CocoaLumberjack中实现了对NSLog日志的捕获。
4、捕获NSLog日志有三种方式
  • iOS 10以前可以通过ASL接口来获取
  • 通过fishhook库hook NSLog方法重定向NSLog函数
  • 使用dup2函数和STDERR句柄重定向NSLog函数

二、获取NSLog的日志输出(iOS 10前)

参考CocoaLumberjack中的DDASLLogCapture实现

1、流程介绍
  • 执行DDASLLogCapturestart方法,启动一个异步全局队列去捕获ASL存储的日志;

+ (void)start {//...dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){[self captureAslLogs];});
}

  • 当日志被保存到ASL的数据库时候,syslogd(系统里用于接收分发日志消息的日志守护进程)会发出一条通知。因为发过来的这一条通知可能有多条日志,需要先将几条日志进行合并。

+ (void)captureAslLogs {//....
}

  • 将获得到的数据转成char 字符串类型,再转成NSString类型,最后封装成DDLogMessage对象,通过[DDLog log: message:] 方法将日志记录下来。

+ (void)aslMessageReceived:(aslmsg)msg {//...
}

说明:以上方法不会影响Xcode控制台的输出,无侵入。

2、注册进程间的系统通知
  • captureAslLogs中通过notify_register_dispatch来注册监听进程间的系统通知;

notify_register_dispatch(kNotifyASLDBUpdate, ¬ifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token){//...});

  • 其中宏kNotifyASLDBUpdate表示:日志被保存在ASL数据库发出的跨进程通知;

/** ASL notifications* Sent by syslogd to advise clients that new log messages have been* added to the ASL database.*/
#define kNotifyASLDBUpdate "com.apple.system.logger.message"

  • 将日志保存到ASL数据库时还有很多通知,比如宏kNotifyVFSLowDiskSpace表示:系统磁盘空间不足,捕获到这个通知时,可以去清理缓存空间,避免缓存写入磁盘失败的情况。

#define kNotifyVFSLowDiskSpace "com.apple.system.lowdiskspace"

三、NSLog重定向

1、介绍
  • 在iOS10之后,新的统一日志系统(Unified Logging System)全面取代ASL,没有ASL那样的接口可以让我们取出全部日志,所以为了兼容新的统一日志系统,你就需要对NSLog日志的输出进行重定向。
  • NSLog 进行重定向,可以采用 Hook的方式。因为 NSLog 本身就是一个 C 函数,可以使用fishhook进行重定向。
  • fishhook是Facebook提供的一个动态修改链接Mach-O文件的工具,能够hook C函数。
2、fishhook原理
  • APP运行时,Mach-O文件被dyld(动态加载器)加载进内存

  • ASLR(地址空间布局随机化)让Mach-O被加载时内存地址随机分配

  • 苹果的PIC位置与代码独立技术,让Mach-O调用系统库函数时,先在Mach-O表中的_DATA段建立一个指针指向外部库函数,dyld加载MachO时知道外部库函数的调用地址,会动态的把_DATA段的指针指向外部库函数

  • fishhook能够替换NSLog等库函数,这事是因为Mach-O的符号表里有NSLog等,可以通过符号表找到NSLog字符串。

说明:具体原理参考iOS逆向工程 - fishhook原理

3、利用fishhook hook NSLog函数

实现代码如下:

//申明一个函数指针用于保存原NSLog的真实函数地址
static void (*orig_nslog)(NSString *format, ...);//NSLog重定向
void redirect_nslog(NSString *format, ...) {//可以添加自己的处理,比如输出到自己的持久化存储系统中//继续执行原来的 NSLogva_list va;format = [NSString stringWithFormat:@"[hook success]%@",format];va_start(va, format);NSLogv(format, va);va_end(va);
}int main(int argc, const char * argv[]) {@autoreleasepool {struct rebinding nslog_rebinding = {"NSLog",redirect_nslog,(void*)&orig_nslog};rebind_symbols((struct rebinding[1]){nslog_rebinding}, 1);NSLog(@"%@, hello word!",@"ss");}return
}//[hook success]ss, hello word!

  • 利用fishhook对方法的符号地址进行了重新板顶,从而只要是NDSLog的调用就会转向redirect_nslog方法调用。

参考:使用fishhook hook NSLog 函数

四、dup2重定向

1、介绍
  • NSLog最后重定向的句柄是STDERR,NSLog输出的日志内容,最终都通过STDERR句柄来记录,而dup2函数式专门进行文件重定向的;
  • 可以使用dup2重定向STDERR句柄,将内容重定向指定的位置,如写入文件,上传服务器,显示到View上。
2、核心代码
  • 实现重定向,需要通过NSPipe创建一个管道,pipe有读端和写端,然后通过dup2将标准输入重定向到pipe的写端。再通过NSFileHandle监听pipe的读端,最后再处理读出的信息。
  • 之后通过printf或者NSLog写数据,都会写到pipe的写端,同时pipe会将这些数据直接传送到读端,最后通过NSFileHandle的监控函数取出这些数据。

- (void)redirectSTD:(int )fd {NSPipe * pipe = [NSPipe pipe] ;NSFileHandle *pipeReadHandle = [pipe fileHandleForReading] ;int pipeFileHandle = [[pipe fileHandleForWriting] fileDescriptor];dup2(pipeFileHandle, fd) ;[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(redirectNotificationHandle:)name:NSFileHandleReadCompletionNotificationobject:pipeReadHandle] ;[pipeReadHandle readInBackgroundAndNotify];
}- (void)redirectNotificationHandle:(NSNotification *)nf {NSData *data = [[nf userInfo] objectForKey:NSFileHandleNotificationDataItem];NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] ;//可以添加自己的处理,可以将内容显示到View,或者是存放到另一个文件中等等//todo[[nf object] readInBackgroundAndNotify];
}//使用
[self redirectSTD:STDERR_FILENO];

  • 参考:iOS日志获取和实时浏览器显示日志



推荐阅读
  • Flutter 2.* 路由管理详解
    本文详细介绍了 Flutter 2.* 中的路由管理机制,包括路由的基本概念、MaterialPageRoute 的使用、Navigator 的操作方法、路由传值、命名路由及其注册、路由钩子等。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • 本文介绍了如何使用 Node.js 和 Express(4.x 及以上版本)构建高效的文件上传功能。通过引入 `multer` 中间件,可以轻松实现文件上传。首先,需要通过 `npm install multer` 安装该中间件。接着,在 Express 应用中配置 `multer`,以处理多部分表单数据。本文详细讲解了 `multer` 的基本用法和高级配置,帮助开发者快速搭建稳定可靠的文件上传服务。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • Framework7:构建跨平台移动应用的高效框架
    Framework7 是一个开源免费的框架,适用于开发混合移动应用(原生与HTML混合)或iOS&Android风格的Web应用。此外,它还可以作为原型开发工具,帮助开发者快速创建应用原型。 ... [详细]
  • 本文介绍了如何使用 CMD 批处理脚本进行文件操作,包括将指定目录下的 PHP 文件重命名为 HTML 文件,并将这些文件复制到另一个目录。 ... [详细]
  • 在PHP中,为了更高效地打开和读取目录并列出其中的文件,可以使用一个自定义函数来返回查询目录下的文件和文件夹列表。该函数会将结果以数组形式返回,并明确区分每个条目是文件还是目录,从而提供更友好和实用的输出。此外,该函数还可以进一步扩展,支持递归查询子目录,以便更全面地获取目录结构信息。 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 在使用Eclipse进行调试时,如果遇到未解析的断点(unresolved breakpoint)并显示“未加载符号表,请使用‘file’命令加载目标文件以进行调试”的错误提示,这通常是因为调试器未能正确加载符号表。解决此问题的方法是通过GDB的`file`命令手动加载目标文件,以便调试器能够识别和解析断点。具体操作为在GDB命令行中输入 `(gdb) file `。这一步骤确保了调试环境能够正确访问和解析程序中的符号信息,从而实现有效的调试。 ... [详细]
  • 本报告对2018年湘潭大学程序设计竞赛在牛客网上的时间数据进行了详细分析。通过统计参赛者在各个时间段的活跃情况,揭示了比赛期间的编程频率和时间分布特点。此外,报告还探讨了选手在准备过程中面临的挑战,如保持编程手感、学习逆向工程和PWN技术,以及熟悉Linux环境等。这些发现为未来的竞赛组织和培训提供了 valuable 的参考。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
author-avatar
miya的发现王国sGA_998
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有