👨🎓作者:Java学术趴
💌公众号:Java学术趴
🚫特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系小编授权。
🙏版权声明:文章里的部分文字或者图片来自于互联网以及百度百科,如有侵权请尽快联系小编。微信搜索公众号Java学术趴联系小编。
☠️每日毒鸡汤:生活是世上最罕见的事情,大多数人只是存在,仅此而已。
👋大家好!我是你们的老朋友Java学术趴,今天继续给大家分享小趴Java日志框架。对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离不日志。java领域存在多种日志框架,目前常用的日志框架包括Log4j 1,Log4j 2,Commons Logging,Slf4j,Logback,Jul。
Java日志框架
1. Java日志介绍
-
Java日志框架内中的xml配置文件中的标签是固定的,不可以自定义
-
配置文件基本结构:
以标签开头,
包含0或多个子标签,
包含0或多个标签,
最多只能有一个标签。
以下是SLF4J门面技术。
这个是SLF4J门面技术这个深绿色的代表网络适配器。
- 日志文件是用于记录系统操作事件的文件集合。
- 日志文件他具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。
- 日志主要分为两种:调试日志与系统日志(工作中大部分使用)
// 使用反射机制的这个类只要是该项目的 src下的类都可以。
Logger logger = LogManager.getLogger(入门案例.class);
1.1 调试日志
- 我们平时使用的debug功能只能暂时查看运行信息,而不能长期的保存这些运行信息。而调试日志可以更加方便的“重新”这些问题,即可以保存这些运行信息。
1.2 系统日志
- 系统日志是用来记录系统中的硬件、软件和系统相关问题的信息。同时还可以监视系统中发生的事件。用户可以通过它来检查错误发生的原因,或者寻找收到攻击留下来的痕迹。
- 系统日志包括系统日志、应用日志和安全日志几种。
2.日志框架
2.1 日志框架的作用
- 控制日志输出的内容和格式。
- 控制日志输出的位置。
- 日志文件相关的优化,如异步操作、归档、压缩。
- 日志系统的维护。
- 面向接口开发——日志的门面。
2.2 日志框架的价值
- 我们可以直接使用别人写好的日志框架,提高开发效率。
2.3 市面流行的日志框架
- JUL:java util logging Java原生日志框架。
- Log4j:Apache的一个开源项目。
- Logbcak :由Log4j之父做的另一个开源项目,业界中称为 log4j 后浪。他是一个可靠、通用且灵活的Java日志框架。
- Log4j2 :Log4j的第二个版本,各个方面与Logback及其相似。具有插件式结构、配置文件优化等特征,在Spring Boot1.4版本之后就不在支持 log4j ,所以出现了第二个版本的。
- JCL
- SLF4j
2.4 日志门面和日志框架的区别
-
日志框架技术 :JUL、Log4j、Logbcak、Log4j2
-
日志门面技术 :JCL、SLF4j
为什么要使用日志门面技术:
- 每一种日志框架都有自己单独的API,要使用对应的框架就要使用对应的API,这就大大的增加了应用程序代码对于日志框架的耦合性。使用日志门面技术之后,不论底层是什么日志框架,我们拿到代码之后可以使用自己习惯的日志框架就行解读,不用修改一行代码。
- 其实框架1调用的是自己的方法a() ,框架2调用的自己的方法b() ,此时将这两个方法抽取出来称为方法c();
3. JUL
3.1 JUL 简介
- JUL全程 Java Util Logging,它是java原生的日志框架,使用时不需要另外引入第三方的类库,相对于其他的框架使用方便,学习简单,主要使用在小型应用中。
3.2 JUL 组件介绍
3.3 日志的级别(Level)
- 注意:其中包含这个800,也就是info
- info是默认的打印信息级别。
Logger logger = Logger.getLogger("com.yunbocheng.JUL.JULTest.test01");
logger.severe("severe信息");
logger.warning("warining信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
此时打印的结果是 :只有info级别以及比info级别高的日志信息
3.4 入门案例
- 见项目 “入门以及默认级别展示” 。
3.5 自定义日志级别
- 见项目 “自定义日志级别 ”
总结 :
- 用户使用Logger来进行日志的记录,Logger可以同时持有多个处理器Handler。(同时在控制台和自定义位置进行日志信息的输出)
- 日志的记录使用的是Logger,日志的输出使用的是Handler。
- 添加了哪些handler对象,就相当于需要根据所添加的handler将日志信息输出到指定的位置上,例如:控制台、指定位置文件…
3.6 JUL Logger中的父子关系。
- 见项目”JUL中的父子关系“
3.7 JUL配置文件解析
- 见项目”JUL配置文件“
以上所有的配置相关的操作,都是一Java硬编码的形式进行的。
我们可以使用更加清晰,更加专业的一种做法,就是配置文件。
如果我们没有自己添加这个配置文件,则会使用系统默认的配置文件。
这个配置文件:
java.home --> 找到jre文件夹 --> lib --> logging.properties
配置文件中的#代表的注释,可以删除掉。
3.8 自定义配置文件
-
我们将配置文件修改为自定义的输出级别
-
如果想要获取到这个自定义的配置文件,此时一定需要一个输入流来读取这个文件。项目见”JUL配置文件“
3.9 日志信息的追加
- 只需要在日志配置文件中加入 :java.util.logging.FileHandler.append = true
3.10 JUL日志框架使用方式总结(原理解析)
4. Log4j
4.1 Log4j简介
-
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、CUI组件,甚至可以是套接口服务器、NT的事件记录器。只要是我们需要的地方,一般都可以输出日志信息。
-
我们可以控制每一条日志信息输出的格式。
-
通过定义每一条日志信息的级别,我们能够更加细致的控制日志的生成过程。
-
这些设置可以通过一个配置文件来灵活的进行配置,而不需要修改应用的代码。
-
Log4j --> Log for java
-
我们使用log4j技术,主要使用的是其配置文件,我们也可以使用硬代码的格式在Java中来写这个日志配置信息。
-
从时间上讲,log4j的产生时间要比JUL早。
4.2 Log4j组件
4.3 日志输出格式说明
4.4 Log4j的级别
- 其中 debug 是我们在没有进行设置的情况下,默认的日志输出级别。
4.5 配置文件
- 将项目中的项目”配置文件“
4.6 使用默认配置文件打印日志
-
代码在项目“配置文件”中
-
注意:这个日志配置文件必须在main–>rescoures文件下,且名字必须是log4j.properties文件。此时log4j日志文件会自动加载这个配置文件。
-
注意:在properties配置文件中,每一条命令后不要加分号,否则会报错。
# 以下是配置文件的代码信息
# 这行代码代表让这个日志执行指定的配置信息
# 这个trace代表的是输出级别,这个console是我们自定义的一个名称(见名思意)appenderName
# 这个可以设置打印到多个地方,中间用逗号隔开。
# 这个Logger是继承的根节点rootLogger。
log4j.rootLogger = trace,console
#配置appender输出方法
log4j.appender.console = org.apache.log4j.ConsoleAppender
#配置输出信息的格式
log4j.appender.console.layout = org.apache.log4j.SimpleLayout
- 以上使用的是默认的日志信息打印格式。(SimpleLayout)
// 以下是输出日志信息的代码
public void test01(){
Logger logger = Logger.getLogger(配置文件.class);
// 打印输出信息
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
4.7 自定义配置打印日志信息格式
- 代码在项目“配置文件”中
#这行代码的代表打印到控制台
log4j.rootLogger = trace,console
#配置appender输出位置
log4j.appender.console = org.apache.log4j.ConsoleAppender
#配置输出信息的格式
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#这行是设置自定义的日志信息打印格式
log4j.appender.console.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS}
- 这个是使用的自定义日志信息打印格式 PatternLayout,这个时候只需要在默认的配置文件中加入一行指定打印日志信息格式的代码即可。
4.8 将日志输出到指定文件中的配置
- 源代码见项目“日志信息输出到文件”
#这行代码的代表打印到控制台
log4j.rootLogger = trace,file
#配置appender输出位置
log4j.appender.file = org.apache.log4j.ConsoleAppender
#配置输出信息的格式
log4j.appender.console.file = org.apache.log4j.PatternLayout
#这行是设置自定义的日志信息打印格式
log4j.appender.file.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS} %m%n
#第一个file是我们自己命名的appenderName,第二个file是用来指定文件的位置。
log4j.appender.file.file = E://log4j.log
#设置输出日志的编码格式(输出中文的日志信息)
log4j.appender.file.encoding = UTF-8
- 此时这个日志信息会输出到这个指定位置的文件中。
4.9 将日志信息输出到多个位置
-
源代码见项目“日志信息输出到文件”
-
同时输入到控制台和指定文件中
# 需要将以上输出到控制台和文件的代码都要写上
# 最主要的是修改打印到的位置代码,这是代表可以在 appenderName 为这个两个的地方输出
# 这个 file,console是我们自定义的名称,(见名思意原则就是代表控制台和文件)
log4j.rootLogger = trace,file,console
4.10 根据文件大小拆分配置文件(RollingFileAppender)
- 源代码见项目“日志信息输出到文件”
log4j.rootLogger = trace,rollingFile,console
# RollingFileAppender的配置,我们可以针对实际含义起名
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS} %m%n
log4j.appender.rollingFile.file = E://log4j.log
log4j.appender.rollingFile.encoding = UTF-8
# 指定日志文件的大小
log4j.appender.rollingFiler.maxFileSize = 1MB
# 指定日志文件的数量
log4j.appender.rollingFiler.maxBackupIndex = 5
- 这个时候日志6会覆盖掉日志1。以下就是生成的5个文件。这5个文件会以序号进行排列。
4.11 根据时间来拆分日志文件(DailyRollingFileAppender)
- 源代码见项目“日志信息输出到文件”
- 这个会根据你输入的时间间隔来生成新的日志信息,这个时间可以是一天,也可以是一秒,需要注意的是这个并不是自动为我们生成新的日志文件,是我们手动生成的日志文件,比如:你设置的间隔是 yyyy-MM-dd ,这个时候如果你现在输出了一个日志文件,那么在这个时间开始的后24个小时内都不会生成新的日志文件,在这24小时内输出的日志文件都会存储到这个一个旧的日志文件中。即使过了24个小时,系统也不会为我们自动生成一个新的日志文件,需要程序员自己生成一个新的日志文件,加可以精确到秒,那么一秒就会为我们生成一个新的日志文件。
# DailyRollingFileAppender的配置,我们可以针对实际含义起名
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS} %m%n
log4j.appender.dailyRollingFile.file = E://log4j.log
log4j.appender.dailyRollingFile.encoding = UTF-8
#不要忘记前边的那个点
appender.dailyRollingFile.datePattern = '.'yyyy-MM-dd HH:mm:ss
- 在这个类中没有提供覆盖的方法。
4.12 日志持久化(将日志信息存储到数据库)
-
项目源码见 “日志持久化_将数据存储到数据库”
-
第一步 :需要在Maven中添加mysql依赖。
-
第二步 :在配置文件中配置连接数据库的信息并且设置插入语句。
注意:这个插入语句必须在一行上。
# 持久化日志信息 将日志信息存储到数据库
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/log
log4j.appender.logDB.User = root
log4j.appender.logDB.Password = 567cybtfboys
# 此时要像数据库中插入数据,使用insert语句
log4j.appender.logDB.Sql = INSERT INTO tab_log(id,name,create,level,category,fileName,message) values('project_log','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%m')
- 第三步 :在主方法中输出日志信息
Logger logger = Logger.getLogger(日志持久化_将数据存储到数据库.class);
// 输出日志信息
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
4.13 自定义Logger的配置
-
见项目 “自定义Logger配置”
-
第一步 :配置自定义logger
# 使用最高父类rootLogger配置logger,这个时候继承的是父logger(根节点)
log4j.rootLogger = trace,console
# 配置自定义logger,此时使用的是自定义的父logger (自定义)
log4j.logger.com.yunbocheng = info,file
- 输出结果
从输出的位置来看,控制台输出了信息,日志文件也会输出信息。所以可以得出结论,如果根节点的logger和自定义父logger配置的输出位置是不同的则取二者的并集,也就是配置的位置都会进行日志的输出。
如果二者配置的日志级别不同,主要以按照我们自定义的父logger的级别输出为主。
5.JCL (日志门面技术)
5.1 JCL简介
- JCL是Apache提供的一个通用日志API
-
LoggerFactory // 这个叫日志工厂
5.2 入门案例以及JCL是如选择日志框架的
- 详细信息见 “入门案例”
6. SLF4J (日志门面技术)
- 只要使用到日志框架,百分之九九使用的都是SLF4J门面技术。
6.1 门面模式(外观模式)
- 他属于GoF23种设计模式的一种。
- 门面技术,核心是:外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更容易使用。
6.2 日志门面
- 外观模式主要提现Java种得一种好得封装性。更简单说,就是对外提供得接口要尽量简单。
- 这就相当于是一个门面技术,我们吃什么不需要和每一位厨师打交道,只需要和前台打交道即可。这个前台就相当于是一个API接口。
6.3 常见得日志框架及日志门面
6.4 SLF4J简介
6.5 SLF4J桥接技术
- 为什么使用桥接技术
- 因为在SLF4J出现之前已经出现了一些日志框架(比如:log4j、JUL、JCL等) 这些框架没有继承这个SLF4J的日志API接口。这个时候就需要使用这个桥接技术。在SLF4J之后出现的日志框架都实现了这个SLF4J接口,就不用桥接技术实现。
6.6 入门案例
- 见项目”入门案列“
6.7 SLF4J动态打印信息的实现
- 见项目“入门案列”
6.8 打印异常信息
- 见项目 “打印异常信息”
6.9 SLf4J集成日志框架(logback)
- SLf4J集成logback日志框架
- 注意:如果在pom.xml文件种存在多个日志框架,默认使用先导入的日志框架实现(也就是谁在最上边就先打印谁)
- 不论谁在上下,只要存在多个日志框架,那么都会报错( Class path contains multiple SLF4J bindings )
- 在实际应用的时候,我们一般情况下,仅仅只是做一种日志实现的集成就可以了
6.10 SLF4J集成log4j
-
源码见项目 ”SLF4J集成log4j的方法“
-
这个是slf4j以前出现的日志框架,此时需要绑定一个适配器 slf4j-log4j12
-
在pom.xml文件中修饰依赖配置
<!--SLF4J核心配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</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日志框架的配置文件(这个配置文件在main–>resources下)并且这个配置文件需要声明为 log4j.properties
# 使用根节点的logger对象
log4j.rootLogger = trace,console
# 将日志信息输出到控制台
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS} %m%n
- 主函数:
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 注意此时使用的是 slf4j 中的类和接口来创建的logger对象,而不是log4j
Logger logger = LoggerFactory.getLogger(SLF4J集成log4j的方法.class);
// 打印输出信息
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
- 此时输出的结果就是log4j格式的输出结果
6.11 SLF4J集成log4j
-
源码见项目 ”SLF4J集成JUL的方法“
-
这个是slf4j以前出现的日志框架,此时需要绑定一个适配器 slf4j-jdk14
-
在pom.xml文件中修饰依赖配置
<!--SLF4J核心配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<!--JUL适配器依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>
<!--因为JUL是JDK内置的,所以不需要额外导入JUL实现的依赖-->
- 使用JUL也不需要配置文件,只有log4j需要配置文件
- 实现主类
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 此时的这个logger对象也是通过 slf4j 获取的,完全不需要JUL参与
Logger logger = LoggerFactory.getLogger(SLF4J集成log4j的方法.class);
// 打印输出信息
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
- 输出结果 (此时输出的就是JUL格式的日志信息)
- 从结果看是JUL日志打印的,默认是info级别的输出的。
6.12 同时集成多个日志会出现错误信息
- 一次集成多个日志框架会发生错误,也可以打印出日志信息,只不过打印的日志信息格式是pom.xml文件中最上边的那个日志框架。
6.13 日志重构需求
- 我们以前都是使用SLF4J来实现JUL与log4j,导入的都是slf4j的包。此时需要使用log4j包下的类和方法将日志重构为 slf4j + logback的组合。在不接触源码的情况下实现这个需求。
- 首先将所有关于其他日志实现和门面依赖全部去除。仅仅只留下log4j的依赖,测试的过程中,只是使用log4j相关的组件。
- 这个时候需要使用桥接器来实现这个需求。桥接器解决的是项目中日志的重构问题。当前系统中存在之前的日志API,可以通过桥接器转换到slf4j的实现。
-
以上除了绿色和蓝色的都是桥接器。
-
桥接器的使用步骤:
- 第一步 :删除之前旧的日志框架依赖(此时是使用log4j --> slf4j+logback)
<!--删除log4j依赖,以为此时需要使用log4j进行重写-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 添加 slf4j 提供的桥接组件 (此时我们需要添加log4j的桥接器)
<!--SLF4J核心配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<!--logback日志框架-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<!--slf4j提供的log4j桥接器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
- 打印日志信息函数
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.junit.Test;
//注意:此时使用了 log4j包下的类和方法实现了 logback日志格式的输出。
// 此时需要使用 slf4j提供的log4j-over-slf4j桥接器
Logger logger = LogManager.getLogger(日志重构.class);
logger.info("info信息");
- 输出结果 (一看是黑色的日志信息,一看就是logback打印的输出的日志信息)
-
证明了现在使用的确实是slf4j门面+logback实现。
-
在重构之后,就会为我们造成这样一种假象,我们明明使用的是log4j包下的日志组件资源,但是真正日志的实现,却是使用slf4j门面+logback实现,这就是桥接器给我们带来的效果。
注意:
- 桥接器如果配置在适配器的上方,则执行报错,不能同时出现。
- 桥接器如果配置在适配器的下方,则不会执行桥接器,没有任何意义。
- 在实际开发中,我们不要在pom.xml中同时给出适配器和桥接器。
7. Logback
7.1 Logback简介
7.2 Logback中的组件
7.3 Logback配置文件
Logback提供了3种配置文件
- logback.groovy
- logback-test.xml
- logback.xml
如果都不存在则采用默认的配置。
7.4 日志输出格式
注意:-10代表给案例设置10个字符,左对齐。+10代表给案例设置10个字符,右对其。
7.4 入门案例
-
项目见”入门案列“
-
trace(追踪信息) < debug(普通信息) < info(重要信息) < warn(警告信息) < error(错误信息) 其中debug为默认的打印级别。
- pom.xml项目依赖文件
<!--导入slf4j核心依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<!--导入logback框架依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
- 编写打印日志信息代码
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 注意:以上都是使用的 slf4j 门面技术来实现的这个logback的日志框架。完全不需要使用logback包中的类和方法。使用的是 slf4j 的门面技术。
// 这里我们还是使用slf4j的门面技术来实现logback
Logger logger = LoggerFactory.getLogger(入门案列.class);
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
- 此时没有修改打印日志信息的级别,所以此时打印的为默认级别(debug)。
7.5 使用Logback配置文件(logback.xml)来打印日志信息
- 配置文件中的信息以及实现方式见项目 “Logback配置文件”。
7.6 Logback将日志信息打印到指定文件
- 配置文件中的信息以及实现方式见项目 “Logback配置文件”。
7.7 Logback同时在多个位置打印日志信息(文件、控制台)
- 此时只需要修改配置文件中的信息即可
<!--这是第一种打印到多个位置的配置(此时打印到控制台和文件的日志级别都是info级别)-->
<root level="info">
<!--引入appender,日志记录器,使用name属性来获取指定的appender对象-->
<appender-ref ref="consoleAppender"/>
<appender-ref ref="fileAppender"/>
</root>
<!--这是第二种打印到多个位置的配置(此时虽然代码中写的控制台和文件的打印级别不同,但是此时打印出来的都是info级别,以最后一个级别为准)-->
<root level="ALL">
<!--引入appender,日志记录器,使用name属性来获取指定的appender对象-->
<appender-ref ref="fileAppender"/>
</root>
<root level="info">
<!--引入appender,日志记录器,使用name属性来获取指定的appender对象-->
<appender-ref ref="consoleAppender"/>
</root>
7.8 将日志信息打印html文件
- 此时需要修改配置文件信息,此时的配置文件信息比较特殊
<property name = "pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L %thread %m%n"></property>
<property name="logDir" value="E://test"></property>
<!-- 配置输出html文件 -->
<!-- 这个也属于输出到指定文件,还是使用FileAppender类 -->
<appender name="htmlFileAppender" class="ch.qos.logback.core.FileAppender">
<file>${logDir}/logback.html</file>
<!--设置输出格式-->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<prttern>${pattern}</prttern>
</layout>
</encoder>
</appender>
<root level="ALL">
<!--引入appender,日志记录器,使用name属性来获取指定的appender对象-->
<appender-ref ref="htmlFileAppender"/>
</root>
-
注意:我们只能设置打印的日志信息内容,不能设置这个网页的打印格式以及样式。
-
但是,当我们打印出logback.html文件后,我们可以人为的修改其中的样式以及格式
-
这个html中包含 HTML+CSS。
-
在实际的开发中,如果日志文件不是很大,我们可以考虑使用html进行日志打印,因为可读性强。
7.9 Logback日志拆分和归档压缩
- 其实我们在XML中使用的这些标签都是来自class类中的属性
<!-- 将文件拆分和归档主要使用以下的配置信息 -->
<!-- 配置文件的appender 可拆分归档的文件-->
<appender name="roll" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 输出格式(自定义格式)使用EL表达式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!-- 设置文件位置(使用EL表达式) -->
<file>${logDir}/roll_logback.log</file>
<!-- 指定拆分规则(这里使用文件大小和时间来拆分日志信息) -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按照时间和压缩格式来声明文件名-->
<!-- 下边的这一长串都是文件名 实际开发中,将日志设置到日即可 i是一个序号:用于区别日志文件-->
<!-- gz是一种压缩包格式,这个格式常在linux中使用 我们这里获取到的也是一个压缩文件-->
<fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
<!-- 按照文件大小进行拆分 -->
<maxFileSize>1KB</maxFileSize>
<!-- 注意:这里只有这个文件的大小超过1KB的时候才会拆分日志文件,这个时候拆分出来的就是一个以上格式的压缩包-->
</rollingPolicy>
</appender>
7.10 通过过滤器更细粒度的打印日志信息
- 修改其中的XML配置文件
<!-- 配置控制台appender 使用过滤器-->
<appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
<!-- 设置控制台输出信息形式 -->
<target>
System.err
</target>
<!-- 设置打印日志信息格式(自定义)-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!-- 配置过滤器 -->
<fileter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 设置日志的输出级别 -->
<level>WARN</level>
<!-- 高于level中设置的级别,则打印日志 -->
<onMatch>ACCEPT</onMatch>
<!-- 低于level中设置的级别,则屏蔽日志 -->
<onMisMatch>DENY</onMisMatch>
</fileter>
7.11 异步日志
- 以前一直使用的同步日志(一根线程)
为什么要使用异步日志
- 在实际的开发中,代码的执行顺序肯定是自上而下执行的,这个时候如果我们的日志信息非常的庞大,那么我们的系统信息需要等待日志信息全部打印完毕之后才可以打印系统信息,此时系统会处于一种停滞状态,对系统本身业务代码的执行效率非常的低。
异步日志的作用
- 在我们打印日志信息的同时不会影响系统代码的执行。
配置异步日志的方式
<!-- 配置异步日志 -->
<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="consoleAppender"/>
</appender>
<!--
日志记录器
配置root logger
level:配置日志级别
-->
<root level="ALL">
<appender-ref ref="asyncAppender"/>
</root>
异步日志的原理:
-
系统会为日志操作单独的分配出来一根线程,原来用来执行当前方法的主线程会继续向下执行。
-
线程1 : 系统业务代码执行 线程2 :打印日志 两根线程争夺CPU的使用权。
在实际的我们使用异步日志的方式来实现业务。
7.12 自定义logger
<!-- 以上使用的是root(根节点的logger),我们此时使用自定义的logger-->
<!-- 这里需要使用name属性来指定父类路径(这个路径必须是输出日志文件文件的父路径)-->
<logger name="com.yunbocheng" level="info" additivity="false">
<!-- 在自定义logger中配置appender -->
<appender-ref ref="consoleAppender"/>
</logger>
- 此时打印出的日志信息级别全是info级别和比info级别高的日志信息。
7.13 Logbcak的补充
- 关于异步日志的补充
- 不同配置文件之间的转换
8. Log4j2
8.1 Log4j2简介
- Apache Log4j2是对Log4j的升级,它提供了Logback中可用的许多改进,同时修复了Logback架构中的一些问题。被誉为是目前最优秀的java日志框架。
8.2 Log4j2特征
-
性能提升:在多线程场景中,异步记录器的吞吐量比Log4j 1.x 和 Logback高18倍,延迟低。
-
自定重新加载配置:与Logback一样,Log4j2可以在修改时自动重载加载配置。与Logback不同,它会在从重新配置发生时不会丢失日志事件。
-
高级过滤:与Logback一样,Log4j2支持基于 Log事件中的上下文数据,标记,正则表达式和其他组件进行过滤。此外,过滤器还可以与记录器关联。与Logback不同,Log4j2可以在任何这些情况下使用通用的Filter类。
-
插件架构:Log4j2 使用插件模式配置组件。因为,无需编写代码来创建和配置 Appender、Layout、Pattern Converyer 等。在配置了的情况下,Log4j2自动识别插件并使用他们。
-
无垃圾回收机制:在稳态日志记录期间,Log4j2 在独立应用程序中是无垃圾的,在web应用程序中式低垃圾。这减少了垃圾收集器的压力,并且可以提供更好的响应性能。
8.3 主流日志框架
- 目前市场上最主流的日志门面技术是 SLF4J,虽然 Log4j2 也是日志门面,因为它的日志实现功能非常强大,性能优越。所以我们一般情况下还是将Log4j2看做是日志的实现。
- SLF4J + Log4j2 的组合,是市场上最强大的日志功能实现方式,绝对是主流日志框架。
8.4 入门案列
- 这个案例不使用 SLF4J 来实现,仅用Log4j2来实现。
- 配置pom.xml文件 (这些依赖其实就是一个jar包)
<!-- 添加log4j2核心 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</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>
- 实现日志输出
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
// 以上没有使用 SLF4J门面技术,完全使用的log4j中的类和方法进行实习的。
public class 入门案例 {
@Test
public void test01(){
/*
* 这个案例中我们单纯的使用Log4j2 门面+实现
* 不使用 SLF4J 实现。
* Log4j2和log4j提供了相同的输出级别。
* Log4j2的默认输出级别是 error
* */
Logger logger = LogManager.getLogger(入门案例.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信心");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
}
- 运行结果
- 此时会观察到 ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration 这句话的含义是:没有找到 Log4j2的配置文件,使用默认的打印日志信息格式。
- 出现这句话就说明此时这个日志是使用 Log4j2框架实现的。
8.5 配置文件基础标签
-
见项目 “Log4j2基础标签”
-
对于Log4j2配置文件中的标签,要求首字母都大写。logback中的首字母是小写。
8.6 Log4j2与SLF4J联合使用(使用slf4j来实现log42日志)
- 见项目 “Log4j2与SLF4J联合使用”
- 这里不仅需要导入 slf4j 的日志门面技术还需要导入 log4j2的日志门面。
- 执行原理:slf4j门面调用的是log4j2的门面,在由log4j2的门面调用log4j2的实现。
- 需要在pom.xml文件中导入多个依赖
<!-- log4j2日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</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>
<!-- 导入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配置文件(此时将日志信息输出到控制台)
<!--注意:xml配置文件中的标签是固定的,不可以随意修改,否则会报错-->
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration>
<Appenders>
<!-- 控制台输出 这个name属性不可以改变 -->
<Console name="consoleAppender" target="SYSTEM_ERR">
</Console>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="consoleAppender"/>
</Root>
</Loggers>
</Configuration>
- 实现日志输出
// 注意:我们使用的都是 slf4j门面技术中的类与方法实现的日志信息的打印,没有适应到log4j2.
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 注意:此时使用的 slf4j 的门面技术,输出的是 slfj的日志级别(有5个级别)
Logger logger = LoggerFactory.getLogger(Log4j2与SLF4J联合使用.class);
// slf4j 中存在5种日志输出级别,此时使用是slf4j的记录器,而不是log4j2的,所以只能输出slf4j中的五种级别。
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
-
注意:虽然表面使用的是 slf4j,但是底层使用的是log4j2门面技术与log4j2的实现框架。
-
输出结果:
8.7 日志文件输出
- 将项目 “日志文件输出”。
- 配置 log4j2.xml 文件
<!--
配置全局通用属性
注意:在log4j2中的标签都是首字母大写,只有全局通用属性的标签首字母不大写。
-->
<properties>
<property name="logDir">D://test</property>
</properties>
<!-- 配置appender -->
<Appenders>
<!-- 配置文件输出 -->
<File name="fileAppender" fileName="${logDir}/log4j2.log">
<!-- 配置文件输出格式(这里设置输出格式的语法和logback中是一样的。) -->
<!-- -5表示左对齐,+5表示右对齐 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"/>
</File>
</Appenders>
<!--修改logger配置信息-->
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="fileAppender"/>
</Root>
</Loggers>
-
实现日志输出 (和上边一样,次数省略不写)
-
输出结果(输出的格式是我们自定义的格式)
8.8 日志文件拆分
- 这个很重要。详细的解释见项目 “log4j2日志拆分”。
8.9 log4j2 中的异步日志
-
日志默认是同步的,异步日志就是分配单独的线程做日志记录。
-
异步日志是 log4j2 中最大的特色。
- 可以将这两种实现日志的方式看做完全不一样。
8.9.1 AsyncAppender 方法(实际中用的少)
- AsyncAppender是通过引用别的 Appender 来实现的,当有日志事件到达时,会开启另外一个线程来处理他们。AsyncAppender默认使用Java中自带的类库(util类库),不需要导入外部类库。AsyncAppender 应该是在它引用 Appender 之后配置,因为如果在Appender的时候出现异常,对应用来说是无法感知的。当使用此 Appender 的时候,在多线程的环境下需要注意,阻塞队列容易受到锁争用的的影响,这可能会对性能产生影响。这个时候,我们需要使用无锁的异步记录器 (AsyncLogger)
8.9.2 AsyncLogger 方法(实际中用的多)
- AsyncLogger才是log4j2实现异步最重要的功能体现,也是官方推荐的异步方式。
- 它可以使调用Logger.log返回更快。其中包括全局异步和混合异步。
- 全局异步:所有的日志都异步的记录,在配置文件上下不用做任何的改动,只需要在jvm启动的时候增加一个参数即可实现,实际开发中使用较少。
- 混合异步:你可以同时在应用中使用同步日志和异步日志,这使得日志的配置方式更加的灵活。混合日志需要修改配置文件来实现,使用AsyncLogger标记配置,实际开发中使用较多。
8.10 log4j2使用AsyncAppender方法实现异步日志
- 使用AsyncAppender方式实现的全局异步日志输出
- 在 pom.xml 文件中加入异步日志依赖
<!-- 异步日志依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.7</version>
</dependency>
- 在 log4j.xml配置文件的 Appenders标签中,对于异步进行配置,使用Async标签。
<!-- 配置异步日志(AsyncAppender)-->
<Async name="myAsync">
<!-- 将控制台输出做异步的操作 -->
<AppenderRef ref="consoleAppender"/>
</Async>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="myAsync"/>
</Root>
</Loggers>
- 测试全局异步输出日志信息
//日志的记录
Logger logger = LoggerFactory.getLogger(Log4j2与SLF4J联合使用.class);
// slf4j 中存在5种日志输出级别,此时使用是slf4j的记录器,而不是log4j2的,所以只能输出slf4j中的五种级别。
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 < 100; i++) {
System.out.println("-----------");
}
- 输出结果(观察结果可以发现,此时实现了异步日志的输出)
8.11 log4j2使用AsyncLogger 方法实现异步日志(全局异步)
- 全局异步 :所有的日志都是异步的日志记录,在配置文件上不用做任何的改动。
- 只需要在类路径 resources 下添加一个properties属性文件,做一步配置即可。
文件名要求是:log4j2.component.properties - 在log4j2.component.properties配置文件中,加入下面这行代码即可。
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
- 修改 log4j2.xml 文件中的配置信息(只需要将输出的位置修改即可)
<Loggers>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="consoleAppender"/>
</Root>
</Loggers>
- 输出结果(实现了全局异步)
8.12 log4j2使用AsyncLogger 方法实现异步日志(混合异步)
- 同时在应用中使用同步日志和异步日志。
- 需求:假设我们现在有自定义的logger – com.yunbocheng 让自定义的logger是异步的,让rootlogger是同步的。
注意:在做测试前,一定要将全局的异步配置注释掉(resources下的properties),使用 # 注释。
#Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
- 注释掉全局异步的文件(以上)
- 修改 xml配置文件
<!-- 配置logger -->
<Loggers>
<!--
测试(混合异步) 自定义logger,让自定义的logger为异步logger
includeLocation="false"
表示去除日志记录中的行号信息,这个行号信息非常的影响日志记录的效率(生产中都不加这个行号)
严重的时候可能记录的比同步的日志效率还要低。
additivity="false"
表示不继承 rootlogger
-->
<AsyncLogger name="com.yunbocheng" level="trace"
includeLocation="false" additivity="false">
<!-- 将控制台的输出,设置为异步打印 -->
<AppenderRef ref="consoleAppender"/>
</AsyncLogger>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="consoleAppender"/>
</Root>
</Loggers>
- 以上都在控制台上进行输出。
注意:
- AsyncAppender、AsyncLogger不要同时出现,没有这个需求,效果也不会叠加。
- 如果同时出现,那么效率会以AsyncAppender为主。
- AsyncLogger中的全局异步和混合异步也不要同时出现,没有这个需求,效果也不会叠加。
各种语言的异步日志强度比较
以上项目的源代码,点击星球🌍进行免费获取 星球(Github地址)如果没有Github的小伙伴儿。可以搜索🔍微信公众号:Java学术趴,📭发送Java日志,免费给发给大家项目源码,代码是经过小编亲自测试🔧的,绝对可靠,免费拿去使用。
-------💘看完的大佬们可以关注一下小编,会一直更新小技巧,免费分享给大家呦!!!💝-----