# 学习目标
日志的作用和目的
日志的框架
JUL的使用
log4j的使用
JCL的使用
# 日志文件
日志文件是用于记录系统操作事件的记录文件或文件集合,具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。
在编写代码的时候,通常需要通过日志信息程序运行是否正常,出错时,根据日志信息能快速定位到错误地点。对于软件系统的开发,以及日后的维护工作,都有极其重要的作用。
# 日志框架
# 问题
日志框架需要考虑的问题:
控制日志输出的内容和格式
控制日志输出的位置
日志优化:一部日志,日志文件的归档和压缩
日志系统的维护
面向接口开发——日志门面
# 现有的日志框架
日志分为两个大的部分,日志实现与日志门面。所谓日志门面,就是为各种日志API
提供一个统一的接口,类似于操作数据库的JDBC
,针对不同的数据库,都是操作同一套API
接口。
日志门面:
JCL
slf4j
日志实现
JUL
logback
log4j
log4j2
# JUL
JUL
全称Java util Logging
是java
原生的日志框架,使用时,不需要引用第三方类库,方便、简单、灵活。
Loggers
:被称为日志记录器,应用程序通过获取Logger
对象,调用其API
来发布日志信息。Logger
通常时应用系统访问日志系统的入口程序。
Appenders
:也被称为Handlers
,每个Logger
都会关联一组Handlers
,Logger
会将日志交给关联Handlers
处理,由Handlers
负责将日志做记录。Handlers
在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。
Layouts
:也被称为Formatters
,它负责对日志事件中的数据进行转换和格式化。Layouts
决定了数据在一条日志记录中的最终形式。
Level
:每条日志消息都有一个关联的日志级别。该级别展现日志消息的重要性和紧迫性,不同的日志框架该级别会有细微差别。
Filters
:过滤器,根据需要定制哪些信息会被记录,哪些信息会被放行。
# 日志级别
severe
当程序运行错误的时候,就可以用severe
来记录错误信息。
warning
记录程序运行遇到的问题,一般不会造成程序中止运行,但是也需要注意。
info
记录程序运行中的一些消息,例如数据库连接信息,io
的传递信息等等。
config
记录配置信息。
fine
finer
finest
这三者都是记录debug
信息。使用时用其中一个就行了。
除上述外,还有两个特殊级别,off
(用来关闭日志记录),all
(用来开启日志记录)。
# 示例代码
基础类
package org.example.test;
public class JULTest {
public static void main(String[] args) throws IOException {
}
}
基本使用
// 获取日志记录器对象
Logger logger = Logger.getLogger("org.example.test.JULTest");
// 日志输出
logger.info("hello jul");
// 通用方法进行日记记录
logger.log(Level.INFO,"info msg");
String name = "小明";
String age = 12;
logger.log(Level.INFO,"用户信息:{0} {1}",new Object[]{name,age});
日志级别
// 获取日志记录器对象
Logger logger = Logger.getLogger("org.example.test.JULTest");
// 日志记录输出
// jul默认的日志级别是info
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
JUL
的默认级别时info
,因此上述代码只会输出到info
。
自定义配置日志级别
// 获取日志记录器对象
Logger logger = Logger.getLogger("org.example.test.JULTest");
// 自定义日志级别
// 关闭JUL默认的日志级别
logger.setUseParentHandlers(false);
//创建ConsoleHandler
ConsoleHandler consoleHandler = new ConsoleHandler();
// 创建格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 进行关联
consoleHandler.setFormatter(simpleFormatter);
logger.addHandler(consoleHandler);
// 配置自定义的日志级别
logger.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
// 日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
在上述代码中,关闭JUL
的默认配置,将日志的输出级别设置为ALL
,所有的日志信息都将会输出。
日志输出到文件
// 获取日志记录器对象
Logger logger = Logger.getLogger("org.example.test.JULTest");
// 关闭JUL默认的日志级别
logger.setUseParentHandlers(false);
// 创建FileHandler,这里写日志文件的位置
FileHandler fileHandler = new FileHandler("D:\\jul.log");
// 创建格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 进行关联
fileHandler.setFormatter(simpleFormatter);
logger.addHandler(fileHandler);
// 配置自定义的日志级别
logger.setLevel(Level.ALL);
fileHandler.setLevel(Level.ALL);
// 日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
上述代码将日志信息输出到了文件中,日志级别同样是ALL
。
logger对象父子关系
// 获取日志记录器对象
Logger logger1 = Logger.getLogger("org.example.test.JULTest");
// 获取日志记录器对象
Logger logger2 = Logger.getLogger("org.example.test");
// 测试是否是父子关系
System.out.println(logger1.getParent() == logger2);
// 所有日志记录器的顶级父元素 LogManager$RootLogger
System.out.println("Logger2 Parent:"+logger2.getParent()+",name:"+logger2.getParent().getName());
// 关闭JUL默认的日志级别
logger2.setUseParentHandlers(false);
//创建ConsoleHandler
ConsoleHandler consoleHandler = new ConsoleHandler();
// 创建格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 进行关联
consoleHandler.setFormatter(simpleFormatter);
logger2.addHandler(consoleHandler);
// 配置自定义的日志级别
logger2.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
// 日志记录输出
logger1.severe("severe");
logger1.warning("warning");
logger1.info("info");
logger1.config("config");
logger1.fine("fine");
logger1.finer("finer");
logger1.finest("finest");
上述代码展现了父子对象Logger
对象关系,子承父业,父对象logger2
设置日志级别为ALL
,子对象的默认日志级别也变成了ALL
。
# 日志配置
在resource
目录下创建logging.properties
文件
# RootLogger 顶级父元素制定的默认处理器为: ConsoleHandler
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
# RootLogger 顶级父元素默认的日志级别为:ALL
.level= ALL
# 自定义 Logger 使用
org.example.handlers = java.util.logging.ConsoleHandler
org.example.level = CONFIG
org.example.userParentHandlers = false
# 向日志文件文件输出 handler 对象
# 指定日志文件的路径
java.util.logging.FileHandler.pattern= D:/logs/java%u.log
# 指定日志文件内容大小
java.util.logging.FileHandler.limit= 50000
# 指定日志文件级别
java.util.logging.FileHandler.level= ALL
# 指定日志文件数量
java.util.logging.FileHandler.count= 1
# 指定日志文件的输出格式
java.util.logging.FileHandler.formatter= java.util.logging.SimpleFormatter
# 指定文件输出以追加的方式
java.util.logging.FileHandler.append= false
# 向控制台输出的 handler 对象
# 指定 handler 对象的日志级别
java.util.logging.ConsoleHandler.level= ALL
# 指定 handler 对象的日志消息信息格式对象
java.util.logging.ConsoleHandler.formatter= java.util.logging.SimpleFormatter
# 指定 handler 对象的字符集
java.util.logging.ConsoleHandler.encoding= UTF-8
# 日志实现原理
1 初始化
LogManager
1.1
LogManager
加载logging.properties
配置 1.2 添加Logger
到LogManager
2 从单例
LogManager
获取Logger
3 设置级别
Level
,并指定日志记录LogRecord
4
Filter
提供了日志级别之外更细粒度的控制
5
Handler
是用来处理日志输出位置
6
Formatter
是用来格式化LogRecord
的
# Log4j
Log4j
是Apache
的一个开源项目,通过使用Log4j
,我们可以控制日志信息输送的目的地是控制台、文件等,可以定制每一条日志输出的格式,控制日志输出的级别,完成这些操作,仅仅只需要修改配置文件,并不用修改业务逻辑代码。
使用Log4j
,首先需要导入Log4j
的jar
包,在maven
项目中,只需引用相应坐标即可:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
# 日志级别
Log4j
一共提供有六个日志级别:
fatal
表示严重错误,通常会造成系统崩溃中止运行。
error
表示错误信息,不会影响系统运行。
warn
表示警告信息,可能会发生的问题。
info
程序运行中的信息,数据库连接,io操作等等。
debug
表示调试信息,一般在开发中使用。
trace
表示追踪信息,记录所有的流程信息。
Log4j
的默认级别为debug
。
# 示例代码
基础类
package org.example;
public class App{
public static void main(String[] args){
}
}
入门代码
// 初始化配置信息,暂不使用配置文件
BasicConfigurator.configure();
// 获取日志记录器对象
Logger logger = Logger.getLogger(App.class);
// 严重错误,一般会造成系统崩溃,或者不能运行
logger.fatal("fatal");
// 错误信息,不会影响系统的运行
logger.error("error");
// 警告信息,可能会发生问题
logger.warn("warn");
// 运行信息,数据连接,网络连接,IO操作等等
logger.info("info");
// 调试信息,一般在开发中使用,记录程序变化
logger.debug("debug");
// 追踪信息,记录程序所有的流程信息
logger.trace("trace");
基本使用
一般Log4j
的配置信息主要就是通过配置文件,因此代码一般不做过多的改动,只需要将上面的默认配置文件注释掉或者删掉,在resource
目录下添加配置文件log4j.properties
即可。
// 获取日志记录器对象
Logger logger = Logger.getLogger(App.class);
// 严重错误,一般会造成系统崩溃,或者不能运行
logger.fatal("fatal");
// 错误信息,不会影响系统的运行
logger.error("error");
// 警告信息,可能会发生问题
logger.warn("warn");
// 运行信息,数据连接,网络连接,IO操作等等
logger.info("info");
// 调试信息,一般在开发中使用,记录程序变化
logger.debug("debug");
// 追踪信息,记录程序所有的流程信息
logger.trace("trace");
# 日志配置
在resource
目录中创建log4j.properties
文件
基本配置
最简单的配置,仅仅只配置了,日志级别,输出位置,输出格式。
# 指定 RootLogger 顶级父元素默认配置
# 指定日志级别=trace,使用的 appender 为console
log4j.rootLogger = trace,console
# 指定控制台日志输出的位置
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定日志的输出格式 layout
log4j.appender.console.layout = org.apache.log4j.SimpleLayout
开启log4j内置日志记录
在要记录的类代码中添加如下代码。
// 开启 log4j 内置日志记录
LogLog.setInternalDebugging(true);
开启后可以观察到log4j
自身执行的日志信息。
日志输出格式
日志输出的格式,log4j
提供了以下几种。
日志输出格式 | 作用 |
---|---|
HTMLLayout | 以html表格形式布局 |
PatternLayout | 灵活指定布局模式 |
SimpleLayout | 简单日志布局 |
TTCCLayout | 包含产生时间、线程、类别等 |
通过修改,以下配置信息,就可实现简单的日志输出格式。
# 指定日志的输出格式 layout
log4j.appender.console.layout = org.apache.log4j.SimpleLayout
若是想实现自定义的日志输出格式,则需要指定日志的布局为PatternLayout
,然后添加以下代码,就实现了自定义日志格式。
# 指定日志格式的内容
log4j.appender.console.layout.conversionPattern = [%-5p] %r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
上述的一些代码定制日志输出格式,可在下面表格查找其含义。
符号 | 作用 |
---|---|
%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 | 输出代码中的行号 |
%% | 输出一个"%"字符 |
日志输出到文件
# 日志文件输出的 appender 对象
log4j.appender.file = org.apache.log4j.FileAppender
# 指定日志的的格式 layout
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
# 指定日志文件的保存路径
log4j.appender.file.file = D:/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8
同时需要更改rootLogger
# 指定日志级别=trace,使用的 appender 为console和file
log4j.rootLogger = trace,console,file
按大小日志文件拆分
当日志输出到文件的时候,需要考虑到一个问题,能否将所有的日志记录都输出到一个文件中,这是不友好的,当日志记录众多,查找起来相当复杂,所以要使用到文件拆分。
# 按照文件大小拆分的 appender 对象
# 日志文件输出的 appender 对象
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
# 指定日志的的格式 layout
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
# 指定日志格式的内容
log4j.appender.rollingFile.layout.conversionPattern = [%-5p] %r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件的保存路径
log4j.appender.rollingFile.file = D:/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.rollingFile.encode = UTF-8
# 指定日志文件内容的大小
log4j.appender.rollingFile.maxFileSize = 1MB
# 指定日志文件的数量
log4j.appender.rollingFile.maxBackupIndex = 10
同业需要修改rootLogger
log4j.rootLogger = trace,rollingFile
按时间拆分日志文件
# 按照时间规则拆分的 appender 对象
log4j.appender.dailyFile = org.apache.log4j.DailyRollingFileAppender
# 指定日志的的格式 layout
log4j.appender.dailyFile.layout = org.apache.log4j.PatternLayout
# 指定日志格式的内容
log4j.appender.dailyFile.layout.conversionPattern = [%-5p] %r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件的保存路径
log4j.appender.dailyFile.file = D:/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.dailyFile.encode = UTF-8
# 指定日期拆分规则
log4j.appender.dailyFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
修改rootLogger
log4j.rootLogger = trace,dailyFile
将日志文件输出到数据库
首先需要在数据库中创建一个表
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) NOT NULL COMMENT '行号',
`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
`message` varchar(255) DEFAULT NULL COMMENT '输出代码中指定的消息',
PRIMARY KEY (`log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6005 DEFAULT CHARSET=utf8;
在pom.xml
中添加mysql
数据库的连接器。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
然后在配置文件中添加数据库的信息。针对不同的数据库有不同的实现,这里使用的是mysql
。
# 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:3306/srcrs?useSSL=false&serverTimezone=UTC
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('itcast','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
更改rootLogger
log4j.rootLogger = trace,logDB
自定义logger配置
这个是仅仅针对这个org.example
包下的类文件有用
log4j.logger.org.example = info,file
对于父对象rootLogger
,则会进行继承,日志的级别将进行覆盖,日志输出的位置将进行合并。
log4j.rootLogger = trace,console
# 日志实现原理
Log4j
主要由Loggers
(日志记录器)、Appenders
(输出端)和Layout
(日志格式化器)组成。其中Loggers
控制日志的输出级别与日志是否输出;Appenders
指定日志的输出方式(输出到控制台、文件等);Layout
控制日志信息的输出格式。
日志输出位置 | 作用 |
---|---|
ConsoleAppender | 输出到控制台 |
FileAppender | 输出到文件 |
DailyRollingFileAppender | 按时间产生日志文件 |
RollingFileAppender | 按文件大小产生新文件 |
WriterAppender | 将日志按流的方式输出到任何位置 |
# JCL
全名叫做Jakarta Commons Logging
,是Apache
提供的一个通用日志API。JCL
为日志门面,为所有的java
日志实现提供一个统一的接口,自身也提供了一个日志实现SimpleLog
,允许开发人员使用Log4j
,JUL
日志实现。
JCL
有两个基本的抽象类,Log
(基本记录器)和LogFactory
(创建Log
对象)。
# 项目初始化
在pom.xml
中导入JCL
的相应的坐标。
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
基础类
package org.example;
public class App
{
public static void main( String[] args ){
}
}
# JUL实现
入门代码
// 获取 log 日志记录器对象
Log log = LogFactory.getLog(App.class);
// 日志记录输出
log.info("hello jcl");
# Log4j实现
在pom.xml
中插入log4j
的坐标
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
在resource
目录中创建log4j.properties
文件,写入如下内容。
# 指定 RootLogger 顶级父元素默认配置
# 指定日志级别=trace,使用的 appender 为console
log4j.rootLogger = trace,console
# 指定控制台日志输出的位置
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定日志的输出格式 layout
log4j.appender.console.layout = org.apache.log4j.SimpleLayout
# JCL原理
内置有日志实现的一个顺序,优先级高的执行了,优先级低的就会被屏蔽。
private static final String[] classesToDiscover = new String[]{
"org.apache.commons.logging.impl.Log4JLogger",
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
};
灵魂拷问,优先级低的如何让它实现呢?