02、Log4j(第三方日志框架,带源码分析)(下)
六、各种Layout案例
6.1、PatternLayout(自定义格式示例)
创建过程:
①创建log4j.properties
,Maven
项目放置到resource
目录下
# 设置rootlogger日志等级为trace、指定appender为下面定义的ConsoleAppender
log4j.rootLogger = trace,console
# 指定ConsoleAppender设置别名为console
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定ConsoleAppender的layout设置为PatternLayout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 设置该layout的自定义格式(解析时会调用)
log4j.appender.console.layout.ConversionPattern= [%-5p]%r %l %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
- 设置自定义格式时实际上会先解析出.后面的名称为
ConversionPattern
之后添加上set,最后通过反射调用void setConversionPattern(String conversionPattern)
方法设置自定义格式。
源码查看是如何进行赋值的
经过一番努力,定位到通过反射来执行set方法的操作,看下去即可:
- 最终就是通过反射来执行
void setConversionPattern(String conversionPattern)
操作的,不仅感慨那些编写源代码的人是真的强!!!
②执行程序,输出自定义的格式内容:
public class LogTest {
public static void main(String[] args) {
//开启LogLog的debug模式
LogLog.setInternalDebugging(true);
//获取logger实例
Logger logger = Logger.getLogger(LogTest.class);
//打印不同的日志等级
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
- 看框中的格式即为我们自定义的格式内容!!!
七、各种Appender示例
7.1、FileAppender及其子类
FileAppender
:用于输出到文件,其有两个实现类,DailyRollingFileAppender
提供了轮询功能(可根据指定时间点添加文件)、RollingFileAppender
提供了根据文件大小拆分的功能。
首先看下层级图:
WriterAppender
:提供属性encoding
。FileAppender
:提供属性append
(是否追加,默认true)、file
(文件路径)、bufferedIO
(布尔值,默认false)、bufferSize
(文件大小,默认为8K)。DailyRollingFileAppender
:提供属性datePattern
(指定间隔轮询年月日时分秒)。RollingFileAppender
:提供属性maxFileSize
(单个文件最大容量)、maxBackupIndex
(最大分割文件数量)。
使用任意实现类时,即可设置其父类的属性。
FileAppender示例
作用:向文件进行输出,其有两个实现类可实现轮询以及分段(多文件)。
示例演示
①创建log4j.properties
log4j.rootLogger = trace,file
# FileAppender
# file为名称 其中属性file:文件路径 encoding:编码
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.file = C:/Users/93997/Desktop/projectexers/logs/log.txt
log4j.appender.file.encoding = UTF-8
# 设置自定义布局(自定义输出格式)
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern= [%-5p]%r %l %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
②程序测试
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
public class LogTest {
public static void main(String[] args) {
//开启LogLog的debug模式
LogLog.setInternalDebugging(true);
//获取logger实例
Logger logger = Logger.getLogger(LogTest.class);
//打印不同的日志等级
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
- 注意其中
logs
文件需要提前创建。
源码分析
之前我们已经通过源码看到实际赋值是通过反射调用set
方法进行的,所以我们直接看其中的set
方法以及默认属性即可。
说明:对于encoding
编码格式的设置,是因为FileAppender
继承了WriterAppender
(其中有setEncoding()
)。
DailyRollingFileAppender示例
作用:轮询功能(根据指定是每小时,还是每天输出log日志),父类是FileAppender
,包含父类的所有设置属性。
示例演示
# 日志等级为trace,指定appender为下面以roll作为别名的
log4j.rootLogger = trace,roll
# DailyRollingFileAppender 轮询
log4j.appender.roll = org.apache.log4j.DailyRollingFileAppender
log4j.appender.roll.file = /logs/log4j.log
log4j.appender.roll.encoding = UTF-8
# datePattern指的是根据分钟来进行轮询 =》可设置年月日时分秒毫秒如右: '.'yyyy-MM-dd-HH-mm-ss-SSS
log4j.appender.roll.datePattern = '.'yyyy-MM-dd-HH-mm
# 设置自定义布局(自定义输出格式)
log4j.appender.roll.layout = org.apache.log4j.PatternLayout
log4j.appender.roll.layout.ConversionPattern= [%-5p]%r %l %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.properties
放置在resource目录下(Maven项目)。- 这里设置路径
file
为/logs
:指的是c:\logs
目录下
测试程序:
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
public class LogTest {
public static void main(String[] args) {
//开启LogLog的debug模式
LogLog.setInternalDebugging(true);
//获取logger实例
Logger logger = Logger.getLogger(LogTest.class);
//打印不同的日志等级
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
- 上图仅作测试展示,与下方规则操作并不一致。新增的文件后缀实际上就是我们指定的每隔1分钟的时间。
- 规则说明(测试结果):我们指定路径为
/logs/log4j.log
,在第一次运行测试程序时会创建log4j.log
以及其第二个文件(两个都输出内容),接着第二次运行测试程序时(间隔未满1分钟),则继续会追加到log4j.log
中;第三次运行测试程序(超过1分钟),则会再次创建一个后缀添加日期的log文件并将日志内容输入到其中。
源码查看
RollingFileAppender示例
作用:按照指定文件大小进行拆分,拆分最大的文件数量可指定。其父类为FileAppender
。
示例演示
# 日志等级为trace,指定appender为下面以rollfile作为别名的#
log4j.rootLogger = trace,rollfile
# RollingFileAppender 分段
log4j.appender.rollfile = org.apache.log4j.RollingFileAppender
log4j.appender.rollfile.file = /logs/log4j.log
log4j.appender.rollfile.encoding = UTF-8
# 设置单个文件最大容量(KB、MB、GB,其他单位默认传为10MB+1)以及最大文件个数
log4j.appender.rollfile.maxFileSize = 1MB
log4j.appender.rollfile.maxBackupIndex = 5
# 设置自定义布局(自定义输出格式)
log4j.appender.rollfile.layout = org.apache.log4j.PatternLayout
log4j.appender.rollfile.layout.ConversionPattern= [%-5p]%r %l %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.properties
放置在resource目录下(Maven项目)。- 其中设置单个容量大小为1MB,最大文件数为5个。
程序测试:
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
public class LogTest {
public static void main(String[] args) {
//开启LogLog的debug模式
LogLog.setInternalDebugging(true);
//获取logger实例
Logger logger = Logger.getLogger(LogTest.class);
for (int i = 0; i < 1000000; i++) {
//打印不同的日志等级
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
- 由于程序执行1次看不到效果,所以我们执行多次来进行查看最终效果。
- 这些都是LogLog的debug日志,能够看到正在执行的事情。
- 可以看到对应的5个文件后缀名与我们设置最大文件数有关!!!
规则:若是超过文件容量maxFileSize
则进行拆分,最多拆分我们设置的maxBackupIndex
值数量的文件个数,若是超过文件数量则按照最先记录日志的进行覆盖。
源码分析
提问:对于最大文件容量maxFileSize
可以直接写单位的吗,这么舒服?看下去吧
老样子先看下对应的set方法
接着我们分析其中的set
方法:看看是如何根据单位分配的:
public class RollingFileAppender extends FileAppender {
//默认为10MB 1maxFileSize=1字节
protected long maxFileSize = 10*1024*1024;
//1、设置容量方法
public void setMaxFileSize(String value) {
//调用了OptionConverter辅助类中的转换方法
maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);//见2
}
}
public class OptionConverter {
//2、toFileSize()根据传入的String值来转换
public static long toFileSize(String value, long dEfault) {
if(value == null)
return dEfault;
//首先将字符串中字母全都变为大写
String s = value.trim().toUpperCase();
//计量单位1个字节
long multiplier = 1;
int index;
//下面进行容量判断,如KB、MB、GB
if((index = s.indexOf("KB")) != -1) {
multiplier = 1024;
s = s.substring(0, index);//例如15KB,则为15
}
else if((index = s.indexOf("MB")) != -1) {
multiplier = 1024*1024;
s = s.substring(0, index);
}
else if((index = s.indexOf("GB")) != -1) {
multiplier = 1024*1024*1024;
s = s.substring(0, index);
}
if(s != null) {
try {
return Long.valueOf(s).longValue() * multiplier;
}
catch (NumberFormatException e) {
LogLog.error("[" + s + "] is not in proper int form.");
LogLog.error("[" + value + "] not in expected format.", e);
}
}
//若是其他计量单位的话,默认返回10MB+1的容量
return dEfault;
}
}
通过查看源码,我们得知其中对字符串中的容量单位进行查找匹配,从而换算为指定的字节容量!!!
7.2、JDBCAppender
示例演示
概述及前提准备
JDBCAppender
:这个appender能够连接数据库并且执行指定的SQL语句,主要用于将日志插入到数据库中。
提前准备如下:
①创建数据库表log
:该表包含了日志的相关消息
CREATE TABLE `log` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
`create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
`level` varchar(255) DEFAULT NULL COMMENT '优先级',
`category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
`file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
`thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
`line` varchar(255) DEFAULT NULL COMMENT '号行',
`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
`message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
PRIMARY KEY (`log_id`)
);
- 记得先创建一个
test
数据库,下面会用到
②导入Mysql
的jar
包(驱动包),在pom.xml
中添加坐标
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
示例演示
# rootlogger的日志等级是trace,appender为JDBCAppender,logDB是下面指定的别名
log4j.rootLogger = trace,logDB
# JDBCAppender 存储到数据库中
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=123456
log4j.appender.logDB.Sql=INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('changlu','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
- 配置信息包含
layout
(输出格式)、Driver
(指定mysql驱动类)、URL
、User
、password
(连接数据库三要素)以及要执行的sql语句(其中的各种%形式都是PatternLayout
中的指定内容,可见上面6.1) - SQL对应则为简单的插入语句。
测试程序:
public static void main(String[] args) {
//开启LogLog的debug模式
LogLog.setInternalDebugging(true);
//获取logger实例
Logger logger = Logger.getLogger(LogTest.class);
for (int i = 0; i < 1000000; i++) {
//打印不同的日志等级
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
Log4j
的LogLog
的配置信息。
源码分析*
首先依旧是set
方法查看:
那么我们会发出疑问,该类是如何执行SQL语句的呢?我们看其中的execute
方法。
public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
//类名赋值操作
public void setDriver(String driverClass) {
try {
//类加载我们的mysql驱动,此时进行初始化操作了
Class.forName(driverClass);
} catch (Exception e) {
errorHandler.error("Failed to load driver", e,
ErrorCode.GENERIC_FAILURE);
}
}
//获取连接操作
protected Connection getConnection() throws SQLException {
if (!DriverManager.getDrivers().hasMoreElements())
setDriver("sun.jdbc.odbc.JdbcOdbcDriver");
if (connection == null) {
//获取数据库连接
connection = DriverManager.getConnection(databaseURL, databaseUser,
databasePassword);
}
return connection;
}
//执行sql语句方法
protected void execute(String sql) throws SQLException {
Connection con = null;
//这里使用到Statement来进行操作
Statement stmt = null;
try {
con = getConnection();
stmt = con.createStatement();
//执行sql语句方法
stmt.executeUpdate(sql);
} finally {
if(stmt != null) {
stmt.close();
}
closeConnection(con);
}
//System.out.println("Execute: " + sql);
}
}
看了源码之后我们知道在setDriver()
时就对Mysql
驱动类进行了初始化,之后execute()
方法中获取数据库连接,获取Statement
来进行操作数据库。
舒服了,但是对于解析log4j.properties
的源码部分没有进行深入,主要是对于现在的我来说太复杂了就先放着,真心觉得写源码的大佬真的太强了,自己真的还只是个菜鸟!加油吧!
八、自定义Logger
如何自定义Logger
呢?依旧是在配置文件中进行配置。
- 语法:
log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
。其中logger_name
可以设置为指定的包名,若获取logger
实例的name
为这个包名及包名下的类(全限定名)即归属于该实例。
用途:通过使用自定义Logger
能够将第三方包下调用的类以及自己定义的类进行日志区分,对于自己的类的日志信息输出到文件中,而对于第三方包的类输出到屏幕上。
实例演示
# rootLogger日志等级为trace,输出到屏幕上
log4j.rootLogger = trace,console
# 设置两个自定义logger
# xyz.changlu(自己创建的包)自定义logger,日志等级为info,输出到文件
log4j.logger.xyz.changlu = info,file
# 设置org.apache(第三方包)作为一个自定义logger,日志等级为error
log4j.logger.org.apache = error
# console
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern= [%-5p]%r %l %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
# FileAppender
# file:文件路径 encoding:编码
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.file = C:/Users/93997/Desktop/projectexers/logs/log.log
log4j.appender.file.encoding = UTF-8
# 设置自定义布局(自定义输出格式)
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern= [%-5p]%r %l %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
- 两个自定义
logger
默认都会继承rootLogger
的,当调用logger
实例时不仅仅会调用自身的appender
还会调用rootLogger
的appender
,并且logger
实例的日志等级会改变rootLogger
的日志等级(为logger
实例的日志等级)。
程序测试:
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.LogLog;
public class LogTest {
public static void main(String[] args) {
//开启LogLog的debug模式
LogLog.setInternalDebugging(true);
//① 自己包下类的自定义logger实例
//getLogger()参数为xyz.changlu包下的类,所以获取到配置文件中xyz.changlu的logger实例
//logger实例与rootLogger的日志等级都为INFO,本身实例输出到文件,rootLogger输出到窗口
Logger logger = Logger.getLogger(LogTest.class);
System.out.println(logger.getName());
//打印不同的日志等级
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
//② org.Apache包下的自定义logger实例,只输出到屏幕(本身实例没有设置logger)
// 本身实例与rootLogger日志等级为error
Logger logger1 = Logger.getLogger(Logger.class);
System.out.println(logger1.getName());
logger1.fatal("fatal logger1");
logger1.error("error logger1");
logger1.warn("warn logger1");
logger1.info("info logger1");
logger1.debug("debug logger1");
logger1.trace("trace logger1");
}
}
总结:当设置自定义logger
配置时,当你设置name为一个包名时(如xyz.changlu
),若在程序中调用Logger.getLogger(LogTest.class)
(LogTest
类的全限定类名为xyz.changlu.LogTest
,即也就是logger
实例的name
)就是在配置文件中配置的xyz.changlu
logger实例。
总结
1、Log4j
是Apache
提供的第三方日志框架,需要引入第三方jar包,其中同样了包含了Logger
、Appender
以及Layout
,有六个日志等级,默认日志等级为debug
。—见第一、二部分。
2、Log4j
的自定义配置文件名称为log4j.xml
。—见第三、四部分
3、包含了一个内置日志LogLog
,需要通过LogLog.setInternalDebugging(true);
来进行开启debug等级日志,作用是能够在控制台显示相关的加载配置信息过程,能够清楚其中的流程。—见第五部分
4、对于Log4j
必须要对rootLogger
进行初始化,默认不提供初始化,若想使用默认提供的需要调用方法BasicConfigurator.configure();
初始化或者我们进行自定义配置文件见四、六、七部分,主要着重介绍输出到文件、数据库以及自定义layout
的格式。—见四、六、七部分
参考资料
- 点赞
- 收藏
- 关注作者
评论(0)