今天就跟大家聊聊有关APP是如何高效保存日志,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
有过嵌入式开发经验的都知道日志的存储是个比较麻烦的问题。之前ARM开源了一款项目:cmbacktrace,github地址就不贴了,可以将crash时的堆栈信息进行保存。但是对于手机app来说,光保存程序崩溃时的日志信息时远远不够的,根本无法定位到具体原因。在讲解淘系使用的日志系统之前,先看一下最通用的日志系统。
通用方案一
主流的日志模块比如logback,是属于实时日志记录系统,也就是每产生一句日志就进行加密存入磁盘文件。这样有个缺点就是I/O过于密集占用大量CPU资源,影响程序性能,极易卡顿。一般使用与app开发调试阶段,正式发版肯定要把log功能关闭,等用户产生程序崩溃等反应问题时,才重新打一个调试包进行复现。但是很多时候场景不完全相同,很难复现问题。
前文有讲过cache数据与磁盘数据之间的关系:进程是如何使用内存的?
程序写文件操作的时候,并不是直接操作磁盘里的文件,而是先把数据写入系统缓存,也就是脏页中,然后操作系统的守护进程update进程会定时(一般为30s)将脏页更新到磁盘里。数据写入磁盘存在两次拷贝:从用户空间内存拷贝到内核空间内存,然后从内核空间flush写入到磁盘。加上具体写入磁盘的操作无法由程序控制,因此这里会造成CPU峰值过高导致性能降低。
通用方案二
直接使用Android的logsdk当然方便,但是会造成性能问题,那么我们可以先将日志保存到内存缓存,达到一定大小后再加密写入文件。同时为了减少数据流,先对数据进行压缩再写入(借鉴HTTP网络传输的做法)。整体方案如下图:
这种方案不需要实时保存日志,不会产生CPU峰值。但是丢日志的问题仍然没解决。比如程序crash异常退出时,很多场景并不会有系统事件通知,也就无法保存crash时的堆栈。鹅厂的嵌入式操作系统Tencenttiny OS宣称在stm32系列单板上解决了这个问题,但是使用起来仍然差强人意。更为重要的是,Android系统比嵌入式系统复杂的多,很多方案无法直接套用。
手淘优化方案
手淘app主要从两个方面来提高日志使用效率。
mmap
通过调用mmap存储映射I/O,具体方案就是将磁盘文件映射到用户空间缓冲区上,对内存缓冲区执行数据操作时,相当于操作磁盘中的文件。这样的好处就是不需要使用read和write。映射方案如下图所示。
使用mmap的好处是免去一次实时数据拷贝,同时可以通过调用msync、munmap将数据脏页写回磁盘文件,对于应用层可控(起一个后台线程异步执行就行)。优化后具体效果可以参看APUE图14-28。
这里要注意,intmadvise(caddr_t addr, size_t len, int advice);这个接口在Unix说明是可以通过使用madv_willneed参数来预加载磁盘文件到内存,但是在mac上实验并没什么效果,因此妥善的方案是对每个页执行读取一个字节的操作,这样保证文件加载到内存中。
数据压缩
数据是先加密还是先压缩?标准做法是先压缩再加密。明文一般都有冗余度,压缩之后内容会变少。相反如果先加密,会破坏文件的冗余度。通用的压缩方法主要有gzip、huffman等,手淘借鉴HTTP2的hpack头部压缩,在facebook开源的zstd压缩算法基础上进行了网络传输的优化。因为日志中通常会有大量相同特性的短语,因此会在服务端常驻一个字典,并且适时更新由客户端拉取,通过字典压缩提升压缩率。同时为了防止数据损坏影响整个压缩包无法解压,采用流式压缩(即日志达到32k时进行压缩)。分块压缩虽然总体效率略低,但是因为不会在短时间内几种打包压缩,不会造成CPU峰值。
实际使用过程中还对外置SD卡进行了适配,当SD卡被删除时会将日志存入临时缓存,每次进程被杀掉时清理缓存,防止长时间占用用户空间造成舆情。
看完上述内容,你们对APP是如何高效保存日志有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。