Skip to content

17日志监控:怎样面向业务设计日志方案?

我们知道,应用在运行过程中会遇到各种突发情况,而完善的日志记录会提高开发者定位问题的速度,为开发者解决问题提供一些必要的信息。比如应用里负责和外部业务系统交互的接口,在某一时刻返回了与预期不符的数据。

如果你没有建立详细的日志记录,当你想找到"为什么之前运行正常的接口突然出现问题"的答案时,可能要复盘接口的整个源码,重新调试一遍之后才能发现关键信息。反之,你可以清楚地找到答案:外部业务系统中返回的数据结构发生了变更,导致接口运算过程出现了问题,从而返回了奇怪的数据。

总的来说,强大且详细的日志记录会让你维护应用的过程变得轻松,因为你可以通过日志记录的表象来分析问题所在。云开发当然不会遗漏这一点,它配备了强大的日志管理系统,让开发者能够轻松使用,使服务的开发和维护变得更加高效。

所以这一讲,我就深度带你了解一下怎么使用日志系统,以及如何合理做好日志,希望你能通过今天的学习,掌握云开发日志的关键用法,在日常开发中合理使用。

日志是什么

在应用运行过程中会产生一些阶段性结果,它们会被记录在一个独立的地方便于开发者查询和提取,查询和提取的文档就叫日志。我们在开发过程中经常和日志打交道,所以我就不讲日志对开发的功用了,而是侧重讲一下各种形态下的日志表现,便于你掌握云开发日志所处的位置和作用。以互联网应用为例,日志分为前端日志和后端日志。

前端日志是在客户端中运行程序时产生的日志,通常用本地存储记录,通过自动或手动触发的方式发回开发者服务器,便于开发者进行相关的日志分析工作。

手机上的大部分软件都有各自的日志记录,按照时间或某种崩溃机制进行触发上传到开发者服务器。有的软件会在你反馈问题时附带最近的日志一块上报;还有一部分软件会让用户自行决定日志的上传,比如微信。

除了手机安装的软件,浏览器中运行的网站以及电脑端运行的软件,产生的都是前端日志。

后端日志就是在服务端运行过程时产生的日志,通常按照一定的规则结构存储在对应的容器环境中,便于开发者进行检索和查看。此类日志只在服务端产生,并不会在用户侧的客户端中出现,主要为开发者解决服务端问题时提供参考和检索证据。

云开发从本质上讲属于后端云服务,所以云开发的日志属于后端日志。

如何记录日志

开发者主要在云函数或云托管中编写后端服务业务,导致云函数和云托管的所有的代码都是开发者编写的,所以需要开发者手动记录日志。云开发记录日志有两种方式:

  • 原有日志打印;

  • SDK 封装日志打印。

你在编码的过程中,会用到最基本的输出方法函数,比如 C 语言的 printf,JS 的 console 对象。而云函数或云托管会在执行过程中输出编程语言原有打印方式,你可以通过云函数的控制台日志窗口查看输出方法所打印的内容。

在每一个云函数被调用(或者云托管每一次被触发时),云开发都会记录计算时所有被打印的信息,然后把此次触发运行所打印的内容统一输出到函数的日志信息页中。

正式投入运行的应用,会不断产生成千上万的日志,但出现问题的运行日志可能比较少,会淹没在正常的运行日志中,我们需要有效地检索日志,尽快拿到有质量的信息。而原有日志打印并不利于检索,你要手动翻找,费时费力。

所以,云开发推荐使用SDK封装日志打印方式。这种方式通过云开发服务端 SDK 引入一个日志输出对象,使用特定的日志输出对象输出日志,输出的日志内容会按照开发者的规则进行存放,便于之后进行检索。比如,我们使用云开发服务端SDK wx-server-sdk 打印输出日志:

java
const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event, context) => {
    cloud.logger().log({ 
        content: "this is a log"
    });
    cloud.logger().info({ 
        content: "this is a info"
    });
    cloud.logger().warn({ 
        content: "this is a warn"
    });
    cloud.logger().error({ 
        content: "this is a error"
    });
    return 'suuccess';
}

通过 SDK 的 logger 函数对象,打印 4 种不同类型的信息,输出的日志内容需要是基本的 js 对象,这在输出后能够方便检索。

如何检索日志

当配置了日志的云函数或云托管程序被执行后,你就可以在"云开发控制台------高级日志"中进行检索了,你可以通过函数名的形式查询所有的日志信息,如下图所示:

除了基本的函数名称,你还可以根据日志类型进行查询,比如你只想检查一下有错误问题的日志,可以进行如下查询:

当客户端调用云函数时,每一次的返回结果都会包含 requestID,你也可以联合前端日志共同排查后端问题,通过特定的 requestID 定位后端有关信息:

