遗留问题设想

1.问题列表                                                                       

a. 如何对au进行数据存储

  • 哪些meta信息需要存储
  • au内部信息是否需要进行提取和存储                                                                                                                                   

b.如何对au进行ads的处理                                                      

  • 随即插入
  • 内容切分?                                     
  • 前部和尾部 

c.  

[软件测试] 可测性分析和实践

软件测试中可测性一般是指对系统的可控性、可观测性进行的评估,借以反映系统设计、实现对测试的友好程度和相应的测试成本。可测性在测试阶段会对系统的测试成本及关联产品代码的Patch次数产生重大影响。如何提高可测性成为软件生命周期特别是前期(设计阶段、coding阶段)重要的一环。 本文带领大家探索在实际项目中可测性相关的实战经验和对应的改进措施。

1 提高可测性的切入点

可测性的评估和改进最早开始于两个阶段:

a. 新项目的设计阶段;

b. 已有项目新功能、新策略的提测阶段。

这些是提高团队设计的系统可测性和维持系统的设计高可测性的关键时间点。测试人员会利用各种场合、机会强化开发人员对于可测性的重视:

  1. 可测性的重要性每次设计讨论会, 测试负责人必提醒大家在设计时注意可测性。否则设计出来的功能很可能需要进行重构。
  2. 可控性

    每次晨会中的讨论环节, 测试负责人会提醒模块设计人员,设计的功能需要必要的外部控制、执行动作支持,否则QA无法精确控制过程及缩短测试耗时。

  3. 可观测性每个功能进行测试设计阶段,提前和开发人员沟通必要的功能,观察结果集合可以系统外部获得,错误结果可以被暴露而不是由内部逻辑完全消化。

那可控性和可观测性又指哪些方面呢? 如何向开发人员合理的解释可测性?

1.1 可控性

可控性指系统的状态可受外部控制改变,而不是由内部模块自发的完成。

举个常见的例子:

A. 当某文件存在的时候,该模块自动退出;

B. 当某pid.lock文件存在时,该模块不能启动,即使启动也退出。

上面的状态改变都是由一个外部的文件控制,拥有可控性。

说到这里,问题来了,拥有可控性就万事大吉了吗? 请大家思考,你在实际项目过程中遇到过哪些有可控性但可控性较差的情况?

1.2 可观测性

可观测性指系统内的重要状态、信息可通过一定手段由外部获得。可观测性不仅能观测系统的输出是否符合设计要求,还影响该系统是否可控。系统的必要状态信息在系统测试控制阶段起决定作用。没有准确的状态信息,测试人员无法判断是否要进行下一步的控制变更。无法控制状态变更,可控性又从何谈起?

口说无凭,我们来看几个作者实际项目中遇到的真实案例。

2 实战分析

[1] 垃圾回收GC

垃圾回收GC模块是常见的系统内模块,相信很多测试人员遇到过下面场景或者类似场景:

开发人员终于在大吼一声后宣布垃圾回收模块完成,她的描述如下:

1) 该模块定时自动触发。触发条件是每天晚上1点。

2) 该模块触发后每秒的处理量是N/s。是根据线上情况得到的经验值,硬编码到代码中。

然后,就没有然后了。

测试人员一阵迷茫,这就是全部的询问换来的基本上是“它都是全自动的了,你还想要什么的”表情。

因此这个新功能完成后的二次返工是必然的了。

首先,该模块的可控性太差。测试环境不可以等待每天晚上1点这个时刻,必须有外部能影响这个”全自动“的手段提供。否则全量的系统测试用例回归会被限定在固定测试时间点且无法调整和更改。

其次,该模块的每秒处理量必须能更改到符合测试环境。测试环境基本上都是真实环境的放缩,特别是分布式系统等大规模应用。测试环境机器无论是数量还是类型都远低于实际环境。这种条件下,参数的定量调整是必须要完成的辅助支持。

再次,没有必要的描述如何判断哪些文件/数据被GC掉了。无法观测到执行结果集带来的后果是无法精确的预期测试结果。

而相应的改进措施就是解决上面提到的问题。

[2] 系统内部状态信息

为了保证存储的数据高可用,分布式系统会采取多机存储副本方法。即一个数据被N(>=2)个机器以一定的算法存储相同的数据副本。这个时候经常会遇到的问题:

