目录
日志框架出现的历史顺序:Log4j → JUL → JCL → slf4j → logback → log4j2
前言
虽然JUL是Java原生的日志框架,JDK自带的,使用时不需要另外引用第三方类库,相对于其他日志框架而言,它使用方便,学习简单。
但随着功能增加,单一的日志文件会越来越臃肿,需要按照一定的规则进行滚动拆分,比如按照时机来进行拆分,以天为单位进行管理,但是JUL并不支持,而log4j能够满足我们这一需求
什么是Log4J ?
Log4j是Apache下的一款开源的日志框架,通过在项目中使用Log4j,我们可以控制日志信息输出到控制台、文件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程,方便项目的调试
▎快速入门案例
1. 引入maven依赖(为了方便测试,同步也引入junit)
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.13</version>
</dependency>
</dependencies>
2. 编写测试用例
// 注意:log4j日志框架Logger对象是apache包下:org.apache.log4j.Logger
public class TestLog4J {
// 入门案例
@Test
public void testQuick(){
// 1.获取日志管理器对象
Logger logger = Logger.getLogger(TestLog4J.class);
// 2.输出日志
logger.info("Log4J info");
}
}
3. 运行测试用例
WARN:由上图可知,运行测试用例报警告,提示我们以当前类命名的logger对象,找不到该logger对象的appenders处理器,它希望我们设置并初始化一个系统配置信息
✈ 初始化配置信息
我们可以直接增加一行代码,进行对logger对象的初始化配置
// 获取logger对象前,增加一行初始化配置信息的代码
BasicConfigurator.configure();
该方法内部主要做了两件事:1.创建父元素RootLogger对象 2.为父元素设置Appender处理器
☛ 继续执行测试用例
Log4j日志级别
log4j 日志级别总共有8种,除去两个特殊的级别:off、all,其级别严格可以划分6种
日志级别(降序) | 使用 |
---|---|
OFF | 关闭所有级别的日志记录(不捕获任何内容) |
FATAL | 严重错误,一般会造成系统奔溃并终止运行 |
ERROR | 错误信息,不会影响系统运行 |
WARN | 警告信息,可能会发生问题 |
INFO | 运行信息,数据连接、网络连接、IO 操作等 |
DEBUG(默认) | 调试信息,一般在开发中使用,记录程序变量参数传递信息等 |
TRACE | 追踪信息,记录程序所有的流程信息 |
ALL | 打开所有级别的日志记录(捕获所有内容) |
➳ 结论:级别依次从高到低,其中OFF可用来关闭日志记录,ALL启用所有消息的日志记录
★ 默认日志级别
从测试用例结果得知,我们常用的一般是中间四种,且log4j 默认的日志级别为debug,低于debug级别的trace日志信息没有打印出来,如下
Log4j组件
Log4J 主要由Loggers(日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。其中Loggers控制日志的输出级别与日志是否输出;Appenders指定日志的输出方式(输出到控制台、文件等);Layout控制日志信息的输出格式。
▎Loggers
日志记录器,负责收集处理日志记录,实例的命名就是类的权限定名,Logger的名字大小写敏感,其命名有继承机制:例如name 为org.apache.commons的logger会继承name为org.apache的logger。(与JUL日志框架一样)
✈ 知识点:Log4J 中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接或者间接地继承自root。root logger可以用Logger.getRootLogger() 方法获取。
▎Appenders
Appenders用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。Log4j常用的输出目的地有以下几种
输出端类型 | 作用 |
---|---|
ConsoleAppender | 将日志输出到控制台 |
FileAppender | 将日志输出到文件 |
DailyRollingFileAppender | 将日志输出到一个日志文件,并且每天输出到一个新的文件 |
RollingFileAppender | 将日志信息输出到一个日志文件,并且制定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,产生一个新的文件 |
JDBCAppender | 把日志信息保存到数据中 |
▎Layouts
布局器Layouts用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。Log4j常用的Layouts:
格式化器类型 | 作用 |
---|---|
HTMLLayout | 格式化日志输出为html表格形式 |
SimpleLayout | 简单的日志输出格式化,打印的日志格式为(info - message) |
PatternLayout | 最强大的格式化器,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式 |
Log4j 配置文件
我们知道Logger对象是通过LogManager日志管理器对象来获取的
1. 查看源码LogManager类,其中定义了几个静态成员变量,log4j可以通过加载如下几种方式来配置logger
2. 往下滑,可以看到LogManager的静态代码块,配置文件的加载方式是通过类加载器进行加载的
3.从上图可知,LogManager日志管理器类通过类加载的方式加载配置文件,那么我们可以直接在resource资源目录下创建log4j.properties配置文件,名字必须是一致的,否则加载失败
4. 配置文件创建好了,那么它是如何加载配置文件里面的信息呢?还有参数如何设置?在静态代码块底部可以看到最终调用了selectAndConfigure方法,用于读取配置文件
5. 点击进入selectAndConfigure方法,可以看到内部初始化了一个配置类,该类有2种初始化方式,取决于你的配置文件是什么类型的,由于我们是properties类型的文件,因此我们继续查看第二种类型的配置类
6. PropertyConfigurator类定义了很多变量,在上述入门案例中,重点关注两个对象RootLogger 和 Appender,因此我们只需要简单定义这两个参数即可
7. 搜索这两个参数在当前类中的具体设置方式 ,如下
分析:关于appender处理器的参数具体设置,可设置两个:appender的名称、appender指定的layout类型,也就是说我们在properties的参数需要通过上述蓝色字体描述的格式定义
- log4j.appender.自定义名称 = 具体appender类型
- log4j.appender.自定义名称.layout = 具体layout类型
8. 在log4j.properties配置文件中设置appender配置参数(完整代码贴在下面了)
9. appender处理器的配置参数设置好了,继续搜索 log4j.rootLogger 参数使用
10. 查看具体解析 log4j.rootLogger 参数的方法 parseCategory()
从解析方法中,可知 log4j.rootLogger配置参数的具体格式和可以指定的value值:
✈ 指定格式:逗号切割
log4j.properties配置文件中 log4j.rootLogger 的参数配置,其value值可以设置多个,并且多个值之间以逗号分隔
✈ 指定参数
- level(日志级别):指定rootLogger的日志级别(默认是debug)
- appender(处理器):指定rootLogger的appender处理器,并且一个Logger对象可指定多个
11. 在log4j.properties配置文件中设置rootLogger配置参数
▶ 配置文件总结
至此自定义配置properties文件的简单设置就完成了,主要是针对 rootLogger对象进行相关配置,log4j.properties 配置文件代码如下:
# 设置rootLogger的日志级别为trace,appender处理器类型为下方我们自主命名myAppender的处理器指向的ConsoleAppender
log4j.rootLogger = trace,myAppender
# "log4j.appender." 是前缀规范设置参数,myAppender是我们自己定义的appender的名称
# 设置appender类型为ConsoleAppender:将日志输出到控制台
log4j.appender.myAppender = org.apache.log4j.ConsoleAppender
# 设置名为myAppender的appender处理器,它的layout格式化日志类型为SimpleLayout:简单的日志输出格式化
log4j.appender.myAppender.layout = org.apache.log4j.SimpleLayout
1. 运行测试用例测试配置是否生效
➳ 解析:默认是debug模式,控制台输出了trace级别的日志内容,说明我们的配置文件生效了!
▎自定义Log4j 配置文件总结
由此我们进一步加深了对Log4J的相关组件的认识:
- logger 对象:设置日志级别、日志信息
- Appender 处理器:设置日志的输出位置
- layout 布局器:设置日志输出的格式
通过上述自定义配置文件方式,我们了解到自定义的Logger对象,其命名如果不涉及继承机制,那么默认会继承的RootLogger对象,他是所有logger 对象的根(未指定父类的logger)。
因此在log4j.properties配置文件中,只要对RootLogger父对象进行相关设置即可,因为它们是父子关系,子类默认继承父类的相关特性,例如:level日志级别、appender处理器、layout布局器等
开启Log4j内置日志记录
目前只看到自己的输出日志,未看到log4j自己的日志输出信息,这是因为需要手动开启
在LogManager日志管理器类中,我们可以通过搜索关键词LogLog,找到它的相关打印日志信息
LogLog 类是什么呢?
LogLog为什么也能跟Logger对象一样打印日志信息呢,它跟Logger有何区别?
LogLog类是Log4j 自己内置的log日志记录器,它记录了自己自身执行的信息。
在执行Log4j的过程,为什么没看到控制台关于Log4j的自己的日志输出信息呢?这是因为它内置是默认关闭状态,如下图:
☛ 说明:从上图可看到,输出条件 debugEnabled=true,quietMode=false,Log4j才会打印内置log日志,由于这两个参数默认都是false,因此我们只需要设置debugEnabled为true即可
▎设置debugEnabled
设置方法很简答, 通过如下代码进行设置即可
LogLog.setInternalDebugging(true);
1. 执行下方的测试用例
@Test
public void testProperties(){
// 开启Log4j 的内置log打印
LogLog.setInternalDebugging(true);
// 1.获取日志管理器对象
Logger logger = Logger.getLogger(TestLog4J.class);
// 2.输出日志
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
2. 执行结果
➳结论:由于我们上述自定义了log4j的配置文件,在log4j的内置log中有进行详细的输出
Log4j 的Layout 设置
格式化器类型 | 作用 |
---|---|
HTMLLayout | 格式化日志输出为html表格形式 |
SimpleLayout | 简单的日志输出格式化,打印的日志格式为(info - message) |
PatternLayout | 最强大的格式化器,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式 |
在自定义配置文件中,我们配置了layout格式化类型为SimpleLayout简单的日志输出格式,除此之外还有另外2种格式:HTMLFormatter、PatternLayout
✈ HTMLFormatter
格式化日志输出为html表格形式
1. 将自定义配置文件的layout类型修改
2. 运行测试类
@Test
public void test(){
// 1.获取日志管理器对象
Logger logger = Logger.getLogger(TestLog4J.class);
// 2.输出日志
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
3. 运行结果
内容太长就不长截图了,我们可以把控制台的内容复制到一个文件,用.html后缀名保存查看效果,比如new_file.html
✈ PatternLayout
最强大的格式化器,可根据自定义格式输出日志,如未指定转换格式,则用默认的转换格式
1.修改自定义配置文件中layout类型
log4j.appender.myAppender.layout = org.apache.log4j.PatternLayout
2. 测试用例执行结果如下
简单的不能再简单了,等同于System.out.println()方法,只打印日志内容
★ PatternLayout 配置参数
PatternLayout如果不自定义输出格式,那么会使用默认的格式,如下图
PatternLayout自身提供了两种配置,一种是默认配置 “%m%n” 表示只打印日志内容,第二种详细信息配置,可以打印线程等日志信息,输出内容比较全面。
当然我们也可以自定义配置,Log4j 采用了类似c语言的printf函数的打印格式格式化日志信息,具体的占位符及其含义如下:
占位符 | 含义 |
---|---|
%m | 输出代码指定的日志信息 |
%p | 输出日志级别 |
%n | 换行符 |
%r | 输出自应用启动到输出该log的毫秒数 |
%c | 输出打印语句所属的类全名 |
%t | 输出产生该日志的线程全名 |
%d | 输出服务器当前时间,默认为ISO8601,也可以指定格式,如:%d{yyyy-MM-dd HH:mm:ss} |
%l | 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如Test.main(Test.java:10) |
%F | 输出日志消息产生时所在的文件名称 |
%L | 输出代码中的行号 |
%% | 输出一个“%”字符 |
☁ 如何自定义?
格式化配置 setConversionPattern() 方法,传递了是一个conversionPattern形参
1. 根据 PatternLayout 类的配置格式化方法,我们就可以在log4j.properties文件中进行设置:
# 语法
log4j.appender.myAppender.layout.conversionPattern = xxx
2. 测试类运行结果如下
由上图可知,我们除了随意组合占位符之外,还能增加一些符号来突出内容信息,比如 %t 表示运行该日志的线程名称,为了突出它,我增加了括号:[%t] ,那么它在控制台显示的就是 [线程名]
3. 比如下方配置,为了突出日志级别,使用了[ ] 括号,为了控制台输出美观,增加了很多空格
4. 控制台输出结果
5. 如果嫌弃配置的占位符过多,但是又想显示全面,可以使用 %l 代替下面4个占位符
6. 打印结果如下
➳ 结论:小写的l 可以代替其它4个占位符,并且还增加了运行代码行数的引用
▎拓展
除了占位符来指定日志的格式信息,还可以在%与字符之间,加上修饰符来控制最小宽度、最大宽度和文本的对其方式
比如 %p,我们为了突出日志级别,可以增加括号:[%p]
每个日志级别的字符长度是不一样的,输出的内容不对称,强迫症看了都说不好,那么我们可以在%和字符之间设置一个数值,表示占位n个字符,不足n个字符则用空格补充。
举例说明:%p日志级别,设置占位10个字符
# 指定日志消息的内容格式
log4j.appender.myAppender.layout.conversionPattern = [%10p] %m%n
比如FATAL日志级别,只有5个字符,但是我们设置了占位10个,那么不足10个的,将用空格补齐,让输出的内容进行某种程度上进行对齐
上述默认是右对齐,我们还可以 “ - ” 符号,来让它进行左对齐
# 指定日志消息的内容格式
log4j.appender.myAppender.layout.conversionPattern = [%-10p] %m%n
Log4j 的Appenders 设置
输出端的类型有很多种,我们可以设置一个或多个输出端类型
输出端类型 | 作用 |
---|---|
ConsoleAppender | 将日志输出到控制台 |
FileAppender | 将日志输出到文件 |
DailyRollingFileAppender | 将日志输出到一个日志文件,并且每天输出到一个新的文件 |
RollingFileAppender | 将日志信息输出到一个日志文件,并且制定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,产生一个新的文件 |
JDBCAppender | 把日志信息保存到数据中 |
✈ FileAppender
将日志输出到文件
设置方法与上述自定义配置文件中 ConsoleAppender 是一致的
- 定义appender类型
- 定义该appender的layout消息格式化组件
- 将定义的appender配置到Logger对象
# ---------------------------设置FileAppender-------------------------
# 日志文件输出的Appender对象
log4j.appender.myFileAppender = org.apache.log4j.FileAppender
# 指定消息格式 layout
log4j.appender.myFileAppender.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.myFileAppender.layout.conversionPattern = [%p] %r %l %d{yyyy-MM-dd HH:mm:ss} %m%n
每个logger对象可配置多个Appender,不同的Appender可指定不同的layout 消息格式化组件
测试运行结果
➹ FileAppender 参数
FileAppender类中有4个参数,我们需要重点关注2个:fileName、fileAppend
- fileAppend:日志是否以追加的方式保存,true表示每条日志消息都追加保存到日志文件中
- fileName:日志文件名称
其中fileName是日志文件名称,这个参数跟我们上面运行报的警告有直接关系,在FileAppender类中有一个setFile方法,就是上述报错的内容,让我们指定一个file,也就是文件名称,同时也可以指定文件路径,格式为:路径/ 文件名.log
1. 通过FileAppender类提供的setFile() 方法,指定保存日志文件的路径和日志文件的名称,如下
# 该指定路径的文件夹必须存在,否则报错
log4j.appender.自定义appender的名称.file = 路径/日志文件名称
2. 设置完毕后,查看测试结果
3. 我们查看指定的文件目录结果
★ 字符集编码设置
有些时候为了防止输出的日志文件乱码,我们需要设置其字符集编码
FileAppender 类中没有提供设置编码的参数,但是它的父类中有提供
因此我们可以直接拿来设置,如下
# 指定文件保存路径
log4j.appender.myFileAppender.file = /Users/wpf01137679/logs/log4j.log
# 指定日志文件的字符编码集
log4j.appender.myFileAppender.encoding = UTF-8
✈ RollingFileAppender
按照文件大小拆分的appender对象
FileAppender类中可设置日志的保存方式,比如追加或覆盖,如果是追加,每条日志信息都保存到一个日志文件,不便于管理维护,如果是覆盖,那就直接日志消息丢失了。
我们希望是按照一定的规则将日志分开保存,由此FileAppender提供了两个实现对象:RollingFileAppender 和 DailyRollingFileAppender
RollingFileAppender 类提供了2个参数的设置,如下
★ 配置文件设置
1. 设置相关参数,由于是FileAppender的子类,应该FileAppender类的设置可以直接使用,在此基础上,我们增加RollingFileAppender的两个参数设置即可
# 设置rootLogger的日志级别为trace,appender处理器类型
log4j.rootLogger = trace,rollingAppender
# ---------------------------设置RollingFileAppender-------------------------
log4j.appender.rollingAppender = org.apache.log4j.RollingFileAppender
# 指定消息格式 layout
log4j.appender.rollingAppender.layout = org.apache.log4j.SimpleLayout
# 指定文件保存路径
log4j.appender.rollingAppender.file = /Users/wpf01137679/logs/log4j.log
# 指定文件的字符编码集
log4j.appender.rollingAppender.encoding = UTF-8
# 指定日志文件的最大size
log4j.appender.rollingAppender.maxFileSize = 1MB
# 指定日志文件的数量
log4j.appender.rollingAppender.maxBackupIndex = 10
2. 编写测试类进行测试
@Test
public void test(){
// 1.获取日志管理器对象
Logger logger = Logger.getLogger(TestLog4J.class);
// 2.输出日志(增加文件大小,循环打印)
for (int i = 0; i < 100000; i++) {
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
3. 文件生成结果
➳ 结论:文件最大size为1MB,超出1MB会重新创建新日志文件,如果生成的日志文件数量超过了我们定义的10个,则最早创建的日志文件会被覆盖掉
✦ 扩展知识点
可能有同学发现了,RollingFileAppender类的maxFileSize成员变量是long类型,为啥配置文件中可以设置带单位的字符串?
在它的set方法中,实际调用了一个转换方法 toFileSize() ,其内部有进行对单位的换算,如果我们传入long类型的话,不方便阅读,而且数值太长,如果是单位的方式,会更加醒目
✈ DailyRollingFileAppender
按照时间规则拆分的appender对象
其默认的拆分方式是按照天来拆分
1.我们也可以修改为按秒来拆分,分隔符规定使用“-” ,如下
2. 测试结果如下,每执行一次,基本上都会生成一个日志文件
✈ JDBCAppender
把日志信息保存到数据中
1. 导入mysql的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2. 在数据库中建立日志表,用于消息存放
create table logs
(
log_id int auto_increment comment '日志ID' primary key,
project_name varchar(255) null comment '项目名',
create_date varchar(255) null comment '创建时间',
level varchar(255) null comment '优先级',
category varchar(255) null comment '所在类的全名',
file_name varchar(255) null comment '输出日志消息时所在的文件名称',
thread_name varchar(255) null comment '日志事件的线程名',
line varchar(255) null comment '行号',
all_category varchar(255) null comment '日志事件的发生位置',
message varchar(4000) null comment '输出代码中指定的消息'
)
comment '日志表';
3. 设置log4j.properties配置文件中配置相关连接信息
# ---------------------------设置JDBCAppender-------------------------
log4j.appender.logsAppender = org.apache.log4j.jdbc.JDBCAppender
# 指定消息格式 layout
log4j.appender.logsAppender.layout = org.apache.log4j.PatternLayout
# 指定连接信息
log4j.appender.logsAppender.Driver = com.mysql.cj.jdbc.Driver
log4j.appender.logsAppender.URL = jdbc:mysql://localhost:3306/test
log4j.appender.logsAppender.User = root
log4j.appender.logsAppender.Password = 123456
log4j.appender.logsAppender.Sql = insert into \
logs(project_name, create_date, level, category, file_name, thread_name, line, all_category, message) \
values ('aheadTestNginx','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
连接信息的参数设置可查看JDBCAppender类中的相关字段属性设置
4. 运行测试类
5. 查看数据库插入结果:每输出一条日志都会插入一条数据到数据库中
Log4j 的自定义logger
刚才所创建的logger对象都默认继承RootLogger,RootLogger的日志级别和输出位置已经在log4j.properties中进行了相应设置,为了方便后期的灵活指定,我们可以自定义logger对象
在Log4j下的PropertyConfigurator类有相关的配置参数,如下
1. 在log4j.properties配置文件中进行配置
# 设置rootLogger的日志级别为trace,appender处理器类型为ConsoleAppender
log4j.rootLogger = trace,consoleAppender
# 自定义logger对象的日志级别以及输出位置
# logger对象的名称为:com.Log4j,一般设置类的所在package
log4j.logger.com.Log4j = info,fileAppender
# ---------------------------设置ConsoleAppender-------------------------
# "log4j.appender." 是前缀规范设置参数,myAppender是我们自己定义的appender的名称
# 设置appender类型为ConsoleAppender:将日志输出到控制台
log4j.appender.consoleAppender = org.apache.log4j.ConsoleAppender
# 设置名为myAppender的appender处理器的layout日志格式化类型
log4j.appender.consoleAppender.layout = org.apache.log4j.PatternLayout
# 指定日志消息的内容格式
log4j.appender.consoleAppender.layout.conversionPattern = [%p] %r %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# ---------------------------设置FileAppender-------------------------
# 日志文件输出的Appender对象:输出到指定路径文件
log4j.appender.fileAppender = org.apache.log4j.FileAppender
# 指定消息格式 layout
log4j.appender.fileAppender.layout = org.apache.log4j.SimpleLayout
# 指定文件保存路径
log4j.appender.fileAppender.file = /Users/wpf011/log/log4j.log
# 指定文件的字符编码集
log4j.appender.fileAppender.encoding = UTF-8
!! 注意:同时设置了rootLogger父对象和自定义logger对象的配置参数,名为 com.Log4j 的自定义logger对象会继承rootLogger的属性配置,level日志级别将会被子对象的配置覆盖,而appender处理器会进行重用,也就是日志消息会在控制台进行输出(rootLogger指定的appender),也会输出到指定文件(自定义logger指定的appender)
2. 编写测试类
3. 输出结果:
● 日志级别会覆盖:自定义的logger对象设置的是info级别,会覆盖掉父对象设置的trace日志级别
● appender处理器会重用:父对象指定的appender 和自定义logger指定的appender 都会被使用
☁ 自定义logger对象有什么作用?
答:根据不同的业务场景来进行日志记录,比如我们自己编写的代码可以保存到日志文件中,第三方技术产生的问题,可以在控制台进行输出,并且日志级别设置为error。
✈ 例如:自定义一个logger对象为 org.apache,设置其日志级别为error,但不指定appender处理器,因为它会继承rootLoggere父对象的属性:即ConsoleAppender,将日志消息打印到控制台
1. 配置文件配置自定义对象的参数
2. 在测试类中再创建一个日志记录器对象:org.apache
第一个是当前类名所在的package的自定义logger对象,第二个是org.apache
3. 输出结果:名为org.apache 的自定义logger对象,只打印了error的日志级别消息
4. 日志输出文件中,也只有名为 com.Log4j 的自定义logger对象的输出内容
结论:自定义logger对象可以做到不同业务场景的日志隔离 ,方便维护管理和问题定位