文章目录
Java Log 快速入门
一、日志框架介绍
1、Java 日志框架的分类
在 Java 框架系统中,为了可以在不同的日志系统中进行切换,于是将 Java 日志API的调用
和实现
进行分离
专门进行 API
定义的叫做 日志门面
Java 日志实现
则是针对专门的日志门面
实现的
2、具体框架
在Java里面,流行的共有两种日志门面
- JCL(Jakarta Common Logging),该日志下实现较老,现在不流行使用
- SLF4J(Simple Logging Facade For Java),目前流行的日志门面
门面日志只能给出有哪些隔离级别,但不能设置,具体的级别由日志实现决定,这点非常重要
同样,在Java生态中也存在的很多的Java日志实现
- Log4j,公有两个大版本,
Log4j1
和Log4j2
,1.x
版本已停止维护,现在一般是直接使用2.x
版本,且性能更高(支持异步日志) - JUL,这是
JavaSE
默认的日志实现,默认的配置文件位于JAVA_HOME
目录下的conf
文件夹下的logging.properties文件 - Logback,
Log4j1
作者的另一个日志框架,性能也很高,SpringBoot
的默认日志框架
二、不同日志框架快速使用
日志门面就是一个API
框架,他会自动发现classpath
下实现该API
的日志框架并使用,如果发现多个,则会输出警告并选择一个日志实现
利用日志门面API
,可以获取对应的 Logger
对象并使用,它真实的工作过程还是使用的日志实现,日志门面只是个封装作用
(1)、JCL
在JCL
里面有两个重要的对象,
日志的隔离级别
trace > debug > info > warn > error > fatal
,这个隔离级别是接口的,门面日志不能设置隔离级别
- LogFactory,日志工厂,利用日志工厂可以获取一个日志对象,其他框架为了实现该日志门面,需要实现该抽象类
- Log,日志对象,在
JCL
里面就是个通用的接口,通过该接口对象完成日志的输出
调用 **LogFactory.getLog()**获取一个日志对象(注意接口的包路径)
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.error("Hello World!");
}
}
在新版本的JCL
里面,内置了多个LogFactory
和Logger
的实现,比如log4j2、slf4j、JUL
,所以只需要导入对应的依赖就可以直接使用了
如果存在多个实现,则会优先log4j2
,再slf4j
,最后如果没有提供任何实现时,则使用JUL
(或通过commons-logging.properties
配置文件指定)
关于slf4j
,这其实是另一个日志门面,也就是说它也只是一套API,它需要他自己的门面日志实现
如果使用了JCL
没有默认实现的日志系统,则需要自己提供对应的适配器(使日志框架适配日志门面)
大概过程是实现LogFactory
抽象类和Logger
接口,并使用commons-logging.properties(resources目录下)
配置文件指定
由于传统的JCL
性能不太好,所以Spirng
重写了一套JCL
实现,叫做spring-jcl
,它的工作原理和过程与传统的JCL
几乎相同,就连包名都相同
使用时,只需要导入使用的日志实现就可以了
关于如何扩展
spring-jcl
,还不知道(可以是通过spi发现,传统jcl可能是通过实现LogFactory类发现)
导入依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.5</version>
</dependency>
(2)、SLF4J
导入依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
SLF4J
是目前最流行的日志门面,使用时只需要通过LoggerFactory.getLogger()
来获取Logger
对象,如下示例所示:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main
{
public static void main(String[] args)
{
Logger logger = LoggerFactory.getLogger(Main.class);
logger.error("Hello World");
}
}
一般而言,会把Logger
对象设置为private static final
类型
为了使SLF4J
与多个不同的日志系统结合工作,所以存在适配器
和桥接器
两个工具
适配器
由于SLF4J
只是一个日志门面,需要依赖一个具体的日志实现,且这个日志实现必须实现SLF4J
的接口,但由于已经存在很多已经存在的日志框架
为了使其也能被SLF4J
日志门面发现,需要一个适配器
,使SLF4J
可以作为他们的日志门面,官方示例图如下所示:
正如上图所示,要想使用
Log4j、JUL
需要添加适配器,依赖如下所示
-
Log4j1(slf4j-log4j12)
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>2.0.17</version> </dependency>
-
由于存在版本漏洞问题,为了修复,上面依赖再构建依赖时会重定向到
reload4j
,依赖如下所示<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-reload4j</artifactId> <version>2.0.17</version> </dependency>
上面两个依赖的效果相同
-
Log4j2(log4j-slf4j-impl)
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.24.3</version> </dependency>
注意
log4j1
和log4j2
所需要的依赖不同 -
JUL(slf4j-jdk14)
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>2.0.17</version> </dependency>
上面这些依赖都是需要进行适配的日志框架所需要添加的依赖
而对于那些日志框架本身就实现了SLF4J的日志框架,则不需要添加对应的适配器,比如logback、simple、no-operation
桥接器
对于那些没有使用SLF4J
日志门面获取使用了其他的日志门面(JCL
)的项目,要想在再不需要代码的情况下使用SLF4J
日志门面,就需要桥接器
,官网图如图所示,它可以对
JCL、Log4j、JUL
进行桥接,对应的需要添加以下依赖:
-
Log4j1(log4j-over-slf4j)
<dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>2.0.17</version> </dependency>
-
Log4j2(log4j-to-slf4j)
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-to-slf4j</artifactId> <version>2.21.1</version> </dependency>
-
JUL(jul-to-slf4j)
<dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>2.0.13</version> </dependency>
-
JCL(jcl-over-slf4j)
<dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>2.0.17</version> </dependency>
对于SLF4J
而言,有两个大版本,且共工作方式完全不同,使用时使用注意版本
和日志实现
或者适配器
的关系
对于1.x
而言,对应的日志实现需要提供org.slf4j.impl.StaticLoggerBinder.class
类文件,如果存在多个,则会显示警告(打印所有实现)并使用第一个日志实现
对于2.x
而言,使用的时JDK
的SPI
机制,需要提供META-INF/services/org.slf4j.spi.SLF4JServiceProvider
文件,该文件的内容只想一个日志实现,如果存在多个同样会显示警告并打印所有实现,同时只使用第一个实现
上面的都是日志门面的使用,下面的都是日志实现框架的使用
(1)、LOG4J
LOG4J
有两个版本,1.x
版本已停止维护,现在基本都是使用的2.x
版本,且性能非常的高
日志的隔离级别
trace > debug > info > warn > error > fatal
,默认日志隔离级别为error
1.x
版本的简单使用
-
导入依赖
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency>
-
使用
import org.apache.log4j.Logger; public class Main { public static void main(String[] args) { Logger logger = Logger.getLogger(Main.class); logger.error("Hello World"); } }
使用
Logger.getLogger()
获取日志对象 -
添加配置文件
1.x
版本的Logger
使用要求类路径下必须存在log4j.xml
配置文件,否则将无法工作,例如上面的代码控制台会出现报错(配置文件这个就不写了)
2.x
版本的使用发生了很大的变化
-
导入依赖
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.23.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.23.1</version> </dependency>
上面导入的依赖表示
2.x
版本的LOG4J
由两部分组成,一个是具体的实现log4j-core
,另一个时日志门面log4j-api
没错,在
2.x
版本里也有个LOG4J
实现的日志门面log4j-api
,只不过使用的不多,且它最出名的是日志实现log4j-core
,log4j-core
依赖包含log4j-api
,所以只导入log4j-core
同样是可以的 -
使用
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"); } }
使用
LogManager.getLogger()
获取日志对象,默认的日志级别为error
-
添加配置文件
在
2.x
版本里,不需要配置文件也可以正常工作,当然一般都会添加配置文件来完成个性化定义,配置文件位于根类类路径下,也就是resources
目录下文件名为
log4j2.xml
,2.x
版本也支持其他格式的配置文件(json、properties
等)这些配置也可以使用
编程方式配置
,重要的对象由LoggerContent、LoggerManager、LoggerContentFactory、LoggerConfiguration等
(2)、JUL
JUL(java util logging)
,是Java自带的日志实现,位于java.util
包下,因此不需要导入其他的依赖
日志隔离级别
finest > finer > fine > config > info > warning > severe
,默认的日志级别为info
-
使用
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"); } }
-
添加配置文件
由于是Java平台自带的日志实现,所以存在默认的配置文件,位于
JAVA_HONE
目录下的conf
文件夹下(java8位于jre内),配置文件名为logging.properties
当然这是默认的配置文件,也可以在类路径下添加
logging.properties
文件来覆盖默认的配置当然,一切也是可以使用
编程方式配置的
(3)、LOGBACK
LOGBACK
是Log4j1
之后出来的,且和SLF4J
一同推出,所以它本身就符合slf4j-api
的规范
LOGBACK
由两部分组成,logback-classic 和 logback-core
两部分,logback-classic
包含logback-core
和slf4j-api
所以使用LOGBACK
就是直接使用SLF4J
api相关的操作
日志隔离级别(同时也是SLF4J
Logger接口的隔离级别,门面日志只能给出有哪些隔离级别,但不能设置,具体的级别由日志实现决定)
trace > debug > info > warn > error
,默认的日志隔离级别为debug
-
使用
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Main { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(Main.class); logger.trace("Hello World"); logger.debug("Hello World"); logger.info("Hello World"); logger.warn("Hello World"); logger.error("Hello World"); } }
-
添加配置文件
LOGBACK
不需要配置文件也可以正常工作,也可以通过在类路径下(resources
)添加logback.xml
文件来自定义配置最后,它也是可以通过
编程方式配置的
三、简单的工作流程和原理
不论是LOG4J
还是LOGBACK
和JUL
,其内部都维持这一个配置对象,这个配置对象可以是通过编程方式配置的,也可以是通过解析配置文件
而获得过的
对于LOG4J
和LOGBACK
(高版本中),都使用了服务发现机制,为了解决在存在多个可用日志系统时指定所使用的系统,都可以通过配置文件指定需要使用的日志实现
如果不存在指定的日志实现(或者没有提供),则会使用默认的配置,一般都是寻找某个服务类或者实现特定接口或抽象类的类,并从中选择一个使用,应该是选择第一个找到的实现,所以classpath
中依赖的顺序很重要
对于如何管理日志器,一般会存在一个LoggerContext
对象,表示一个日志系统上下文,其中包含日志器的配置、Appender、Filter、Formatter、Level
等,一般来说一个系统只有一个LoggerContext
,但对于特殊的应用环境也可以存在多个,同时也会有一个LoggerContextFactory
表示LoggerContext
的工厂,等对象
关于配置文件,一般日志框架都会须在classpath
路径下指定名字的文件,且这个文件应该为classpath
的根路径下,由于其他jar
也可能存在同名的文件,所以自己系统的classes
文件夹一定要classpath
中的第一个(一般来说找到第一个匹配的文件就会立即返回)
四、版本注意事项
1、log4j-slf4j-impl
内部slf4j
版本注意
由于SLF4J
的1.x
和2.x
版本的工作方式完全不相同,也不兼容,所以需要特别注意日志实现或者适配器
使用的是哪个版本的SLF4J
在log4j-slf4j-impl
适配器依赖内,包含log4j-core
依赖和slf4j-api
依赖,这里的slf4j-api
依赖是1.x
版本的