a) 机器间的数据由于数据复制顺序的不同,会有数据差异。a、b、c三台机器,a、b机器可能已完成一次数据的更新到最新数据版本data1,c还处于老版本data0.

b) 由于版本差异,内部必须维护副本revision的版本号以进行数据同步和异常处理。

这种情况, 好的设计原则上要保证多机副本的必要状态信息被外部获取。

A. 数据的副本分布信息、副本的revision版本号等需要提供接口获得

B.由于机器宕机造成的副本分布变化要能够及时反映和更新。(比如带一定间隔周期的更新)

只有在这种必要信息被获取的情况下,测试人员才能更好的掌握系统状态并根据系统状态进行清晰的测试结果预期。

[3] 参数的热设定

参数的热设定是经常碰到的问题。一个系统越复杂、可定制,它可设定的参数就越多。一个好的设计应该能热设定其中的参数,然后执行重新加载动作。

举个实际的例子, 下面的配置文件是一个系统的存储节点配置文件截图。该截图仅展示了大约1/5的配置参数。

a. 如果参数不可重新热加载,那么测试用例执行过程中都必须进行进程的重启。

进程的重启势必造成单个测试用例的时间拉长,复杂系统成百+的测试用例会造成总体测试时间的拉长。每个多消耗1-2分钟,整体就是小时级别的时间消耗。这对Slow build或完整性测试集执行来说是个灾难。可测性也比较差。

b. 参数不可热加载会在系统运维期间失去热调整参数的机会,可能导致系统的间断性停服务。这对基础服务来说是个噩梦,上层依赖于基础服务的应用可能成百上千,停服的代价过于大。一些gdb强行attach进程进行等修改变量的临时方法由于进程状态的不确定性因素会带来不小的风险。作者负责的项目曾出现gdb热修改带来集群主控节点宕机停集群的惨痛经历。

参数的热设定和加载虽然增加了一定的逻辑复杂度,但对比带来的收益是值得付出并实践的。

[4] 系统使用信息统计

系统使用信息的统计在如下方面特别重要:

1) 产品线运营数据,为产品运营、后续产品改进等环节提供一手资料

2) 运用系统、集群状态信息监控以解决运维过程中发生的问题

3) 利用系统状态信息进行内部运行状态判定,以测试是否达预期

1和2虽然不直接涉及可测性,但测试人员在系统设计阶段需要进行这方面的考虑以防止系统开发后期进行的功能性重构带来测试整体架构重构。系统接近尾声进行的功能性重构对测试人员来说是个非常头疼的问题。测试用例依赖的统计信息等接口可能被大量使用,这类的更改带来不小的用例调整、更正工作。

测试人员在信息统计的设计阶段需要了解系统在现有的设计基础上可能衍生的二期、三期甚至更后期的功能,以提前影响当前的功能设计,提高数据、接口、操作方面的可扩展性。为以后可能产生的新功能打好可测性基础。少埋坑、多考虑场景适应性。

上面的场景是作者在实际测试项目中经常遇到的,因此抽取出来做个示例。实际的项目测试遇到的场景远比这些复杂、多样且不可预知。这个时候需要大家多思考场景,多根据已有的经验进行防御性准备。

那有没有通用的提供可测性的方法呢?

3 提高可测性的通用方法

  1. 摒弃原有的开发人员只进行单纯的代码单元测试的观念,让开发人员也进行系统级测试。作者在实践过程中最推崇的方法就是此条。具体地说,开发人员进行的是系统级测试,禁止刷行覆盖率型的单纯函数覆盖UT。由系统级接口或者功能来驱动整个测试过程。无法直接进行驱动的测试行为需要撰写模拟器或者模拟模块进行。这样开发人员会切身的感受到可控性和可观测性的重要性。进而推动系统在这两个方面的实现更易用和便于测试。由此而来的良性循环能让系统整体可测性始终处于较好水平。
  2. 测试人员深度了解被测系统,能够在可测性出现问题的时候及时指出问题所在。只有深度了解被测系统,详细分析系统实现逻辑和代码,做到可黑盒、可白盒测试的程度,才能提前预测可测性薄弱环节,提前预防这样的事情发生。在可测性出现问题时,及时介入和提出建设性意见。在需要进行测试代码植入以方便测试流畅进行等方面亲自动手,协助开发人员解决问题。

