目录:
概览
日志的发展历程
基本用法
1 log4j
2 jul
3 jcl
4 slf4j
5 log4j2
参考/来源:
概览
市面流行的日志框架:
- JUL:java util logging Java原生日志框架。
- Log4j:Apache的一个开源项目。
- Logbcak :由Log4j之父做的另一个开源项目,业界中称为 log4j 后浪。他是一个可靠、通用且灵活的Java日志框架。
- Log4j2 :Log4j的第二个版本,各个方面与Logback及其相似。具有插件式结构、配置文件优化等特征,在Spring Boot1.4版本之后就不在支持 log4j ,所以出现了第二个版本的。
- JCL
- SLF4j
分类:
- 日志框架技术 :JUL、Log4j、Logbcak、Log4j2
- 日志门面技术 :JCL、SLF4j
为什么要使用日志门面技术:
每一种日志框架都有自己单独的API,要使用对应的框架就要使用对应的API,这就大大的增加了应用程序代码对于日志框架的耦合性。使用日志门面技术之后,不论底层是什么日志框架,我们拿到代码之后可以使用自己习惯的日志框架就行解读,不用修改一行代码。
其实框架1调用的是自己的方法a() ,框架2调用的自己的方法b() ,此时将这两个方法抽取出来称为方法c()。
日志的发展历程
从最早期开始,大家都是使用
System.out
和System.err
来打印日志;不灵活也不可以配置;要么全部打印,要么全部不打印;没有一个统一的日志级别后来
Log4j
就出现了,它是Ceki Gülcü
这个大佬开发的,后来Log4j
成为了Apache
基金会项目中的一员后来
Java
也推出了自己的日志框架JUL(Java Util Logging)
,在package java.util.logging
下Apache
又推出了日志接口Jakarta Commons Logging
,也就是日志抽象层,你就可以很方便的在Log4j
和JUL
之间做切换Ceki Gülcü
觉得觉得JCL
不好,开发了一套新的日志门面Slf4j(Simple Logging Facade for Java)
、它的实现Logback
以及一些桥接包:jcl-over-slf4j.jar :jcl ——> slf4j
slf4j-jcl.jar :slf4j ——> jcl
log4j-over-slf4j :log4j ——> slf4j
slf4j-log4j12.jar :slf4j ——> log4j
jul-to-slf4j :jul ——> slf4j
slf4j-jdk14.jar :slf4j ——> jul后来
Apache
直接推出新项目,不是Log4j1.x
升级,而是新项目Log4j2
,因为Log4j2
是完全不兼容Log4j1.x
的,它也搞了分离的设计,分化成log4j-api
和log4j-core
,这个log4j-api
也是日志接口,log4j-core
是日志实现,它也出了很多桥接包:log4j-jcl :jcl ——> log4j2
log4j-1.2-api :log4j ——> log4j2
log4j-slf4j-impl :slf4j ——> log4j2
log4j-jul :jul ——> log4j2
log4j-to-slf4j :log4j2 ——> slf4j
基本用法
log4j
依赖
<dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
配置文件
log4j.properties
# Set root logger level to DEBUG and its only appender to A1. log4j.rootLogger=DEBUG, A1 # A1 is set to be a ConsoleAppender. log4j.appender.A1=org.apache.log4j.ConsoleAppender # A1 uses PatternLayout. log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
测试类
import org.apache.log4j.Logger; public class Main { public static void main(String[] args) { Logger logger = Logger.getLogger(Main.class); logger.info("hello, world!"); } } /* 输出: 0 [main] INFO cn.eagleli.log.log4j.Main - hello, world! */
jul
import java.util.logging.Logger;
public class Main {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class.getName());
logger.info("hello, world!");
}
}
/*
输出:
八月 11, 2021 11:06:19 下午 cn.eagleli.log.jul.Main main
信息: hello, world!
*/
jcl
依赖:
<dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> </dependencies>
测试类
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Main { public static void main(String[] args) { Log log = LogFactory.getLog(Main.class); log.info("hello, world!"); } } /* 输出: 八月 11, 2021 11:08:25 下午 cn.eagleli.log.jcl.Main main 信息: hello, world! */
从上面输出结果可以看出,默认会使用
jul
作为底层的日志框架如果我们想换成
log4j
作为底层的日志框架,怎么办呢?只需要加一个依赖即可,如下:依赖
<dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
输出
0 [main] INFO cn.eagleli.log.jcl.Main - hello, world!
从结果可以看出,底层日志框架已经变了,同样发现,我们的代码没有任何改动,只是加了一个依赖,由此可以看出接口的重要性。
slf4j
依赖
<dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> </dependencies>
测试类
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Main { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(Main.class); logger.info("hello, world!"); } } /* 输出: 23:14:30.893 [main] INFO cn.eagleli.log.slf4j.Main - hello, world! */
上面底层日志框架使用的是
logback
那如果我们想切换成
jcl
作为底层实现框架,怎么办呢?只需换一个依赖即可,如下:改变依赖
<dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jcl</artifactId> <version>1.7.30</version> </dependency> </dependencies>
输出
输出: 八月 11, 2021 11:18:27 下午 org.slf4j.impl.JCLLoggerAdapter info 信息: hello, world!
从上面结果可以看出,底层已经切到
jcl
了,而jcl
默认采用的是jdk
日志框架cl-over-slf4j
和slf4j-jcl
是不能同时使用的,因为前一个使用jcl API
桥接到slf4j
,后一个是使用slf4j API
桥接到jcl
,如果同时引用会导致循环调用,进而导致栈溢出
log4j2
依赖
<dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.14.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.14.1</version> </dependency> </dependencies>
测试类
测试类: import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Main { public static void main(String[] args) { Logger logger = LogManager.getLogger(Main.class); logger.error("hello, world!"); } } /* 输出: 23:22:12.148 [main] ERROR cn.eagleli.log.log4j2.Main - hello, world! */
以上我们采用的
log4j2
作为底层的实现,我们想要用slf4j
作为底层的实现,怎么办呢?只需加一个依赖即可,如下:改变依赖
<dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.14.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-to-slf4j</artifactId> <version>2.14.1</version> </dependency> </dependencies>
输出
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
从结果看出,我们底层日志框架已经切换了,因为没有任何
slf4j
的实现类框架,所以没有输出日志。