Java日志框架
一、日志概述
1.1、日志文件
- 日志文件是用于记录系统操作事件的文件集合。
- 日志文件它具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要的作用。
1.2、调试日志
- 我们平时在调试程序的过程中所使用的肯定就是专业开发工具自带的debug功能,可以实时查看程序运行情况,不能够有效保存运行情况的信息。
- 调试日志是能够更加方便的去重现这些问题。
1.3、系统日志
系统日志包括系统日志、应用日志和安全日志这几种分类。
- 系统日志是用来记录系统中硬件、软件和系统相关问题的信息。同时还可以监视系统中发生的事件。用户可以通过它来检查错误发生的原因,或者寻找收到攻击是留下的痕迹
二、日志框架
1.1、常见的日志框架
-
j.u.l j.u.l是java.util.logging包的简称,是JDK在1.4版本中引入的Java原生日志框架
-
Log4j Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式
-
Log4j2 Log4j官方的第二个版本,各个方面都是与Logback及其相似 Log4j2已经不仅仅是Log4j的一个升级版本了,而是从头到尾被重写的,这可以认为这其实就是完全不同的两个框架
-
LogBack 由Log4j之父做的另一个开源项目 LogBack也是一个很成熟的日志框架,其实LogBack和Log4j出自一个人之手,这个人就是Ceki Gülcü。 logback当前分成三个模块:logback-core、logback- classic、logback-access。 logback-core是其它两个模块的基础模块。 logback-classic是Log4j的一个改良版本。 此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日记系统如 Log4j或j.u.l。 logback-access访问模块与Servlet容器集成提供通过Http来访问日记的功能。
1.2、日志框架的作用
- 控制日志输出的内容和格式。
- 控制日志输出的位置。
- 日志文件相关的优化,如异步操作、归档、压缩…
- 日志系统的维护
- 面向接口开发 – 日志的门面(jcl 、slf4j)
1.3、日志门面和日志框架的区别
1.3.1、种类和出现顺序
日志框架技术: JUL、Logback、Log4j、Log4j2
日志门面技术: JCL、SLF4j
出现顺序: log4j -->JUL–>JCL–> slf4j --> logback --> log4j2
1.3.2、看下阿里手册的说明
不知道有多少人看过《阿里巴巴Java开发手册》,其中有一条规范做了『强制』要求:
为什么阿里巴巴禁止工程师直接使用日志系统(Log4j、Logback)中的 API? 这边就是日志门面的就出来了
1.3.3、为什么要使用日志门面技术
日志门面技术 JCL、SLF4j
注明: spring用JCL springboot用SLF4j(后面会进行说明)
在软件开发领域有这样一句话:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。 而门面模式就是对于这句话的典型实践。
原因说明:
- 每一种日志框架都有自己单独的API,要使用对应的框架就要使用对应的API,这就大大的增加了应用程序代码对于日志框架的耦合性
- 我们使用了日志门面技术之后,对于应用程序来说,无论底层的日志框架如何改变,应用程序不需要修改任意一行代码,就可以直接上线了。
日志门面的一个比较重要的好处——解耦。 可以成理解为项目面向统一抽象层编程。后面可以看slf4j进行理解
1.4、 日志级别
由低到高 trace < debug < info < warn < error
三、JUL(了解)
1.1、JUL简介
JUL全程 Java Util Logging,它是java原生的日志框架,使用时不需要另外引用第三方的类库,相对其他的框架使用方便,学习简单,主要是使用在 小型应用(Java基础的应用) 中。
1.2、JUL组件
图示: 说明:
- Logger:被称为记录器,应用程序通过获取Logger对象,抵用其API来发布日志信息。Logger通常被认为是访问日志系统的入口程序。
- Handler:处理器,每个Logger都会关联一个或者是一组Handler,Logger会将日志交给关联的Handler去做处理,由Handler负责将日志做记录。Handler具体实现了日志的输出位置,比如可以输出到控制台或者是文件中等等。
- Filter:过滤器,根据需要定制哪些信息会被记录,哪些信息会被略过。
- Formatter:格式化组件,它负责对日志中的数据和信息进行转换和格式化,所以它决定了我们输出日志最终的形式。
- Level:日志的输出级别,每条日志消息都有一个关联的级别。我们根据输出级别的设置,用来展现最终所呈现的日志信息。根据不同的需求,去设置不同的级别。
1.3、使用
package sqy.log;
import org.junit.Test;
import java.util.logging.*;
public class JULTest {
@Test
public void test01(){
System.out.println(JULTest.class.getName());
Logger logger = Logger.getLogger(JULTest.class.getName());
logger.info("输出info信息1");
logger.log(Level.INFO,"输出info信息2");
String name = "zhangsan";
int age = 18;
logger.log(Level.INFO,"学生的姓名:{0},年龄:{1}",new Object[]{name,age});
}
}
四、Log4j ( log for java)
Log4j官网文档
我们使用log4j技术,主要使用的是其配置文件
1.1、简介
- Log4j是Apache的一个开源项目
- 通过使用Log4j,可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
- 也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程
- 通过配置文件来灵活地进行配置
1.2、组件
- Loggers (日志记录器)
Loggers 控制日志的输出以及输出级别 - Appenders(输出控制器)
指定日志的输出方式(输出到控制台、文件等) - Layout(日志格式化器)
控制日志信息的输出格式
1.3、详细说明
1.3.1、常用日志级别
DEBUG < INFO < WARN < ERROR
1.3.2、5个常用Appenders
- ConsoleAppender :将日志输出到控制台
- FileAppender :将日志输出到文件中
- DailyRollingFileAppender :将日志输出到一个日志文件,并且每天输出到一个新的文件
- RollingFileAppender :将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件
- JDBCAppender :把日志信息保存到数据库中
1.3.3、常用3个Layouts
Layouts提供四种日志输出样式, 如根据HTML样式 自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式
- HTMLLayout :格式化日志输出为HTML表格形式
- SimpleLayout :简单的日志输出格式化,打印的日志格式如默认INFO级别的消息
- PatternLayout :最强大最常用的格式化组件,可以根据自定义格式输出日志,如果没有指定转换格式, 就是用默认的转换格式
1.3.4、日志输出格式(占位符)说明
%m 输出代码中指定的日志信息
%p 输出优先级,及 DEBUG、INFO 等
%n 换行符(Windows平台的换行符为 "\n",Unix 平台为 "\n")
%r 输出自应用启动到输出该 log 信息耗费的毫秒数
%c 输出打印语句所属的类的全名
%t 输出产生该日志的线程全名
%d 输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}
%l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10)
%F 输出日志消息产生时所在的文件名称
%L 输出代码中的行号
%% 输出一个 "%" 字符
%5c 输出category名称,最小宽度是5,category<5,默认的情况下右对齐
%-5c 输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格
%.5c 输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格
%20.30c category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉
1.4、使用(输出、持久化到文件、持久化到数据库)
maven项目
pom文件:
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
<!-- 单元测试4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
配置文件 log4j.properties(resources目录下)
# %m 输出代码中指定的日志信息
# %p 输出优先级,及 DEBUG、INFO 等
# %n 换行符(Windows平台的换行符为 "\n",Unix 平台为 "\n")
# %r 输出自应用启动到输出该 log 信息耗费的毫秒数
# %c 输出打印语句所属的类的全名
# %t 输出产生该日志的线程全名
# %d 输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}
# %l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10)
# %F 输出日志消息产生时所在的文件名称
# %L 输出代码中的行号
# %% 输出一个 "%" 字符
# [%p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
# 可以在 % 与字符之间加上修饰符来控制最小宽度、最大宽度和文本的对其方式
# [%10p]:[]中必须有10个字符,由空格来进行补齐,信息右对齐
#配置根节点logger
log4j.rootLogger=trace,console
#配置自定义logger
log4j.logger.com.bjpowernode.log4j.test=info,file
#配置apache的logger
log4j.logger.org.apache=error
#配置appender输出方式 输出到控制台
log4j.appender.console=org.apache.log4j.ConsoleAppender
#配置输出到控制台的格式
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
#配置appender输出方式 输出到文件
log4j.appender.file=org.apache.log4j.FileAppender
#配置输出到文件中的格式
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
#第一个file是我们自己命名的appenderName,第二个file是用来指定文件位置的属性
log4j.appender.file.file=D://test
#配置输出字符编码
log4j.appender.file.encoding=UTF-8
#RollingFileAppender的配置,我们可以针对于实际含义起名
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.rollingFile.file=D://test
log4j.appender.rollingFile.encoding=UTF-8
#指定日志文件内容大小
log4j.appender.rollingFile.maxFileSize=1MB
#指定日志文件的数量
log4j.appender.rollingFile.maxBackupIndex=5
#DailyRollingFileAppender的配置,我们可以针对于实际含义起名
log4j.appender.dailyRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.dailyRollingFile.file=D://test
log4j.appender.dailyRollingFile.encoding=UTF-8
log4j.appender.dailyRollingFile.datePattern='.'yyyy-MM-dd HH-mm-ss
#配置appender输出方式 输出到数据库表
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=root
log4j.appender.logDB.Sql=INSERT INTO tbl_log(name,createTime,level,category,fileName,message) values('project_log','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%m')
测试案例:
package sqy.log;
import org.junit.Test;
import org.apache.log4j.*;
public class Log4jTest {
@Test
public void test01(){
Logger logger = Logger.getLogger(Log4jTest.class);
logger.fatal("我是fatal信息");
logger.error("我是error信息");
logger.warn("我是warn信息");
logger.info("我是info信息");
logger.debug("我是debug信息");
logger.trace("我是trace信息");
}
@Test
public void test02(){
Logger logger = Logger.getLogger(Log4jTest.class);
for (int i = 0; i < 10000; i++) {
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
}
@Test
public void test03(){
Logger logger = Logger.getLogger(Log4jTest.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
}
五、Logback
1.1、Logback说明
- Logback是由log4j创始人设计的又一个开源日志组件。
- Logback当前分成三个模块:logback-core,logback- classic和logback-access。
- logback-core是其它两个模块的基础模块。
- logback-classic完整实现SLF4J API。可以很方便地更换成其它日志系统如log4j或JDK14 Logging
- logback-access访问模块与Servlet容器集成提供通过Http来访问日志的功能。
- logback-classic是log4j的一个改良版本
Logback性能比log4j强很多
1.2、Logback配置文件
- logback.groovy
- logback-test.xml
- logback.xml
- logback-spring.xml
- 如果都不存在则采用默认的配置
1.2.1、配置文件分析解读
- 通用配置抽取EL表示式获取value值
- 控制台appender的配置
- 日志持久化到文件中的appender(普通文件、不拆分)
- 日志保存到html中的appender (html文件)
- 对日志文件进行 拆分归档 的appender配置(升级版)
- 控制台appender的过滤器的配置(过滤器)
- 异步日志的配置
- 引入自定义appender、配置隔离级别
详细信息看注释
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--configuration 根节点-->
<configuration>
<!--
配置文件通用属性
<property name="" value=""></property>
通过以${name}的形式,方便的取得value值 (el表达式的方式)
-->
<!--定义俩种格式化的方式 通过name来取值-->
<property name="pattern01" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n"></property>
<property name="pattern02" value="[%-5level]%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m%n"></property>
<!-- 日志的保存位置-->
<property name="logDir" value="D://logs"></property>
<!--================================通用配置===========================================-->
<!-- 01、控制台appender 名字随便取不重复就行-->
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<!--
System.out 表示以黑色字体输出日志(默认)
System.err 表示以红色字体输出日志
-->
<target>System.err</target>
<!-- 配置日志输出格式,直接引入上述的通用属性即可-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 pattern01上面定义了-->
<pattern>${pattern01}</pattern>
</encoder>
</appender>
<!-- 02、日志保存到文件中的appender (普通文件)-->
<appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
<!-- 引入文件位置和文件名 -->
<file>${logDir}/logback.log</file>
<!-- 设置输出格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern01}</pattern>
</encoder>
</appender>
<!-- 03、日志保存到html中的appender (html文件)-->
<appender name="htmlAppender" class="ch.qos.logback.core.FileAppender">
<!--文件位置和文件名称-->
<file>${logDir}/logback.html</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<!--用HTMLLayout-->
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>${pattern02}</pattern>
</layout>
</encoder>
</appender>
<!-- 04、对日志文件进行 拆分归档 的appender配置 -->
<appender name="rollAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 输入格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern01}</pattern>
</encoder>
<!-- 文件位置和名称 -->
<file>${logDir}/roll_logback.log</file>
<!-- 指定拆分规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按照时间和压缩格式声明文件名 压缩格式zip -->
<fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.zip</fileNamePattern>
<!-- 按照文件大小来进行拆分 -->
<maxFileSize>1KB</maxFileSize>
</rollingPolicy>
</appender>
<!--05、 控制台appender的过滤器 (过滤器)-->
<appender name="filterConsoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<!--红色字体输出。默认黑色-->
<target>System.err</target>
<!--指定格式化的格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern01}</pattern>
</encoder>
<!-- 配置过滤器 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--设置日志的输出级别 -->
<level>ERROR</level>
<!--表示:高于level中设置的级别,则打印日志 -->
<onMatch>ACCEPT</onMatch>
<!--表示: 低于level中设置的级别,则屏蔽日志 -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--06、配置异步日志 -->
<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<!--引入控制台的consoleAppender (引入的appender看自己需求)-->
<appender-ref ref="consoleAppender"/>
</appender>
<!--07、配置隔离级别 all表示全部 -->
<root level="ALL">
<!--引入我们定义的appender-->
<appender-ref ref="consoleAppender"/><!--控制台-->
<appender-ref ref="filterConsoleAppender"/><!--控制台输出的过滤器 (注释掉consoleAppender)-->
<appender-ref ref="fileAppender"/><!--普通文件持久化 (不拆分会导致一个文件过大)-->
<appender-ref ref="htmlAppender"/><!--保存到html文件中,可以改html的样式,数据量不大用这个。可读性强-->
<appender-ref ref="rollAppender"/><!--对日志文件拆分压缩 就是fileAppender的升级版,直接用这个就行了。fileAppender可以注释掉-->
<appender-ref ref="asyncAppender"/><!--异步日志 提高性能(原理是多线程抢占线程)(注释掉在异步引入的Appender例如consoleAppender)-->
</root>
</configuration>
<!--
%-10level 级别 10个字符,左对齐
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期
%c 当前类全限定名
%M 当前执行日志的方法
%L 行号
%thread 线程名称
%m或者%msg 信息
%n 换行
-->
1.3、案例使用
pom文件
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- logback日志实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
测试代码
package sqy;
import com.bjpowernode.logback.test01.LOGBACKTest01;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogBackTest {
@Test
public void test01(){
Logger logger = LoggerFactory.getLogger(LogBackTest.class);
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
@Test
public void test02(){
for (int i = 0; i < 50; i++) {
Logger logger = LoggerFactory.getLogger(LogBackTest.class);
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
System.out.println("开启异步日志===>01");
System.out.println("开启异步日志===>02");
System.out.println("开启异步日志===>03");
System.out.println("开启异步日志===>04");
}
}
拆分压缩效果: html和普通文件效果 异步日志效果
1.4、Logback异步日志说明( log4j2修复提升)
可配置属性
可配置属性 < discardingThreshold>0< /discardingThreshold> 设置为0,说明永远都不会丢弃trace、debug、info这3个级别的日志 问题在于: 配置了一个阈值当队列的剩余容量小于这个阈值的时候,当前日志的级别 trace、debug、info这3个级别的日志将被丢弃
配置队列的深度
配置队列的深度 < queueSize>256</ queueSize>,这个值会影响记录日志的性能 默认值就是256
- 关于这两个属性,一般情况下,我们使用默认值即可
- 不要乱配置,会影响系统性能,了解其功能即可
六、JCL(日志门面-spring5用这个)
1.1 说明
全称为Jakarta Commons Logging,是Apache提供的一个通用日志API
用户可以自由选择第三方的日志组件作为具体实现 ,像log4j,或者jdk自带的jul, common-logging会通过动态查找的机制,在程序运行时自动找出真正使用的日志库- common-logging内部有一个Simple logger的简单实现,但是功能很弱。所以使用common-logging,通常都是配合着log4j以及其他日志框架来使用
好处:
使用它的好处就是,代码依赖是common-logging而非log4j的API,避免了和具体的日志API直接耦合,在有必要时,可以更改日志实现的第三方库
JCL 有两个基本的抽象类:
- Log:日志记录器
- LogFactory:日志工厂(负责创建Log实例)
1.2、JCL源码剖析
这看下这篇文章就可以==>文章地址
1.3、JCL原则
JCL使用原则: 如果有log4j,优先使用log4j 如果没有任何第三方日志框架的时候,我们使用的就是JUL
1.4 、案例
1.4.1 JCL默认会使用JUL的api案例
pom文件
<!--jcl依赖-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
测试案例
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
public class JCLTest {
@Test
public void test01(){
Log log = LogFactory.getLog(JCLTest.class);
log.info("info信息");
}
}
1.4.2 jcl+log4j 和源码分析
pom文件
<!--jcl依赖-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--
导入log4j会让jcl使用log4j的api而不使用jul的api
-->
<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>
resources创建log4j.properties
log4j.rootLogger=trace,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%
测试代码和源码分析
package com.bjpowernode.jcl.test;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
public class JCLTest {
@Test
public void test02(){
Log log = LogFactory.getLog(JCLTest.class);
log.info("info信息");
}
}
七、SLF4J (日志门面)
SLF4J官方文档
简单日志门面 (Simple Logging Facade For Java) SLF4J。 SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等
日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。 所以我们可以得出SLF4J最重要的两个功能就是对于日志框架的绑定以及日志框架的桥接。
1.1、门面模式(外观模式)
GoF23种设计模式其中之一
- 门面模式(Facade Pattern),也称之为外观模式,其核心为:外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。
- 外观模式主要是体现了Java中的一种好的封装性。更简单的说,就是对外提供的接口要尽可能的简单。
1.2、日志门面说明
- 前面介绍的几种日志框架,每一种日志框架都有自己单独的API,要使用对应的框架就要使用其对应的API,这就大大的增加应用程序代码对于日志框架的耦合性。
- 为了解决这个问题,就是在日志框架和应用程序之间架设一个沟通的桥梁,对于应用程序来说,无论底层的日志框架如何变,都不需要有任何感知。只要门面服务做的足够好,随意换另外一个日志框架,应用程序不需要修改任意一行代码,就可以直接上线。
1.3、官网图示剖析说明
在没有绑定任何日志实现的基础之上,日志是不能够绑定实现任何功能的
注明:每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;
1.3.1、对官网图分成3种情况
第一种:没有日志实现(没人用)
第二种:不需要桥接器。无缝衔接
都是slf4j门面时间线后面提供的日志实现,所以API完全遵循slf4j进行的设计 那么我们只需要导入想要使用的日志实现依赖,即可与slf4j无缝衔接 nop虽然也划分到实现中了,但是他不实现日志记录
第三种:需要桥接器
都是slf4j门面时间线前面的日志实现,所以API不遵循slf4j进行设计 通过适配桥接的技术,完成的与日志门面的衔接
1.4、如何让系统中所有的日志都统一到slf4j =>官网图示
SLF4J官方说明 如何让系统中所有的日志都统一到slf4j
- 将系统中其他日志框架先排除出去
- 用中间包来替换原有的日志框架
- 我们导入slf4j其他的实现
1.5、 SLF4J集成案例
1.5.1、集成slf4j-simple和logback
集成俩个日志实现的测试 slf4j-simple 和 logback 通常是实现一个
结论: 如果有多个日志实现的话,默认使用先导入的实现
发现: 切换slf4j-simple和logback但是导入的Logger包是没变了。 底层实现随便变,用的还是门面的包。这就是面向统一抽象层
pom文件:
<!--slf4j 核心依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--slf4j 自带的简单日志实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
<!-- logback依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
测试代码
package com.sqy;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4JTest {
@Test
public void test01() {
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
logger.info("pom文件Simple在前logback在后");
}
}
效果: 切换pom文件 logback在前simple在后=>效果:
1.5.2、只集成logback
pom文件:
<!--slf4j 核心依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- logback依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
测试代码
package com.sqy;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4JTest {
@Test
public void test02() {
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
logger.info("pom文件Simple在前logback在后");
}
}
效果:
1.5.3、集成nop测试
结果:nop是没有做成日志实现的
pom文件
<!--slf4j 核心依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- 导入nop -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
</dependency>
测试代码
package com.sqy;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4JTest {
@Test
public void test03() {
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
logger.info("pom文件Simple在前logback在后");
}
}
效果:
1.5.4、集成log4j(需要狸猫换太子一下)
pom文件:
<!--slf4j 核心依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- 导入log4j适配器依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!--导入log4j依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j.properties
# 自定义格式
log4j.rootLogger=trace,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%-10p] %m%n
测试代码:
package com.sqy;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4JTest {
@Test
public void test04() {
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
logger.info("集成log4j");
}
}
效果:
1.6、总结
不管底层的日志实现怎么切换。导入的logger还是sfl4j。这就是门面日志的效果
八、目前最强的Log4j2
Apache Log4j 2是对Log4j的升级,它比其前身Log4j 1.x提供了重大改进,并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些问题。 被誉为是目前最优秀的Java日志框架。
- 目前市面上最主流的日志门面就是SLF4J,虽然Log4j2 也是日志门面,因为它的日志实现功能非常强大,性能优越。所以我们一般情况下还是将 Log4j2 看作是日志的实现
- SLF4j + Log4j2 的组合,是最强大的日志功能实现方式,是主流趋势。
异步日志是log4j2最大的特色,其性能的提升主要也是从异步日志中受益。
1.1、Log4j2特征
- 性能提升
Log4j2包含基于LMAX Disruptor库的下一代异步记录器。在多线程场景中,异步记录器的吞吐量比Log4j 1.x 和 Logback 高 18倍,延迟低
- 自动重新加载配置
01、与Logback一样,Log4j2可以在修改时自动重新加载其配置 02、与Logback不同,它会在重新配置发生时不会丢失日志事件
- 高级过滤
01、与Logback一样,Log4j2支持基于Log事件中的上下文数据,标记,正则表达式和其他组件进行过滤。 02、此外,过滤器还可以与记录器关联。 03、与Logback不同,Log4j2可以在任何这些情况下使用通用的Filter类。
- 插件架构
01、Log4j使用插件模式配置组件。因此,无需编写代码来创建和配置Appender,Layout,Pattern Converter等。 02、在配置了的情况下,Log4j自动识别插件并使用它们。
- 无垃圾机制
01、在稳态日志记录期间,Log4j2 在独立应用程序中是无垃圾的,在Web应用程序中是低垃圾。 02、这减少了垃圾收集器的压力,并且可以提供更好的响应性能。
1.2、异步日志
提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应的是Appender组件和Logger组件
1.2.1、AsyncAppender方式(用的少)
- 是通过引用别的Appender来实现的,当有日志事件到达时,会开启另外一个线程来处理它们。
- 需要注意的是,如果在Appender的时候出现异常,对应用来说是无法感知的。
- AsyncAppender应该在它引用的Appender之后配置,默认使用 java.util.concurrent.ArrayBlockingQueue实现而不需要其它外部的类库。
- 当使用此Appender的时候,在多线程的环境下需要注意,阻塞队列容易受到锁争用的影响,这可能会对性能产生影响。
- 这时候,我们应该考虑使用无锁的异步记录器(AsyncLogger)。
1.2.2、AsyncLogger方式(主流)
-
AsyncLogger才是log4j2实现异步最重要的功能体现,也是官方推荐的异步方式 。 -
它可以使得调用Logger.log返回的更快。你可以有两种选择:全局异步和混合异步。 -
全局异步: 所有的日志都异步的记录,在配置文件上不用做任何改动,只需要在jvm启动的时候增加一个参数即可实现。 -
混合异步: 你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。 虽然Log4j2提供以一套异常处理机制,可以覆盖大部分的状态,但是还是会有一小部分的特殊情况是无法完全处理的,比如我们如果是记录审计日志(特殊情况之一),那么官方就推荐使用同步日志的方式,而对于其他的一些仅仅是记录一个程序日志的地方,使用异步日志将大幅提升性能,减少对应用本身的影响
-
混合异步的方式需要通过修改配置文件来实现,使用AsyncLogger标记配置
混合异步用得比较多。混合异步虽然没有全局异步异步方便但是胜在灵活,想哪一块同步就同步,哪一个异步就异步
1.2.3、性能比对
1.3、测试案例 异步日志(maven工程)
1.3.0、slf4j+log4j2整合需要的依赖
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- log4j适配器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<!-- log4j2日志门面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
<!-- log4j2日志实现 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<!-- 异步日志依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.7</version>
</dependency>
1.3.1、slf4j+log4j2整合流程
- 导入slf4j的日志门面
- 导入log4j2的适配器
- 导入log4j2的日志门面
- 导入log4j2的日志实现
执行原理:
slf4j门面调用的是log4j2的门面,再由log4j2的门面调用log4j2的实现
1.3.2、AsyncAppender测试
流程:
- 添加异步日志依赖
- 在Appenders标签中,对于异步进行配置
- 使用Async标签
- rootlogger引用Async
pom文件:引入依赖
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- log4j适配器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<!-- log4j2日志门面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
<!-- log4j2日志实现 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<!-- 异步日志依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.7</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
创建 log4j2.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<!--01、配置全局通用属性-->
<properties>
<property name="logDir">D://log4j2_logs</property>
</properties>
<!-- 配置appender -->
<Appenders>
<!-- 第一种 配置控制台输出的Appender -->
<Console name="consoleAppender" target="SYSTEM_OUT">
</Console>
<!--第二种 配置文件输出位置和文件名称-->
<File name="fileAppender" fileName="${logDir}//log4j2.log">
<!-- 配置文件输出格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
</File>
<!--
第三种 按照指定规则来拆分日志文件
fileName:日志文件的名字
filePattern:日志文件拆分后文件的命名规则
$${date:yyyy-MM-dd}:根据日期当天,创建一个文件夹
例如:2021-01-01这个文件夹中,记录当天的所有日志信息(拆分出来的日志放在这个文件夹中)
2021-01-02这个文件夹中,记录当天的所有日志信息(拆分出来的日志放在这个文件夹中)
rollog-%d{yyyy-MM-dd-HH-mm}-%i.log
为文件命名的规则:%i表示序号,从0开始,目的是为了让每一份文件名字不会重复
-->
<RollingFile name="rollingFile" fileName="${logDir}/rollog.log"
filePattern="${logDir}/$${date:yyyy-MM-dd}/rollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
<!-- 日志消息格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
<Policies>
<!-- 在系统启动时,触发拆分规则,产生一个日志文件 -->
<OnStartupTriggeringPolicy/>
<!-- 按照文件的大小进行拆分 -->
<SizeBasedTriggeringPolicy size="10KB"/>
<!-- 按照时间节点进行拆分 拆分规则就是filePattern-->
<TimeBasedTriggeringPolicy/>
</Policies>
<!-- 在同一目录下,文件的个数限制,如果超出了设置的数值,则根据时间进行覆盖,新的覆盖旧的规则-->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!-- 03、配置异步日志 -->
<Async name="myAsync">
<!-- 将控制台输出做异步的操作 -->
<AppenderRef ref="consoleAppender"/>
</Async>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger 配置日志级别 -->
<Root level="trace">
<!-- 引用Appender -->
<!--<AppenderRef ref="consoleAppender"/>--><!--控制台输出-->
<!--<AppenderRef ref="fileAppender"/>--><!--持久化日志,普通文件不拆分-->
<!--<AppenderRef ref="rollingFile"/>--><!--持久化日志,拆分-->
<AppenderRef ref="myAsync"/><!--引入异步的配置-->
</Root>
</Loggers>
</Configuration>
测试代码:
package com.sqy;
import com.bjpowernode01.Log4j2Test01;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4j2Test {
@Test
public void test01() {
Logger logger = LoggerFactory.getLogger(Log4j2Test.class);
for (int i = 0; i < 100; i++) {
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
for (int i = 0; i < 50; i++) {
System.out.println("系统业务逻辑==>测试异步");
}
}
}
效果:
1.3.3、AsyncLogger全局异步测试
异步日志实现(单独分配线程做日志的记录)
- 所有的日志都是异步的日志记录,在配置文件上稍微x
- 只需要在类路径resources下添加一个properties属性文件,做一步配置即可
- 文件名要求是:log4j2.component.properties
- 内容: Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
log4j2.xml 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<!--01、配置全局通用属性-->
<properties>
<property name="logDir">D://log4j2_logs</property>
</properties>
<!-- 配置appender -->
<Appenders>
<!-- 第一种 配置控制台输出的Appender -->
<Console name="consoleAppender" target="SYSTEM_OUT">
</Console>
<!--第二种 配置文件输出位置和文件名称-->
<File name="fileAppender" fileName="${logDir}//log4j2.log">
<!-- 配置文件输出格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
</File>
<!--
第三种 按照指定规则来拆分日志文件
fileName:日志文件的名字
filePattern:日志文件拆分后文件的命名规则
$${date:yyyy-MM-dd}:根据日期当天,创建一个文件夹
例如:2021-01-01这个文件夹中,记录当天的所有日志信息(拆分出来的日志放在这个文件夹中)
2021-01-02这个文件夹中,记录当天的所有日志信息(拆分出来的日志放在这个文件夹中)
rollog-%d{yyyy-MM-dd-HH-mm}-%i.log
为文件命名的规则:%i表示序号,从0开始,目的是为了让每一份文件名字不会重复
-->
<RollingFile name="rollingFile" fileName="${logDir}/rollog.log"
filePattern="${logDir}/$${date:yyyy-MM-dd}/rollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
<!-- 日志消息格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
<Policies>
<!-- 在系统启动时,触发拆分规则,产生一个日志文件 -->
<OnStartupTriggeringPolicy/>
<!-- 按照文件的大小进行拆分 -->
<SizeBasedTriggeringPolicy size="10KB"/>
<!-- 按照时间节点进行拆分 拆分规则就是filePattern-->
<TimeBasedTriggeringPolicy/>
</Policies>
<!-- 在同一目录下,文件的个数限制,如果超出了设置的数值,则根据时间进行覆盖,新的覆盖旧的规则-->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger 配置日志级别 -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="consoleAppender"/><!--控制台输出-->
<!--<AppenderRef ref="fileAppender"/>--><!--持久化日志,普通文件不拆分-->
<!--<AppenderRef ref="rollingFile"/>--><!--持久化日志,拆分-->
</Root>
</Loggers>
</Configuration>
log4j2.component.properties文件配置:
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
测试代码
package com.sqy;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4j2Test {
@Test
public void test02(){
Logger logger = LoggerFactory.getLogger(Log4j2Test.class);
for (int i = 0; i < 100; i++) {
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
for (int i = 0; i < 50; i++) {
System.out.println("系统业务逻辑==>测试异步");
}
}
}
效果:
1.3.4、AsyncLogger混合异步测试
不需要log4j2.component.properties配置文件
log4j2.xml 配置文件:自定义logger指定在com.sqy包下日志为异步。其余同步
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<!--01、配置全局通用属性-->
<properties>
<property name="logDir">D://log4j2_logs</property>
</properties>
<!-- 配置appender -->
<Appenders>
<!-- 第一种 配置控制台输出的Appender -->
<Console name="consoleAppender" target="SYSTEM_OUT">
</Console>
<!--第二种 配置文件输出位置和文件名称-->
<File name="fileAppender" fileName="${logDir}//log4j2.log">
<!-- 配置文件输出格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
</File>
<!--
第三种 按照指定规则来拆分日志文件
fileName:日志文件的名字
filePattern:日志文件拆分后文件的命名规则
$${date:yyyy-MM-dd}:根据日期当天,创建一个文件夹
例如:2021-01-01这个文件夹中,记录当天的所有日志信息(拆分出来的日志放在这个文件夹中)
2021-01-02这个文件夹中,记录当天的所有日志信息(拆分出来的日志放在这个文件夹中)
rollog-%d{yyyy-MM-dd-HH-mm}-%i.log
为文件命名的规则:%i表示序号,从0开始,目的是为了让每一份文件名字不会重复
-->
<RollingFile name="rollingFile" fileName="${logDir}/rollog.log"
filePattern="${logDir}/$${date:yyyy-MM-dd}/rollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
<!-- 日志消息格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
<Policies>
<!-- 在系统启动时,触发拆分规则,产生一个日志文件 -->
<OnStartupTriggeringPolicy/>
<!-- 按照文件的大小进行拆分 -->
<SizeBasedTriggeringPolicy size="10KB"/>
<!-- 按照时间节点进行拆分 拆分规则就是filePattern-->
<TimeBasedTriggeringPolicy/>
</Policies>
<!-- 在同一目录下,文件的个数限制,如果超出了设置的数值,则根据时间进行覆盖,新的覆盖旧的规则-->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!--
《混合异步》 重点 name="com.sqy"
01、includeLocation="false"
表示去除日志记录中的行号信息,这个行号信息非常的影响日志记录的效率(生产中都不加这个行号)
严重的时候可能记录的比同步的日志效率还有低
02、additivity="false" 表示不继承rootlogger
-->
<!-- 自定义logger,让自定义的logger为异步logger 只有在com.sqy类的才会异步。其他包的日志是同步-->
<AsyncLogger name="com.sqy" level="trace" includeLocation="false" additivity="false">
<!-- 将控制台输出consoleAppender,设置为异步打印 -->
<AppenderRef ref="consoleAppender"/>
</AsyncLogger>
<!-- 配置rootlogger 配置日志级别 -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="consoleAppender"/><!--控制台输出-->
<!--<AppenderRef ref="fileAppender"/>--><!--持久化日志,普通文件不拆分-->
<!--<AppenderRef ref="rollingFile"/>--><!--持久化日志,拆分-->
</Root>
</Loggers>
</Configuration>
测试代码01:包package com.sqy 效果异步日志
package com.sqy;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4j2Test {
@Test
public void test03(){
Logger logger = LoggerFactory.getLogger(Log4j2Test.class);
for (int i = 0; i < 100; i++) {
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
for (int i = 0; i < 50; i++) {
System.out.println("系统业务逻辑==>测试异步");
}
}
}
测试代码02:包package com.app 效果同步日志
package com.app;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4j2Test_NoAsync {
@Test
public void test01(){
Logger logger = LoggerFactory.getLogger(Log4j2Test_NoAsync.class);
for (int i = 0; i < 100; i++) {
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
for (int i = 0; i < 50; i++) {
System.out.println("系统业务逻辑==>测试异步");
}
}
}
混合异步效果(异步):
com.app包下 Log4j2Test_NoAsync.java 测试效果(同步)
1.4、异步日志 注意事项(影响性能)
- AsyncAppender、AsyncLogger不要同时出现,效果也不会叠加
- 如果同时出现,那么效率会以AsyncAppender为主 (AsyncAppender这个没AsyncLogger好)
- AsyncLogger的全局异步和混合异步也不要同时出现,没有这个需求,效果也不会叠加
九、SpringBoot日志
最基本的依赖(看maven树)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
SpringBoot使用它来做日志功能
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
1.1、SpringBoot默认使用slf4j+logbook
项目启动会先走spring-core的jcl但是后面会被boot给覆盖掉所以是springboot2还是使用slf4j+logbook
debug测试附上图 logback
加载的是slf4j
1.2、底层依赖关系
说明: 1、springboot2.0 starter-logging移除了jcl。转换器直接写到了jcl原本包里面 2、springboot2 的spring-core里面的jcl里面做了适配操作(详情看源码)这个配置
说明:项目启动会先走spring-core的jcl但是后面会被boot给覆盖掉所以是springboot2还是使用slf4j+logbook
1.2、springboot使用logback配置文件
因为springboot默认就是slf4j+logback 所以导入logback.xml是对日志更细粒度的掌控
指定配置
Logging System | Customization |
---|
Logback | logback-spring.xml , logback-spring.groovy , logback.xml or logback.groovy | Log4j2: | log4j2-spring.xml or log4j2.xml | JDK (Java Util Logging) | logging.properties |
logback推荐使用的配置文件是logback-spring.xml
- logback.xml:直接就被日志框架识别了
- logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能
<springProfile name="dev">
<!-- configuration to be enabled when the "dev" profile is active -->
可以指定某段配置只在某个环境下生效 在dev环境生效
</springProfile>
进行引入logback-spring.xml就可以了
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定义日志的根目录 -->
<property name="LOG_HOME" value="/app/log" />
<!-- 定义日志文件名称 -->
<property name="appName" value="atguigu-springboot"></property>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
</springProfile>
<springProfile name="!dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
</springProfile>
</layout>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名称 -->
<file>${LOG_HOME}/${appName}.log</file>
<!--
当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
%i:当文件大小超过maxFileSize时,按照i进行文件滚动
-->
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
那些为了归档而创建的目录也会被删除。
-->
<MaxHistory>365</MaxHistory>
<!--
当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>
<!--
logger主要用于存放日志对象,也可以定义日志类型、级别
name:表示匹配的logger类型前缀,也就是包的前半部分
level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
false:表示只用当前logger的appender-ref,true:
表示当前logger的appender-ref和rootLogger的appender-ref都有效
-->
<!-- logger -->
<logger name="com.sqy" level="debug" />
<!-- Spring framework logger -->
<logger name="org.springframework" level="debug" additivity="false"></logger>
<!--
root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
-->
<root level="info">
<appender-ref ref="stdout" />
<appender-ref ref="appLogAppender" />
</root>
</configuration>
1.3、springboot2切换为log4j2
1. 先排除依赖,导入log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除掉原始依赖 以此去除logback引用 -->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
2. 使用log4j2的配置文件(简化版本详细看log4j2那边)
这样就转换成功了。springboot日志就变成了slf4j(门面)+ log4j2(实现)
总结
这就是Java的日志框架拉。log4j2+sfl4j是主流
|