可测性问题可能出现在系统的各个方面,但只要在系统生命周期的各个环节严格要求并辅以正确的方法,可测性问题就不会成为软件测试中不可攻破的难关。各位朋友,你遇到过哪样的可测性难题呢?如果让你从设计阶段就贯彻好的可测性要求并在整个流程中严格遵守,能否解决你的难题呢?

分布式存储系统的雪崩效应

一 分布式存储系统背景

副本是分布式存储系统中的常见概念:将一定大小的数据按照一定的冗余策略存储,以保障系统在局部故障情况下的可用性。

副本间的冗余复制方式有多种,比较常用有两类:

  • Pipeline:像个管道,a->b->c,通过管道的方式进行数据的复制。该方式吞吐较高,但有慢节点问题,某一节点出现拥塞,整个过程都会受影响
  • 分发:client -> a  client ->b client ->c。系统整体吞吐较低,但无慢节点问题

对于冗余副本数目,本文选择常见的三副本方案。

分布式存储系统一般拥有自动恢复副本的功能,在局部存储节点出错时,其他节点(数据副本的主控节点或者client节点,依副本复制协议而定)自动发起副本修复,将该宕机存储节点上的数据副本恢复到其他健康节点上。在少量宕机情况下,集群的副本自动修复策略会正常运行。但依照大规模存储服务运维经验,月百分之X的磁盘故障率和月千分之X的交换机故障率有很大的可能性导致一年当中出现几次机器数目较多的宕机。另外,批量升级过程中若出现了升级bug,集群按照宕机处理需要进行副本修复,导致原本正常时间内可以完成的升级时间延长,也容易出现数目较多的宕机事件。

二 雪崩效应的产生

在一段时间内数目较多的宕机事件有较大可能性诱发系统的大规模副本补全策略。目前的分布式存储系统的两个特点导致这个大规模副本补全策略容易让系统产生雪崩效应:

a. 集群整体的free空间较小:通常整体<=30%, 局部机器小于<=20% 甚至10%

b. 应用混布:不同的应用部署在同一台物理/虚拟机器上以最大化利用硬件资源

今年火起来的各种网盘、云盘类服务就是a的典型情况。在各大公司拼个人存储容量到1T的背后,其实也在拼运营成本、运维成本。现有的云存储大多只增不减、或者根据数据冷热程度做数据分级(类似Facebook的数据分级项目)。云存储总量大,但增量相对小,为了减少存储资源和带宽资源浪费,新创建的文件若原有的存储数据中已有相同的md5或者sha1签名则当做已有文件做内部链接,不再进行新文件的创建。但即使这样,整体的数据量还是很大。

目前云存储相关业务未有明显的收入来源,每年却有数万每台的服务器成本,为运营成本的考虑,后端分布式存储系统的空闲率很低。而瞬间的批量宕机会带来大量的副本修复,大量的副本修复很有可能继而打满原本就接近存储quota的其他存活机器,继而让该机器处于宕机或者只读状态。如此继续,整个集群可能雪崩,系统残废。

三 预防雪崩

本节主要讨论如何在系统内部的逻辑处理上防止系统整体雪崩的发生。预防的重要性大于事故之后的处理,预测集群状态、提前进行优化也成为预防雪崩的一个方向。

下面选取曾经发生过的几个实际场景与大家分享。

1. 跨机架副本选择算法和机器资源、用户逻辑隔离

现场还原:

某天运维同学发现某集群几十台机器瞬间失联,负责触发修复副本的主控节点开始进行疯狂的副本修复。大量用户开始反馈集群变慢,读写夯住。

现场应对:

优先解决——副本修复量过大造成的集群整体受影响。

a. 处理的工程师当机立断,gdb到进程更改修复副本的条件为副本<2,而非原本的3(replicas_num),让主控节点这个时候仅修复副本数小于2个的文件,即保证未丢失的文件有至少一个冗余副本,防止只有一个副本的数据因可能再次发生的挂机造成文件丢失。

b. 紧急解决这批机器失联问题,发现是交换机问题,a.b.c.d ip网段的c网段机器批量故障。催促网络组尽快修复。

c. 副本修复到>=2之后,Gdb更改检测副本不足周期,将几十秒的检测时间推迟到1天。等待网络组解决交换机问题。

d. 网络恢复,原有的机器重新加入集群。大量2副本文件重新变为3副本,部分3副本全丢失文件找回。

e. 恢复主控节点到正常参数设置状态,系统开始正常修复。

改进措施:

在改进措施前,先分析下这次事件暴露的系统不足:

1) Master参数不支持热修正,Gdb线上进程风险过大。

2) 一定数量但局域性的机器故障影响了整体集群(几十台相对一个大集群仍属于局域性故障)。如上所述,月千分之几的故障率总有机会让你的存储系统经历一次交换机故障带来的集群影响。

案例分析后的改进措施出炉:

1)  Master支持热修正功能排期提前,尽早支持核心参数的热修改。

热修改在上线后的效果可观,后续规避过数次线上问题。

2) 在选择数据副本存储宿主机器的pickup算法中加入跨交换机(机架位)策略,强制——或者尽量保证——副本选择时跨机架位。这种算法底下的副本,至少有1个副本与其他两个副本处于不同的交换机下(IP a.b.c.d的c段)。该措施同时作用于新的存储数据副本选择和副本缺失后的副本补全策略,能在副本宿主选择上保证系统不会因为交换机的宕机而出现数据丢失,进而避免一直处于副本补全队列/列表的大量的丢失副本节点加重主控节点负载。

3) 机器按region划分隔离功能提上日程;用户存储位置按照region进行逻辑划分功能提上日程;Pickup算法加入跨region提上日程。

a) 机器按照物理位置划分region、用户按照region进行逻辑存储位置划分,能让集群在局部故障的情况下仅影响被逻辑划分进使用这部分机器的用户。

这样一来,最坏情况无非是这个region不可用,导致拥有这个region读写权限的用户受影响。Pickup算法跨region的设计进一步保证被划分region的用户不会因为一个region不可用而出现数据丢失,因为其他副本存到其他region上了。于是,核心交换机故障导致一个region数百台机器的宕机也不会对集群造成范围过大的影响了。

b) 增加region可信度概念,将机器的稳定性因素加入到副本冗余算法中。

当集群规模达到一定量后,会出现机器稳定性不同的问题(一般来说,同一批上线的机器稳定性一致)。通过标记region的稳定性,能强制在选择数据副本的时候将至少一个副本至于稳定副本中,减少全部副本丢失的概率。

c) Region划分需要综合考虑用户操作响应时间SLA、物理机器稳定情况、地理位置等信息。

合理的region划分对提升系统稳定性、提升操作相应时间、预防系统崩溃都有益处。精巧的划分规则会带来整体的稳定性提升,但也增加了系统的复杂度。这块如何取舍,留给读者朋友深入思考了。

2. 让集群流控起来

流控方面有个通用且符合分布式存储系统特点的原则:任何操作都不应占用过多的处理时间。这里的“任何操作”包含了在系统出现流量激增、局部达到一定数量的机器宕机时进行的操作。只有平滑且成功的处理这些操作,才能保证系统不因为异常而出现整体受影响,甚至雪崩。

现场还原:

1) 场景1 某天运维同学发现,集群写操作在某段时间大增。通过观察某个存储节点,发现不仅是写、而且是随机写!某些产品线的整体吞吐下降了。

2) 场景2 某集群存储大户需要进行业务调整,原有的数据做变更,大量数据需要删除。

运维同学发现,a. 整个集群整体上处于疯狂gc垃圾回收阶段 b. 集群响应速度明显变慢,特别是涉及到meta元信息更新的操作。

3) 场景3 某天运维同学突然发现集群并发量激增,单一用户xyz进行了大量的并发操作,按照原有的用户调研,该用户不应该拥有如此规模的使用场景。

此类集群某些操作预期外的激增还有很多,不再累述。

现场应对:

1) 立刻电联相关用户,了解操作激增原因,不合理的激增需要立刻处理。

我们发现过如下不合理的激增:

a. 场景1类:通过Review代码发现,大量的操作进行了随机读写更改。建议用户将随机读写转换为读取后更改+写新文件+删除旧文件,转换随机读写为顺序读写。

b. 场景3类:某产品线在线上进行了性能测试。运维同学立刻通知该产品线停止了相关操作。所有公有集群再次发通过邮件强调,不可用于性能测试。如有需要,联系相关人员在独占集群进行性能场景测试。

2) 推动设计和实现集群各个环节的流控机制功能并上线。

改进措施:

1) 用户操作流控

a. 对用户操作进行流控限制

可通过系统内部设计实现,也可通过外部的网络限流等方式实现,对单用户做一定的流控限制,防止单个用户占用过多整个集群的资源。

