java日志:二、log4j使用
1 介绍
Log4j是Apache下的一款开源日志框架,通过在项目中使用Log4j,可以控制日志信息输出到控制台、文件、甚至是数据库中。我们可以控制每条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程,方便项目调试。
Log4j主要由Loggers(日志记录器)、Appenders(输出端)和Layout(日志格式化器)组成,其中Loggers控制日志的输出级别与日志是否输出;Appenders指定日志的输出方式(输出到控制台、文件等);Layout控制日志信息的输出格式。
1.1 Loggers
日志记录器,负责收集处理日志记录,实例的命名就是类"xx"的full quailied name(类的全限定名),Logger的名字大小写敏感,其命名有继承机制。例如:name为org.apache.commoms的logger会继承name为org.apache的logger。
Log4j中有一个特殊的logger叫做"root",它是所有logger的根,也就意味着其他所有的logger都会直接或者间接的继承自root。root logger可以用Logger.getRootLogger()方法获取。
1.2 Appenders
Appenders用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。Log4j常用的输出目的地有以下几种(结构:输出端类型+作用):
输出端类型 | 作用 |
---|---|
ConsoleAppender | 将日志输出到控制台 |
FileAppender | 将日志输出到文件中 |
DailyRollingFileAppender | 将日志输出到一个日志文件,并且每天输出到一个新的文件 |
RollingFileAppender | 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件 |
JDBCAppender | 把日志信息保存到数据库中 |
1.3 Layouts
布局器Layouts用于控制日志输出内容的格式,可以使用各种需要的格式输出日志。常用Layouts如下:
格式化器类型 | 作用 |
---|---|
HTMLLayout | 格式化日志输出为HTML表格形式 |
SimpleLayout | 简单的日志输出格式化,打印的日志格式为(info-message) |
PatternLayout | 最强大的格式日期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式 |
2 依赖配置
提前配置好junit的依赖:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
https://search.maven.org/ 查询log4j的版本:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
注意:需要去掉 < type >bundle</ type >,否则配置时aliyun仓库会报错:
Could not find artifact log4j:log4j:bundle:1.2.17 in nexus-server@alipay (http://mvn.test.alipay.net:8080/artifactory/repo)
3 Log4j的使用
直接执行以下会报错:
import org.apache.log4j.Logger;
import org.junit.Test;
public class Log4jTest {
@Test
public void testlog(){
//获取日志记录器对象
Logger logger=Logger.getLogger(Log4jTest.class);
logger.info("hello log4j");
}
}
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.junit.Test;
public class Log4jTest {
@Test
public void testlog(){
//初始化配置信息,暂不使用配置文件
BasicConfigurator.configure();
//获取日志记录器对象
Logger logger=Logger.getLogger(Log4jTest.class);
logger.info("hello log4j");
//日志级别
//最高级别
logger.fatal("fatal");//严重错误,一般会造成系统崩溃并终止运行
logger.error("error");//错误信息,不会影响系统运行
logger.warn("warn");//警告信息,可能会发生问题
logger.info("info");//运行信息,数据连接、网络连接、IO操作等等
logger.debug("debug");//调试信息,一般在开发中使用,记录程序变量参数传递信息等等
logger.trace("trace");//追踪信息,记录程序所有流程信息
}
}
4 log4j配置文件使用
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
查看log4j的源码:
可知log4j是通过静态代码块中,类加载器加载的配置文件,所以可以在resources下新建一个log4j.properties:
继续:
需要自定义appender的名称:
log4j.properties:
log4j.rootLogger=WARN,conso
log4j.appender.conso=org.apache.log4j.ConsoleAppender
log4j.appender.conso.layout=org.apache.log4j.SimpleLayout
package com.base7;
import org.apache.log4j.Logger;
import org.junit.jupiter.api.Test;
public class Log4jTest {
@Test
public void testLogProperties(){
//获取日志记录器对象
Logger logger=Logger.getLogger(Log4jTest.class);
//最高级别
logger.fatal("fatal");//严重错误,一般会造成系统崩溃并终止运行
logger.error("error");//错误信息,不会影响系统运行
logger.warn("warn");//警告信息,可能会发生问题
logger.info("info");//运行信息,数据连接、网络连接、IO操作等等
logger.debug("debug");//调试信息,一般在开发中使用,记录程序变量参数传递信息等等
logger.trace("trace");//追踪信息,记录程序所有流程信息
}
}
FATAL - fatal
ERROR - error
WARN - warn
5 log4j内置日志记录
进入LogLog的debug,开关如下:
public static void debug(String msg, Throwable t) {
if (debugEnabled && !quietMode) {
System.out.println("log4j: " + msg);
if (t != null) {
t.printStackTrace(System.out);
}
}
}
debugEnable是默认关闭的,quiteMode是默认开启的,在LogLog.class中按下快捷键ctrl+F12(搜索内部的方法等等):
package com.base7;
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
import org.junit.jupiter.api.Test;
public class Log4jTest {
@Test
public void testLogProperties(){
//开启log4j内置日志记录
LogLog.setInternalDebugging(true);
//获取日志记录器对象
Logger logger=Logger.getLogger(Log4jTest.class);
//最高级别
logger.fatal("fatal");//严重错误,一般会造成系统崩溃并终止运行
logger.error("error");//错误信息,不会影响系统运行
logger.warn("warn");//警告信息,可能会发生问题
logger.info("info");//运行信息,数据连接、网络连接、IO操作等等
logger.debug("debug");//调试信息,一般在开发中使用,记录程序变量参数传递信息等等
logger.trace("trace");//追踪信息,记录程序所有流程信息
}
}
6 log4j的layout配置
log4j.properties:
log4j.rootLogger=WARN,conso
log4j.appender.conso=org.apache.log4j.ConsoleAppender
log4j.appender.conso.layout=org.apache.log4j.PatternLayout
import org.apache.log4j.Logger;
import org.junit.jupiter.api.Test;
public class Log4jTest {
@Test
public void testLogProperties(){
//获取日志记录器对象
Logger logger=Logger.getLogger(Log4jTest.class);
//最高级别
logger.fatal("fatal");//严重错误,一般会造成系统崩溃并终止运行
logger.error("error");//错误信息,不会影响系统运行
logger.warn("warn");//警告信息,可能会发生问题
logger.info("info");//运行信息,数据连接、网络连接、IO操作等等
logger.debug("debug");//调试信息,一般在开发中使用,记录程序变量参数传递信息等等
logger.trace("trace");//追踪信息,记录程序所有流程信息
}
}
PatternLayout默认就是只打印%m(消息):
fatal
error
warn
修改log4j.properties如下:
#指定RootLogger 顶级父元素默认配置信息
#指定日志的级别是WARN,使用的appender是conso
log4j.rootLogger=WARN,conso
#指定控制台日志输出的appender
log4j.appender.conso=org.apache.log4j.ConsoleAppender
#指定消息格式 layout
log4j.appender.conso.layout=org.apache.log4j.PatternLayout
#指定消息格式的内容
log4j.appender.conso.layout.conversionPattern=[%p]%r %c %t %d{yyyy年MM月dd日 HH:mm:ss.SSS} %m%n
#%m 输出代码中指定的日志信息
#%p 输出优先级,DEBUG、INFO等
#%n 换行符(Windows平台的换行符为"\r\n",Unix平台是"\n")
#%r 输出自应用启动到输出该log信息耗费的毫秒数
#%c 输出打印语句所属的类的全名
#%t 输出产生该日志的线程全名
#%d 输出服务器当前时间,默认为ISO8601,也可指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss.SSS}
#%l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:12)
#%F 输出日志消息产生时所在的文件名称
#%L 输出代码中的行号
#%% 输出"%"字符
执行上述代码,打印如下:(中文可能乱码,以?表示)
[FATAL]0 com.base7.Log4jTest main 2021?11?04? 21:01:38.394 fatal
[ERROR]1 com.base7.Log4jTest main 2021?11?04? 21:01:38.395 error
[WARN]2 com.base7.Log4jTest main 2021?11?04? 21:01:38.396 warn
再次修改:
#指定RootLogger 顶级父元素默认配置信息
#指定日志的级别是WARN,使用的appender是conso
log4j.rootLogger=WARN,conso
#指定控制台日志输出的appender
log4j.appender.conso=org.apache.log4j.ConsoleAppender
#指定消息格式 layout
log4j.appender.conso.layout=org.apache.log4j.PatternLayout
#指定消息格式的内容
log4j.appender.conso.layout.conversionPattern=[%p]%r %c %t %F %L %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
[FATAL]0 com.base7.Log4jTest main Log4jTest.java 18 2021-11-04 21:03:55.717 fatal
[ERROR]4 com.base7.Log4jTest main Log4jTest.java 19 2021-11-04 21:03:55.721 error
[WARN]4 com.base7.Log4jTest main Log4jTest.java 20 2021-11-04 21:03:55.721 warn
替换%c %t %F %L为%l:
#指定RootLogger 顶级父元素默认配置信息
#指定日志的级别是WARN,使用的appender是conso
log4j.rootLogger=WARN,conso
#指定控制台日志输出的appender
log4j.appender.conso=org.apache.log4j.ConsoleAppender
#指定消息格式 layout
log4j.appender.conso.layout=org.apache.log4j.PatternLayout
#指定消息格式的内容
log4j.appender.conso.layout.conversionPattern=[%p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
[FATAL]0 com.base7.Log4jTest.testLogProperties(Log4jTest.java:18) 2021-11-04 21:04:43.529 fatal
[ERROR]4 com.base7.Log4jTest.testLogProperties(Log4jTest.java:19) 2021-11-04 21:04:43.533 error
[WARN]5 com.base7.Log4jTest.testLogProperties(Log4jTest.java:20) 2021-11-04 21:04:43.534 warn
可以在%与字符之间加上修饰符来控制最小宽度、最大宽度和文本的对齐方式:
格式 | 描述 |
---|---|
%5c | 输出category名称,最小宽度是5,category<5,默认情况下右对齐 |
%-5c | 输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格 |
%.5c | 输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格 |
%20.30c | category名称<20补空格,并且右对齐,>30字符,就会从左边多出的字符截掉 |
比如:
#指定消息格式的内容
log4j.appender.conso.layout.conversionPattern=[%10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
右对齐:
[ FATAL]0 com.base7.Log4jTest.testLogProperties(Log4jTest.java:18) 2021-11-04 21:12:26.715 fatal
[ ERROR]3 com.base7.Log4jTest.testLogProperties(Log4jTest.java:19) 2021-11-04 21:12:26.718 error
[ WARN]3 com.base7.Log4jTest.testLogProperties(Log4jTest.java:20) 2021-11-04 21:12:26.718 warn
7 log4j的FileAppender配置
package com.base3;
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
import org.junit.Test;
public class Log4jTest {
@Test
public void testLogFj(){
//开启log4j内置日志记录
LogLog.setInternalDebugging(true);
//获取日志记录器对象
Logger logger=Logger.getLogger(Log4jTest.class);
//日志记录输出
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
控制台输出:
日志file位置,log4j会在当前路径下,自动新建一个文件夹以及文件,然后写入内容(log4j源码中默认是以追加的形式写入文件):
8 log4j的RollingFileAppender使用(按照文件大小拆分日志)
log4j.properties:
log4j.rootLogger=WARN,conso,rollingFile
log4j.appender.conso=org.apache.log4j.ConsoleAppender
log4j.appender.conso.layout=org.apache.log4j.PatternLayout
log4j.appender.conso.layout.conversionPattern=[%p]%r %l %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=[%p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
#指定日志文件保存路径
log4j.appender.file.file=./logs/file.log
#指定日志文件的字符集
log4j.appender.file.encoding=UTF-8
#按照文件大小拆分的appender
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern=[%p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
log4j.appender.rollingFile.file=./logs/rolling_file.log
log4j.appender.rollingFile.encoding=UTF-8
#指定日志文件的大小(单位:KB、MB、GB等)
log4j.appender.rollingFile.maxFileSize=1MB
#指定日志文件的数量
log4j.appender.rollingFile.maxBackupIndex=10
package com.base3;
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
import org.junit.Test;
public class Log4jTest {
@Test
public void testLogFj(){
//开启log4j内置日志记录
LogLog.setInternalDebugging(true);
//获取日志记录器对象
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");
}
}
}
最多只会产生10个文件:
9 log4j的DailyRollingFileAppender使用(按照时间规则拆分日志)
但是有setDatePattern方法,可自由修改时间规则模式:
在DailyRollingFileAppender类文件按下ctrl+F12:
在log4j.properties中指定日期拆分的规则(时间pattern只能以短横线-划分):
log4j.rootLogger=INFO,conso,dailyFile
log4j.appender.conso=org.apache.log4j.ConsoleAppender
log4j.appender.conso.layout=org.apache.log4j.PatternLayout
log4j.appender.conso.layout.conversionPattern=[%p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
#按照时间规则拆分的appender
log4j.appender.dailyFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dailyFile.layout.conversionPattern=[%p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
log4j.appender.dailyFile.file=./logs/daily_roll_file.log
log4j.appender.dailyFile.encoding=UTF-8
#指定日期拆分规则
log4j.appender.dailyFile.datePattern='.'yyyy-MM-dd-HH-mm-ss-SSS
10 自定义logger对象设置
log4j.properties:
log4j.rootLogger=INFO,conso
#自定义logger对象(会默认继承rootLogger:
# 级别会覆盖,由INFO升级为warn,但是既会向控制台输出:conso;
# 也会向文件file输出)
# 注意:这里自定义的com.base3,是Log4jTest类所在的包名路径
log4j.logger.com.base3 = warn,file
log4j.appender.conso=org.apache.log4j.ConsoleAppender
log4j.appender.conso.layout=org.apache.log4j.PatternLayout
log4j.appender.conso.layout.conversionPattern=[%p]%r %l %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=[%p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
#指定日志文件保存路径
log4j.appender.file.file=./logs/xiaoxu_file.log
#指定日志文件的字符集
log4j.appender.file.encoding=UTF-8
package com.base3;
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
import org.junit.Test;
public class Log4jTest {
@Test
public void testLogFj(){
//开启log4j内置日志记录
// LogLog.setInternalDebugging(true);
//获取日志记录器对象
Logger logger=Logger.getLogger(Log4jTest.class);
//日志记录输出
logger.fatal("fatal 1");
logger.error("error 1");
logger.warn("warn 1");
logger.info("info 1");
logger.debug("debug 1");
logger.trace("trace 1");
}
}
三方框架的日志信息打印(自定义logger,继承的形式):
log4j.properties:
log4j.rootLogger=INFO,conso
#自定义logger对象(会默认继承rootLogger:
# 级别会覆盖,由INFO升级为warn,但是既会向控制台输出:conso;
# 也会向文件file输出)
# 注意:这里自定义的com.base3,是Log4jTest类所在的包名路径
log4j.logger.com.base3 = warn,file
#第三方框架(org.apache.log4j.Logger)的错误日志打印,不指定appender,就是默认rootLogger的appender(conso)
log4j.logger.org.apache=error
log4j.appender.conso=org.apache.log4j.ConsoleAppender
log4j.appender.conso.layout=org.apache.log4j.PatternLayout
log4j.appender.conso.layout.conversionPattern=[%p]%r %l %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=[%p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
#指定日志文件保存路径
log4j.appender.file.file=./logs/xiaoxu_file.log
#指定日志文件的字符集
log4j.appender.file.encoding=UTF-8
package com.base3;
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
import org.junit.Test;
public class Log4jTest {
@Test
public void testLogFj(){
//开启log4j内置日志记录
// LogLog.setInternalDebugging(true);
//获取日志记录器对象
Logger logger=Logger.getLogger(Log4jTest.class);
//日志记录输出
logger.fatal("fatal 1");
logger.error("error 1");
logger.warn("warn 1");
logger.info("info 1");
logger.debug("debug 1");
logger.trace("trace 1");
//三方框架的错误日志信息打印
//因为log4j.properties文件中appender名是类名的包路径,
//这里是org.apache.log4j.Logger,就可以继承log4j.properties的配置
Logger logger1=Logger.getLogger(Logger.class);
logger1.fatal("fatal apache1");
logger1.error("error apache1");
logger1.warn("warn apache1");
logger1.info("info apache1");
logger1.debug("debug apache1");
logger1.trace("trace apache1");
}
}
file中不会存入org.apache这个logger的日志,因为该logger没有指定appender,默认的rootLogger的appender只是输出到控制台的: