第四步,在 resources 目录下创建 log4j.properties 文件,内容如下所示:
### 设置###
log4j.rootLogger = debug,stdout,D
### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=debug.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
再次运行 Demo 类,你会发现 target 目录下会生成一个名叫 debug.log 的文件,内容如下所示:
2020-10-21 15:32:06 [ main:0 ] - [ INFO ] jcl
1
并且可以在控制台看到以下信息:
[INFO ] 2020-10-21 15:32:06,192 method:com.itwanger.Demo.main(Demo.java:12)
jcl
1
2
仔细对比一下,你就会发现,这次输出的格式和之前不一样,这就是因为 Log4j 和 JUL 的日志格式不同导致的。
另外,你有没有发现?我们并没有改动测试类 Demo,它里面使用的仍然是 JCL 获取 Log 的方式:
private static Log logger = LogFactory.getLog(Demo.class);
1
但输出的格式已经切换到 Log4j 了!
SLF4J 除了提供这种解决方案,绑定 Log4j 替换 JUL 和 JCL;还提供了绑定 Logback 替换 JUL、JCL、Log4j 的方案:
还有绑定 JUL 替换 JCL 和 Log4j 的方案:
太强了,有木有?有的话请在留言区敲出 666。
03、SLF4J 比 Log4J 强在哪
SLF4J 除了解决掉以上的痛点,帮助我们的应用程序独立于任何特定的日志系统,还有一个非常牛逼的功能,那就是 SLF4J 在打印日志的时候使用了占位符 {},它有点类似于 String 类的 format() 方法(使用 %s 等填充参数),但更加便捷,这在很大程度上提高了程序的性能。
众所周知,字符串是不可变的,字符串拼接会创建很多不必要的字符串对象,极大的消耗了内存空间。但 Log4J 在打印带参数的日志时,只能使用字符串拼接的方式:
String name = "沉默王二";
int age = 18;
logger.debug(name + ",年纪:" + age + ",是个非常不要脸的程序员");
非常笨重,但加入了 SLF4J 后,这个问题迎刃而解。我们来看一下在 Log4j 项目中加入 SLF4J 的详细的步骤。
第一步,把 log4j 的依赖替换为 slf4j-log4j12(Maven 会自动引入 slf4j-api.jar 和 log4j.jar):
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
第二步,在 resources 目录下创建 log4j.properties 文件,内容和 Log4j 那一篇完全相同:
### 设置### log4j.rootLogger = debug,stdout,D,E ### 输出信息到控制台 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n ### 输出DEBUG 级别以上的日志到=debug.log ### log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File = debug.log log4j.appender.D.Append = true log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 输出ERROR 级别以上的日志到=error.log ### log4j.appender.E = org.apache.log4j.DailyRollingFileAppender log4j.appender.E.File =error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
第三步,新建测试类:
package com.itwanger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author 微信搜「沉默王二」,回复关键字 PDF */ public class Log4jSLF4JDemo { private static final Logger logger = LoggerFactory.getLogger(Log4jSLF4JDemo.class); public static void main(String[] args) { logger.debug("{},是个非常不要脸的程序员","沉默王二"); } }
看到了吧,使用占位符要比“+”操作符方便的多。并且此时不再需要 isDebugEnabled() 先进行判断,debug() 方法会在字符串拼接之前执行。
如果只是 Log4J 的话,会先进行字符串拼接,再执行 debug() 方法,来看示例代码:
String name = "沉默王二";
int age = 18;
logger.debug(name + ",年纪:" + age + ",是个非常不要脸的程序员");
在调试这段代码的时候,你会发现的,如下图所示:
这也就意味着,如果日志系统的级别不是 DEBUG,就会多执行了字符串拼接的操作,白白浪费了性能。
注意,阿里巴巴开发手册上还有一条「强制」级别的规约:
这是因为如果参数是基本数据类型的话,会先进行自动装箱(Integer.valueOf())。测试代码如下所示:
logger.debug("沉默王二,{}岁", 18);
1
通过反编译工具就可以看得到:
logger.debug("\u6C89\u9ED8\u738B\u4E8C\uFF0C{}\u5C81", Integer.valueOf(18));
1
如果参数需要调用其他方法的话,debug() 方法会随后调用。
也就是说,如果不 isDebugEnabled() 的话,在不是 DEBUG 级别的情况下,会多执行自动装箱和调用其他方法的操作——程序的性能就下降了!
测试类运行的结果和之前 Log4J 的一样,小伙伴们可以点击链接跳转到 Log4j 那篇对比下。
04、总结
简单总结一下这篇文章哈。
1)在使用日志系统的时候,一定要使用 SLF4J 作为门面担当。
2)SLF4J 可以统一日志系统,作为上层的抽象接口,不需要关注底层的日志实现,可以是 Log4j,也可以是 Logback,或者 JUL、JCL。
3)SLF4J 在打印日志的时候可以使用占位符,既提高了程序性能(临时字符串少了,垃圾回收的工作量就小),又让代码变得美观统一。
4)小伙伴们如果知道更多秘密的话,建议在留言区贴出来哦。