b. 存储节点操作流控

可按照对集群的资源消耗高低分为High – Medium – Low三层,每层实现类似于抢token的设计,每层token数目在集群实践后调整为比较适合的值。这样能防止某类操作过多消耗集群负载。若某类操作过多消耗负载,其他操作类的请求有较大delay可能,继而引发timeout后的重试、小范围的崩溃,有一定几率蔓延到整个集群并产生整体崩溃。

c. 垃圾回收gc单独做流控处理。删除操作在分布式存储系统里面常用设计是:接收到用户删除操作时,标记删除内容的meta信息,直接回返,后续进行策略控制,限流的删除,防止大量的gc操作消耗过多单机存储节点的磁盘处理能力。具体的限流策略和token值设置需要根据集群特点进行实践并得出较优设置。

2) 流控黑名单

用户因为对线上做测试类的场景可以通过人为制度约束,但无法避免线上用户bug导致效果等同于线上测试规模的场景。这类的场景一般在短时间内操作数严重超过限流上限。

对此类场景可进行流控黑名单设置,当某用户短时间内(e.g. 1小时)严重超过设置的上限时,将该用户加入黑名单,暂时阻塞操作。外围的监控会通知运维组同学紧急处理。

3) 存储节点并发修复、创建副本流控

大量的数据副本修复操作或者副本创建操作如果不加以速度限制,将占用存储节点的带宽和CPU、内存等资源,影响正常的读写服务,出现大量的延迟。而大量的延迟可能引发重试,加重集群的繁忙程度。

同一个数据宿主进程需要限制并发副本修复、副本创建的个数,这样对入口带宽的占用不会过大,进程也不会因为过量进行这类操作而增加大量其他操作的延迟时间。这对于采用分发的副本复制协议的系统尤其重要。分发协议一般都有慢节点检查机制,副本流控不会进一步加重系统延迟而增大成为慢节点的可能。如果慢节点可能性增大,新创建的文件可能在创建时就因为慢节点检查机制而缺少副本,这会让集群状况更加恶化。

3. 提前预测、提前行动

1) 预测磁盘故障,容错单磁盘错误。

场景复现:

某厂商的SSD盘某批次存在问题,集群上线运行一段时间后,局部集中出现数量较多的坏盘,但并非所有的盘都损坏。当时并未有单磁盘容错机制,一块磁盘坏掉,整个机器就被置成不可用状态,这样导致拥有这批坏盘的机器都不可用,集群在一段时间内都处于副本修复状态,吞吐受到较大影响。

改进措施:

a) 对硬盘进行健康性预测,自动迁移大概率即将成为坏盘的数据副本

近年来,对磁盘健康状态进行提前预测的技术越来越成熟,技术上已可以预判磁盘健康程度并在磁盘拥有大概率坏掉前,自动迁移数据到其他磁盘,减少磁盘坏掉对系统稳定性的影响。

b) 对单硬盘错误进行容错处理

存储节点支持对坏盘的异常处理。单盘挂掉时,自动迁移/修复单盘的原有数据到其他盘,而不是进程整体宕掉,因为一旦整体宕掉,其他盘的数据也会被分布式存储系统当做缺失副本,存储资源紧张的集群经历一次这样的宕机事件会造成长时间的副本修复过程。在现有的分布式存储系统中, 也有类似淘宝TFS那样,每个磁盘启动一个进程进行管理,整机挂载多少个盘就启动多少个进程。

2) 根据现有存储分布,预测均衡性发展,提前进行负载均衡操作。

这类的策略设计越来越常见。由于分布式存储集群挂机后的修复策略使得集群某些机器总有几率成为热点机器,我们可以对此类的机器进行热点预测,提前迁移部分数据到相对负载低的机器。

负载均衡策略和副本选择策略一样,需要取舍复杂度和优化程度问题。复杂的均衡策略带来好的集群负载,但也因此引入高复杂度、高bug率问题。如何取舍,仍旧是个困扰分布式存储系统设计者的难题。

四 安全模式

安全模式是项目实践过程中产生的防分布式存储系统雪崩大杀器,因此我特别将其单独列为一节介绍。其基本思路是在一定时间内宕机数目超过预期上限则让集群进入安全模式,按照策略配置、情况严重程度,停止修复副本、停止读写,直到停止一切操作(一般策略)。

