谈谈客户端开发中日志
背景
最近在开发的过程中遇到一个复现概率很低的BUG--项目中有个头像控件专门用于头像的下载,但是线上有用户反馈会出现串头像的问题,比如用户A的头像会显示为用户B,用户B的头像会显示为用户C的头像。这个问题测试小妹那边也是没有办法复现,我们在测试的时候也一头雾水,我们遇到了一个棘手的问题。不过方法总比问题多,最终我们使用了日志的方式定位并且成功解决了问题。把这个过程做个记录以备时有之需
头像控件的设计
首先来说下为什么需要单独设计一个头像控件?这个头像功能是什么呢?该控件做了哪些事情呢?
这还得从我们用户系统的用户头像URL说起,我们用户系统的用户头像URL是固定的,也就是说用户A的用户头像URL是固定的,比如用户A的ID为12345678
,那么用户A的头像URL就是类似http://myserver.com/avatr_12345678
这样的,用户更新了之后,这个URL也是不变的。因此我们会遇到如下的几个场景需要处理头像更新的问题
- 问题1:用户A更新了自己的头像,用户A需要立刻更新自己的头像并且设计已存在的缓存失效重新设计缓存
- 问题2:用户A更新自己的头像,其它用户需要有合适的时机更新到用户A的头像
另外我们还会遇到遇到更新头像的方案问题,这个问题是针对上面的这个场景的:问题2:用户A更新自己的头像,其它用户需要有合适的时机更新到用户A的头像
- 什么时机(时间问题,实时性和流量之间平衡)
- 怎么更新(HTTP请求的缓存方案,使用etag)
综上的分析头像控件的大体流程会是这样的,Sync表示同步,正常是在主线程,Async表示异步,是在子线程队列
- [Sync]1、开始请求头像
- [Sync]2、清除上一个的头像请求
- [Sync]3、判断是否是下载失败的头像
- [Sync-Sync]4、是下载失败,设置占位图
- [Sync]5、不是下载失败执行下一步
- [Sync]6、判断当前显示是否需要下载的图片(列表复用的场景,从下载完成、有图片、有图片url、图片url和下载的url一致、缓存tag一致)
- [Sync-Sync]7、是直接设置图片
- [Sync]8、不是执行下一步
- [Async]9、判断是否需要执行下载
- [Async]10、有缓存(内存缓存以及磁盘缓存)图片 && 缓存过期 -> 需要执行下载
- [Async]11、没有缓存图片-> 需要执行下载
- [Async]12、其它(图片 && 缓存没过期) -> 不需要执行下载
- [Async]13、执行下载,携带上一次的etag(如果有)处理服务端的缓存
- [Async-Sync]14、不不需要执行下载,设置缓存图片即可(有缓存不需要)
- [Async-Sync]15、下载完成,处理图片更新UI,更新缓存、更新etag
定位问题
从上面流程可以看到流程链比较长,并且涉及到线程的调度,这就会导致断点调试的困难,比如断点是在主线程,但是子线程的异步可能很快就过去了,按真实的执行时间进入到对应的断点,这时候需要使用日志进行记录,为了更好的看到关键步骤中的数据,日志中需要添加记录关键步骤中使用的标识,这个场景中使用的是图片的URL即可,另外在缓存获取以及保存缓存阶段,如果需要跟踪对应的图片,可以采用保存临时文件的方式,临时文件的命名可以采取URL的PATH+随机数的方式,然后把该文件保存到本地,把文件地址记录在日志中。
根据以上的分析,在几个关键的地方添加日志记录:
[Sync]1、开始请求头像
,日志记录内容为下载图片URL[Async]9、判断是否需要执行下载
,日志记录内容为下载图片URL,是否执行下载BOOL值,缓存图片[Async]13、执行下载,携带上一次的etag(如果有)处理服务端的缓存
,日志记录内容为下载图片URL[Async-Sync]15、下载完成,处理图片更新UI,更新缓存、更新etag
,日志记录内容为下载图片URL,下载的图片
然后运行会获取到对应的日志,下面是一个头像控件连续两次下载不同头像输出的日志

日志是按时间先后的,可以看到下载第二张图片的时候其实第一个下载还没有真的开始执行,会导致[Sync]2、清除上一个的头像请求
这个步骤是无效的,最终还是有两个下载请求在执行,因为请求是异步执行返回的时机不确定,会导致概率性的显示错误以及缓存的错误。
解决
找到了问题之后解决的方式也就比较容易了,在真实发起请求的步骤取消上一次的请求即可。