打印 Logger 日志时,需不需要再封装一下工具类?
在开发过程中,打印日志是必不可少的,因为日志关乎于应用的问题排查、应用监控等。现在打印日志一般都是使用 slf4j,因为使用日志门面,有助于打印方式统一,即使后面更换日志框架,也非常方便。在 《Java 开发手册》中也有相关的规约。
所以在开发中,一般使用下面这种方式来打印日志。
LOGGER.info("print: {}", "this is the log");
不过有的应用会将 LOGGER
再封装一下,最终写成:
LoggerUtil.info(LOGGER, "print: {}", "this is the log");
本文的主要内容是讨论为什么要封装,有没有必要封装,以及怎样封装,如果小伙伴有更好的建议,可以提出,进行互相学习。
为什么要封装
很多人觉得 slf4j 本来就是日志门面,已经封装的很好了,为什么要多此一举,再额外封装一个 LoggerUtil
呢?
其实这块也是在开发规范中有说明的:
如果不进行封装,则会写成下面这种:
if (LOGGER.isInfoEnabled()) { LOGGER.info("print: {}", "this is the log"); }
所以,一般封装是将 if 判断这块逻辑统一封装为一个工具类。
可能到这里还有小伙伴不是很理解为什么要加 if 判断,可以看下下面这段代码:
可以看出转换逻辑这块相对比较复杂、耗时,在这里只是模拟的场景,实际使用可能会有其他情况,比如打印方法的出参入参、计算耗时等:
LOGGER.info("xxx 方法请求参数为:{}", JSON.toJSONString(req)); LOGGER.info("xxx 执行耗时:{}ms", System.currentTimeMillis() - startTime);
在某些场景下为了提高性能,需要关闭日志,比如大促,秒杀等等。
说到这里相信小伙伴已经看出问题了,因为这样写的话,当我关闭日志打印时,只是关闭了磁盘输出,但是耗时逻辑依然会继续执行。
# 日志级别调整到 error logging.level.com.liuzhihang=error
这也是为什么在开发规范中建议大家手写判断,虽然日志框架中帮我们进行了判断,那只是避免了打印输出日志,实际上像组装日志,序列化实例对象等等还是会被执行的。
当然如果当前应用只有个位数的 tps 或者 tpm 那完全没必要考虑这些,也没必要因噎废食,正常使用就行。
该怎样封装
为了避免每次都要 if 判断的问题,会将 if 模块封装为工具类:
上面的封装,有效避免了每次都需要进行判断,只需要将代码中的打印日志换成 LogUtil 即可:
但是这种情况只能避免打印 既有参数
时的 if 判断,对方法类型的没有作用,这里就需要使用 Supplier
:
实际使用效果:
以上仅为一种封装方式,其他的封装可以自行考虑,比如整个日志框架都封装。
其他使用
这部分封装在 log4j-api-2.17.2.jar
中也有所体现,只不过 slf4j 里面并没有封装 Supplier
支持,详细实现可以自行阅读源码。
那为什么 slf4j 不支持,其实也是有讨论的,可以看 issue #70 ,里面进行了一系列讨论。
最终结果是在 2.0 支持了 Fluent Logging API 语法。
slf4j 2.0 使用
<!-- slf4j 2.0 依赖 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.0</version> </dependency>
按照官方文档的使用案例直接使用即可:
logger.atDebug() .setMessage("Temperature set to {}. Old value was {}.") .addArgument(() -> t16()).addArgument(oldT) .log();
为什么要这样写,只能说是人家的 API 设计就是如此,当然也有其他的考虑,可以看看 github issue。具体使用哪种,用不用封装等等,这些都是根据自己的实际情况来使用。
- 线程池底层原理详解与源码分析
- 30分钟掌握 Webpack
- 线性回归大结局(岭(Ridge)、 Lasso回归原理、公式推导),你想要的这里都有
- 【前端必会】webpack loader 到底是什么
- 中心化决议管理——云端分析
- HashMap底层原理及jdk1.8源码解读
- 详解JS中 call 方法的实现
- 打印 Logger 日志时,需不需要再封装一下工具类?
- 初识设计模式 - 代理模式
- 密码学奇妙之旅、01 CFB密文反馈模式、AES标准、Golang代码
- Springboot之 Mybatis 多数据源实现
- CAS核心思想、底层实现
- 面试突击86:SpringBoot 事务不回滚?怎么解决?
- 基于electron vue element构建项目模板之【打包篇】
- MiniWord .NET Word模板引擎,藉由Word模板和数据简单、快速生成文件。
- 认识线程,初始并发
- 1-VSCode搭建GD32开发环境
- 初识设计模式 - 原型模式
- 线程安全问题的产生条件、解决方式
- 2>&1到底是什么意思?