在没有机器region概念的系统中,安全模式可以起到很好的保护作用。我过去参与的一个项目经历的某次大规模宕机,由于没有安全模式,系统进行正常的处理副本修复,生生将原本健康的存储节点也打到残废,进而雪崩,整个集群都陷入疯狂副本修复状态。这种状态之后的集群修复过程会因为已发生的副本修复导致的元信息/实际数据的更改而变的困难重重。 该事件最后结局是数据从冷备数据中恢复了一份,丢失了冷备到故障发生时间的数据。

当然,安全模式并非完美无缺。“一段时间”、“上限”该如何设置、什么时候停副本修复、什么时候停读、什么时候停写、是自己恢复还是人工干预恢复到正常状态、安全模式力度是否要到region级别,这些问题都需要安全模式考虑,而此类的设计一般都和集群设计的目标用户息息相关。举例,如果是低延迟且业务敏感用户,可能会选择小规模故障不能影响读写,而高延迟、高吞吐集群就可以接受停读写。

五 思考

由于分布式存储系统的复杂性和篇幅所限,本文仅选择有限个典型场景进行了分析和讨论, 真实的分布式存储系统远比这数个案例复杂的多、细节的多。如何平衡集群异常自动化处理和引入的复杂度,如何较好的实现流控和避免影响低延迟用户的响应时间,如何引导集群进行负载均衡和避免因负载均衡带来的过量集群资源开销,这类问题在真实的分布式存储系统设计中层出不穷。如果设计者是你,你会如何取舍呢?

如何优雅的打日志 – optimal-logging

发件人:                           Ma,Guannan
发送时间:                       2014年3月9日星期日 21:40
收件人:                           Ma,Guannan
主题:                               优雅的打日志
作者:Anthony Vallone 翻译: 马冠南
在你的系统故障后,一般需要多久才能找到根本原因? 5分钟? 5天? 如果你的回答是[接近五分钟]。可能你的系统有非常棒的Log输出(机制)。太多时候,这些看起来不是特别重要的处理逻辑如Log输出、Exception处理、测试点等内容都是人们事后才加入的。永远不要低估logging的作用。一个好的日志logging机制可以使调试者需要更少的相关信息来做debug工作。
以下是数年来对我很有帮助的一些方法:
梳理你那不温不火的日志吧:
不要打印太多(无用信息)。
大量的、(打印速度)能让硬盘着火的打印方式说明你没怎么在日志打印上花精力。如果你大量打印, 需要考虑怎么去减少硬盘存取、维护历史日志文件、将他们归档、并且对它们做查询和信息收集。 更重要的是,事后你会发现这些大量打印的无聊东东会让你很难收集到有价值的信息。信息量太大且无用了。
同样,也不要吝啬你的打印语句。
一般情况下,打印日志是为了找到系统的bug或者复现某个问题、或者对某件事情做确认。如果你的日志不能帮你找到bug的原因或者不能帮你确认某件事情确实发生了, 你就需要多打印一些,兄弟,不要太吝啬你的硬盘。
一些适合打印的东西:
·         [启动阶段] 关键的配置参数
·         [运行阶段] Error级别信息
·         [运行阶段] Warning级别信息
·         [运行阶段] 对持久化的信息做了新增或者改动
·         [运行阶段] 重要的状态变更
·         [逻辑处理] 对系统非常重要的分支被触发, 可能是逻辑上或者某个等待事件方面比较重要的分支.
·         [逻辑处理] 长时间运行的逻辑中周期性处理的部分被触发和完成时
·         [逻辑处理] 在上层函数中做言简意赅的日志打印,反应目前的处理逻辑进行到哪一步了.
·         [资源竞争] 某个可能比较耗时的条件的wait操作将被触发
·         [用户交互] 用户操作
·         [已知风险] 监测到已知风险被触及
一些不适合打印的东西如下:
·         [内部逻辑] 不要打印函数的入口 = = 除非她十分的重要, 或者在DEBUG level去打印她。
·         [数据记录] 不要打印for while循环内的数据记录 = = 尽量避免打印一个数据比较多的循环内的数据。 可以在上层的大循环中打印。非常重要且数据量小的可以尝试打印。
·         [异常处理] 巨大的信息、或者文件类。 可以用某种方法把她们做归类、聚合,总结性的打印日志
·         [异常处理] 良性、可被正确处理的错误。该类错误可能对日志阅读者产生困惑。可以用DEBUG level的方式打印她。
·         [异常处理] 重复性的错误日志。不要重复的打印相同、相近的日志。 这样打印会很快的充满你的日志、掩盖真正的错误原因。通过打印一条然后标定他的出现次数是个不错的选择。选择有特殊需要和目的的地方进行细致的打印。
使用多种日志打印级别
不要把所有东西没有区分的打印在同一个层次里面. 最好划分几个层次. 一般的log库都提供数个日志Level.你可以在启动阶段使用其中的几个level. 这能很好的控制日志的详细程度。
经典的几个日志Level有:
·         Debug – 非常详细的打印level、一般只在开发或者调试阶段才会打开这个level.
·         Info – 最受欢迎的level, 一般生产环境都设置成Info Level.
·         Warning – 奇怪或者异常的情况发生时的打印level, 这类日志表明出现了一定问题, 但一般不影响系统的使用。
·         Error – 发生了错误, 但是系统、进程可以恢复。
·         Critical – 发生了重大问题。一般系统、进程等受到影响会死掉、重启或者被关闭。
从实践的经验来看,一般两个日志配置项是需要的。
·         生产环境  – 除了Debug level之外,其他的层次都是需要的。 这种情况下, 一旦发生了错误,一般日志能很好的找到错误原因。
·         开发、调试环境- 在开发新的feature或者模块或者需要复现比较难的生产环境问题时候, 会激活所有的日志level.
测试程序的日志是同等重要的
    打印日志的质量在生产环境和测试环境是同等重要的。如果在测试环境中,某个测试用例失败了。测试code中打印的日志应该能清晰的反映出这次失败  – – 是用例错误导致的失败、还是被测的程序确实产生了问题。如果反映不出,则这个日志打印规则与生产环境是不统一的,需要调整。
