MrLiar 2020-04-27
我们经常在开发项目的时候,需要打印记录项目过程中的一些日志。那我们经常大概会用到 log4j、jul、jcl、slf4j、simple、nop、logback 等等,那我们就详细介绍下这些组件是怎么做日志打印的
JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。
架构介绍:
Loggers :被称为记录器,应用程序通过获取Logger对象,调用其API来发布日志信息。Logger通常为应用程序访问日志系统的入口程序。
Appenders :也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日 志等。
Layouts :也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
Level :每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我可以将Level和Loggers,Appenders做关联以便于我们过滤消息。
Filters :过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。
总结:
用户使用Logger来进行日志记录,Logger持有若干个Handler,日志的输出操作是由Handler完成的。
在Handler输出日志前,会经过Filter的过滤,判断哪些日志级别过滤放行哪些拦截,
Handler会将日志内容输出到指定位置(日志文件、控制台等)。Handler在输出日志时会使用Layout,将输出内容进行排版
案例:
public class JULTest { @Test public void test01() { // 1.获取日志记录器对象 Logger logger = Logger.getLogger("jul"); // 2.日志记录输出 logger.info("jul"); } }
日志的级别:
jul中定义的日志级别
java.util.logging.Level中定义了日志的级别:
SEVERE(最高值)
WARNING 警告信息
INFO (默认级别)
CONFIG 配置信息
FINE debug级别信息,信息颗粒度最小
FINER debug级别信息
FINEST(最低值)debug级别信息,信息颗粒度最大
还有两个特殊的级别:
OFF,可用来关闭日志记录。
ALL,启用所有消息的日志记录。
日志级别案例:
@Test public void testLogLevel() { // 1.获取日志对象 Logger logger = Logger.getLogger("jul"); // 2.日志记录输出 logger.severe("severe"); logger.warning("warning"); logger.info("info"); logger.config("config"); logger.fine("fine"); logger.finer("finer"); logger.finest("finest"); }
由于默认级别是info, 所以以上日志只会打印 info以上级别的日志
修改日志级别:
@Test public void testLogConfig() throws IOException { // 1.创建日志记录器对象 Logger logger = Logger.getLogger("jul"); // 一、自定义日志级别 // a.关闭系统默认配置 logger.setUseParentHandlers(false); // b.创建handler对象 ConsoleHandler consoleHandler = new ConsoleHandler(); // c.创建formatter对象(简单格式转换对象) SimpleFormatter simpleFormatter = new SimpleFormatter(); // d.进行关联 consoleHandler.setFormatter(simpleFormatter); logger.addHandler(consoleHandler); // e.设置日志级别 logger.setLevel(Level.FINE); consoleHandler.setLevel(Level.FINE); // 二、输出到日志文件 FileHandler fileHandler = new FileHandler("d:/jul.log"); fileHandler.setFormatter(simpleFormatter); logger.addHandler(fileHandler); // 2.日志记录输出 logger.severe("severe"); logger.warning("warning"); logger.info("info"); logger.config("config"); logger.fine("fine"); logger.finer("finer"); logger.finest("finest"); }
JCL全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。
它是为 "所有的Java日志实现"提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常弱(SimpleLog)。所以一般不会单独使用它。
他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk自带的日志(JUL)
JCL 有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)。
默认使用 jul 的实现
案例:
1.添加依赖
<dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
2. 测试
public class JCLTest { @Test public void test() { // 创建日志对象 Log log = LogFactory.getLog("jcl"); // 日志记录输出 log.info("info"); } }
JCL原理:
1. 通过LogFactory动态加载Log实现类
2 . 日志门面支持的日志实现数组
3 . 获取具体的日志实现
Log4j是Apache下的一款开源的日志框架,通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目的调试
log4j.properties(最简单配置)
log4j.rootLogger=INFO,Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
案例:
1. 添加依赖
<dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
2. 测试
@Test public void test01() { // 获取日志记录器对象 Logger logger = Logger.getLogger(Log4jTest.class); // 日志记录输出 logger.info("hello log4j"); // 日志测试 logger.info("info"); }
日志的级别:
每个Logger都有一个日志级别(log level),用来控制日志信息的输出。日志级别从高到低分为:
fatal 指出每个严重的错误事件将会导致应用程序的退出。
error 指出虽然发生错误事件,但仍然不影响系统的继续运行。
warn 表明会出现潜在的错误情形。
info 一般和在粗粒度级别上,强调应用程序的运行全程。
debug 一般用于细粒度级别上,对调试应用程序非常有帮助。(默认级别)
trace 是程序追踪,可以用于输出程序运行中的变量,显示执行的流程。
还有两个特殊的级别:
OFF,可用来关闭日志记录。
ALL,启用所有消息的日志记录。
注:一般只使用 4个级别,优先级从高到低为 ERROR > WARN > INFO > DEBUG
简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等。当然slf4j自己也提供了功能较为简单的实现,但是一般很少用到。对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,通过绑定器绑定具体的实现框架(log4j、logback等)进行日志打印,与第三方其它框架统一日志的时候,中间使用桥接器完成桥接。
SLF4J是目前市面上最流行的日志门面。现在的项目中,基本上都是使用SLF4J作为日志系统。
SLF4J日志门面主要提供两大功能:
1. 日志框架的绑定
2. 日志框架的桥接
入门案例:
1. 添加依赖
<dependencies> <!--slf4j core 使用slf4j必須添加--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!--slf4j 自带的简单日志实现 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.27</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
2. 测试
public class Slf4jTest { // 声明日志对象 public final static Logger LOGGER = LoggerFactory.getLogger("slf4j"); @Test public void test01() { //打印日志信息 LOGGER.info("info"); } }
SLF4J支持各种日志框架。SLF4J发行版附带了几个称为“SLF4J绑定”的jar文件,每个绑定对应一个受支持的框架。
使用slf4j的日志绑定流程:
1. 添加slf4j-api的依赖
2. 使用slf4j的API在项目中进行统一的日志记录
3. 绑定具体的日志实现框架
1. 绑定已经实现了slf4j的日志框架,直接添加对应依赖
2. 绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
4. slf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)
通过maven引入常见的日志实现框架:(根据实际情况选择使用,除了引入下面其中一个,还不能少了 slf4j-api 的引用)
官方绑定器API地址: http://www.slf4j.org/manual.html
1. logback
<!-- logback 日志实现 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
2. nop
<!-- nop 日志开关(不使用日志功能) --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.2</version> </dependency>
3. log4j
<!-- 绑定 log4j 日志实现,需要导入适配器,还需要添加 log4j.properties 配置文件 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.26</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
4. jul
<!-- 绑定 jul 日志实现,需要导入适配器 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.25</version> </dependency> <!-- 因为是java原生的日志框架,已经内置了具体实现 -->
原理:
桥接解决的是项目中日志的遗留问题,当系统中存在之前的日志API,可以通过桥接转换到slf4j的实现
1. 先去除之前老的日志框架的依赖
2. 添加SLF4J提供的桥接组件
3. 为项目添加SLF4J的具体实现
4. 统一应用和框架的日志实现
迁移的方式:
如果我们要使用SLF4J的桥接器,替换原有的日志框架,那么我们需要做的第一件事情,就是删除掉原有项目中的日志框架的依赖。然后替换成SLF4J提供的桥接器。
官方api地址: http://www.slf4j.org/legacy.html
原理:
1. log4j
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!-- 配置 log4j 的桥接器 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.25</version> </dependency>
2. logback
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!-- 配置 log4j 的桥接器 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.25</version> </dependency> <!-- logback 日志实现 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
3. jcl
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!-- 配置 jcl 的桥接器 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.8</version> </dependency>
4. jul
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!-- 配置 jul 的桥接器 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>1.7.25</version> </dependency>
注:
1. jcl-over-slf4j.jar(桥接)和 slf4j-jcl.jar(适配)不能同时部署。前一个jar文件将导致JCL将日志系统的选择委托给SLF4J,后一个jar文件将导致SLF4J将日志系统的选择委托给JCL,从而导致无限循环 。
2. log4j-over-slf4j.jar和slf4j-log4j12.jar不能同时出现,原因同上
3 . jul-to-slf4j.jar和slf4j-jdk14.jar不能同时出现,原因同上
4. 所有的桥接都只对Logger日志记录器对象有效,如果程序中调用了内部的配置类或者是Appender,Filter等对象,将无法产生效果。