日志——快速定位问题的主武器

在每个项目中,日志的地位都是举足轻重的,好的日志可以大大提升问题确认的速度,尤其是针对无法断点调试的线上环境。所以好的日志规范是非常必要的。

日志级别

首先不得不说的就是日志的级别,级别的划分主要是为了区分不同目的、不同重要程度的信息,以方便对日志进行过滤。

日志级别由高到底为ERROR>WARN>INFO>DEBUG>TRACE

  • ERROR级别的日志用来标注一些影响到当前程序的运行,或者当前接口、请求的运行的异常情况,常见的有RuntimeException、第三方接口返回的异常状态码等。
  • WARN级别的日志用来标注一些暂时还没有影响程序或请求的运行,但是其本质上是异常的,比如说,代码中尝试打开一个不存在的文件,但是我们做了默认文件的处理不会抛出异常。当我们需要知道,这个文件到底是不是存在的时候,就通过WARN级别的日志输出。
  • INFO级别的日志用来标注程序运行的一些必要信息,比如业务的数据交换,逻辑步骤等,可以方便开发人员知道程序的运行状态。
  • DEBUG级别的日志用来标注和业务相关的更多更详细的信息,比如,每一步的数据变化、调用情况。
  • TRACE级别的日志是用来标注系统或者程序底层的状态信息,不会用来标注业务相关的信息,所以一般程序中也是见不到这个级别的日志的。

使用规范

先从日志的实现上来说,常见的日志门面框架就是slf4jcommons-logging。他们的区别和优缺点就不专门的去说了,肯定是各有所长,普遍使用slf4j。而slf4j的实现,一开始是log4j,后来对其进行了升级优化,就产生了LogBack,如果你使用SpringBoot的话,那么什么都不用做,默认就是它。

日志格式

在输出日志的时候,当需要在其中加入变量的时候,很多人喜欢使用字符串拼接的方式,如:

log.info("xxx业务的请求参数有 :id=" + id + ",name=" + name);

这样使用,会产生多余的字符串对象,占用空间,影响性能。

正确的使用姿势应该是使用字符串格式化操作。如:

// []用来做变量的隔离,方便区分值,{}是占位符,会将后面传入的变量值拼接到该位置
log.info("xxx业务的请求参数有 : id=[{}],name=[{}]", id, name);

提示信息中英文皆可,主要是为了快速定位问题,千万不要因为所谓的使用中文比较low,而在自己英文水平不足够的情况下使用英文。不能定位到问题,什么高端、低级都是扯淡。我的英文比较垃圾,所以我在开发中,基本上使用全中文模式。

各级别日志的使用

ERROR

ERROR级别日志的使用时最简单的也是最容易出问题的。简单在于,这个日志级别在一般开发程序中就2种使用场景,一是捕获到了异常,二是第三方接口中返回错误的状态码。容易出问题在于,很多人在输出异常信息的时候会忽略堆栈信息。

try {
	// TODO something
} catch (Exception e) {
	// 这种方式,输出的信息只有一个简单的提示,根本无法定位到异常发生在哪里
	// 比如说NPE,只会简单输出一个 null,还不如不输出
	log.error("xxx业务操作异常:[{}]", e.getMessage());
	// 正确的输出方式如下,日志信息中,还可以包括其它任何信息,但是最后一个堆栈信息的输出是一定不能省略的
	log.error("xxx业务操作异常:[{}]", e);
}

对于error级别的日志而言,只要在catch时使用,并且正常输出堆栈信息。就不会有什么问题。

WARN

warn级别的日志在一般业务逻辑中使用也是比较少的。它主要针对的是一个异常但并没有影响程序正常运行的情况。例如:

try {
	File file = new File(filePath);
	if (file.exists()) {
		// TODO 读取文件
	} else {
		// 文件不存在则打开默认文件
		file = new File(defaultPath);
		// 由于通过if判断了文件是否存在,所以这个程序并不会抛出异常
		// 但是对于程序来说,用户传递了一个不存在的文件,这是一个异常情况,没报错是因为我们代码的健壮性好
		// 所以,如果此信息有必要输出的话,应该使用warn级别的日志
		log.warn("文件[{}]不存在,已打开默认文件:[{}]", filePath, defaultPath);
	}
}
INFO

info级别日志用来记录业务状态和数据的变更以及操作步骤的记录。是开发中最常用的日志级别。比如service的出入口、第三方数据接口数据交互,远程调用数据流向等。

public ReturnType serviceMethodName(T t) {
	log.info("开始执行xxxx业务,入参:[{}]", t);
	// TODO something
	log.info("执行xxx业务结束:出参:[{}]", t);
} 

提示信息按照自己的习惯写即可,上面也是我临时想的简单的demo,service的出入口并不是一定要有的,比如说service中没有什么数据变化或者没有什么有意义的操作。service中间如果有关键数据交互以及主要业务步骤,也应该增加相应的info级别日志以方便调试。

以下几种情况,笔者建议,一定要使用日志说明:

  1. 调用第三方接口时的出入参,因为第三方接口的问题你是很难追溯的。即使你已经确认不是你的问题,如果没有日志,对方可能也会推脱说,是你调用出错了。
  2. 如果你使用SOA或者微服务等分布式架构,那么远程调用的出入参也要明确记录下来,因为分布式的调试也是很麻烦的。
  3. 使用多线程时,一定要记录参数变化以及当前的线程名,如果你有进行过多线程的调试,那么你一定知道,为什么一定要记录。
  4. 所有暴露的api接口的出入参(尤其是入参),项目开发中联调的沟通成本很大的原因大部分是因为,前端认为他传参不可能错,后端认为一定是你参数有问题。我一般都选择,直接把接口的入参和出参拿出来,谁的问题一目了然。如果没有日志,那就只能通过抓包,这比较麻烦。所以,笔者建议,一定要把接口的出入参在日志中体现出来。
  5. 关键的业务逻辑,一定要多打日志,不要怕麻烦,比如电商中的订单,数据分析中的埋点计算。
DEBUG

debug级别是用来输出更详细的相关信息的。首先,要确定的是,线上环境一定不能使用debug级别的日志输出。如果线上需要debug级别的日志调试,应该为其加上开关。

public ReturnType seviceMethodName() {
	log.info("some info level log");
	// TODO something
	if (log.isDebugEnabled()) {
		log.debug("some debug level log");
	}
}
TRACE

trace用来记录详细的系统运行信息,业务代码中避免使用即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值