测试日志应该包含:
·         测试执行时的环境信息
·         初始状态
·         程序setup内容
·         测试中的关键步骤的checkpoint点
·         与被测系统的交互
·         测试用例的期望结果 – 若出错,一并打印实际的结果
·         环境清除内容和执行结果
提供动态的日志Level的激活和关闭 (比如设定条件触发)
    当生产环境发生问题时候,很明显, 大家一般会去聚焦在查找问题、解决问题。但与此同时,请应该把log也考虑进来。如果你发现,诶,这次很难发现这个问题的原因,你就赚到了。这是一个改善你的日志打印的机会。在fix你的bug或者问题之前,先优化下你的日志,让她下一次在问题发生后能很好、很简单的展示错误发生的原因。
如果你不能复现这个问题或者这是个不稳定的问题,优化这个log直到当再次发现问题时能很好的帮助debugger追踪到问题。
在整个开发过程中,用终止进程(abort程序)的方式来改善日志打印方式。当撰写新的代码的时候,应该牢记尽量避免后面来进行人工调试,应该尽量使用log来查找问题。也要常记着思考,这些log能有效的记录现在系统发生了什么?如果不能,说明日志打印不够充分。
性能数据相关log也应该打印
打印性能数据能很好的帮助debug性能相关的问题。举个例子,在一个大型系统中,一般很难发现某个动作、命令timeout的原因,除非你在每一个很重要的时间点上做了日志log追踪和打印。这种日志优化可以通过打印耗时的处理逻辑的入口和出口来实现,比如:
·         重要的系统调用
·         网络请求处理
·         CPU密集型的处理逻辑
·         已链接的设备、模块的互动操作
·         事务等
多进程、多线程执行中记录事务的踪迹
    我们应该为可能被多个进程、线程处理的事务创建唯一的标识码。可以在事务的初始阶段创建,并传递给每一个事务的处理逻辑。这个唯一的标识码应该被每个处理逻辑在打印日志的时候包含, 这样在系统并行的处理多个事务的时候可以很方便快捷的找到你需要找的那一个。
打印日志并监控你的系统模块
    一个生产环境需要既有日志模块又有监控模块。监控能够提供实时的系统状态汇总。她能在系统出现错误、不寻常的网络请求、持续下降的性能等问题时候提醒你。在某些案例当中,这些信息能够帮助你找到问题的真正原因。不过有意思的是,可能一般情况下监控模块的提醒是你开始介入调查的开始。监控告诉你系统里发生了某些异常,日志打印模块告诉你哪些事务、事件等发生了,从而帮助你了解究竟发生了什么,哪些才是发生这些事情的真正原因。