除了检索出打印的信息之外,你还可以检索出云函数运行的状态信息(包括执行时长、运行内存等),辅助开发者判断情况。同时,你还能检索出开发者打印的日志对象的数据,比如想寻找content 中包含 error 字段的所有日志,就会是如下效果:

当然了,云开发的高级日志还支持更多的查询语法,你可以灵活进行各种类型的日志检索工作(以下是云开发总结的常用的查询语法结构,希望你可以通过各种语法的组合,定义出灵活的检索语句,用于日志业务查询)。

表格中的A、B表示的是查询体,可以进行各种逻辑相关的查询。比如,查询日志中包含"this is"字段但是类型为 info 的所有日志,编写应该如下:

java
this is and level:info

this is 是一个查询体,表示查询所有包含 this is 的日志,level:info 是键值查询体,表示类型为info 的日志,两者之间是"与"的逻辑。最终结果如下:

如何合理设置日志

虽然云开发提供了强大的日志检索能力,但开发者只有合理的使用它,才能发挥其真正的能力,接下来,我根据自己的工作经验,总结一些日志使用的准则,希望你能学习并掌握,提高日志输出和检索的效率。

  • 在关键节点进行记录

好的日志输出,并不是要记录每一行代码执行结果,你只记录函数运行过程中的关键节点就可以。比如在业务中,一个函数肯定会涉及多个资源的交互过程(发起 HTTP 请求,进行数据库操作,云存储的操作等)。

而与外部交互的地方就很关键,你要一一记录相关的请求输入参数以及返回数据,以备出现问题时排查是自身函数问题还是外部接口的问题。

因为一些接口会因为业务原因直接抛出异常 error 来表示请求结果,所以在函数编写过程中,我们会根据业务流程,将相关代码块使用 try-catch 进行异常捕获处理,而 catch 时也需要特定记录一下相关的信息。

最终,函数返回结果时,如果有需要,也可以在返回之前输出一下结果。

  • 合理设置记录的严重级别

一般我们会根据业务流程以及结果影响程度区分日志的级别,比如日常输出阶段性结果时,会直接设置日志类型为 log 或 info 。而对那些对时间以及返回结果有特定业务需求的应用,我们用 warn 来做标记,方便之后的维护巡检。除此之外,我们会用 error 标记那些运行失败的调用或计算出错的点。

区分日志输出的类别有利于开发者在后续维护时根据严重级别处理函数问题,提升发现问题解决问题的效率。

  • 日志信息分开记录

在输出一个日志时,尽可能分拆信息进行输出,比如你想输出特定的错误码以及错误描述,不建议直接以一个字符串的形式输出,像以下这样:

java
cloud.logger().info({ 
    content: "code:-1,msg:fail"
});

而是将里面可检索的关键信息拿出来,进行分开输出:

java
cloud.logger().info({ 
    code: "-1",
    msg: "fail"
});

如此一来,在检索时,可以直接对 code 码进行直接检索,提升了效率。另外,建议在输出日志时,根据日志所属的类型进行特定的记录,扩充自己可检索的条件,达到一查即达的效果。下面列举一下常见到的类型:

在描述日志时,你也可以添加一些类别,进行定向检索:

java
cloud.logger().info({ 
    type: "http-out",//HTTP请求返回,自己拟名字即可
    url: "www.test.com",
    body: "404",
    code: "-1",
    msg: "fail"
});

当你在平时日志记录时有更多的信息,在之后维护定位时才会更加得心应手。

结语

云开发的日志服务是平台现有的能力,不用安装就可以直接使用。你只需要合理使用日志系统,来提升我们的业务稳定性,以及问题可追踪性就可以了。

如果你的应用能在运行的每一个状态都有详尽可灵活查询的日志记录,那么在出现故障需要排查时,你就拥有了无比宝贵且丰富的资料信息,快速定位到问题,在第一时间投入修复工作中。

在课前导读中,我们举了一个空难调查的例子,其实日志记录就是应用的黑匣子,它总会在应用出现危机甚至整体故障无法工作的时候,给予我们最有力且最完备的资料,让我们开发者可以化身调查员,抽丝剥茧,寻找每一次故障时最核心的问题。

关于今天这节课,我想强调这样两个重点:

  1. 使用云开发SDK提供的接口进行日志输出,这将有利于输出后的检索过程,不要使用代码自带的打印语句进行日志输出;

  2. 在编写后端服务程序时,需要提前规划好日志的检索方式以及日志结构,在后续维护过程中保持统一的结构将有利于日志的检索和管理。

最后,我给你留的作业是:根据云开发日志服务有关文档,体验一下使用 SDK 输出日志,并学习使用控制台的日志检索功能。我们下一讲见。