【Log】(一)Java 中的日志框架 JUL、Log4j
【Log】(二)Java 中的日志框架 JCL、SLF
【Log】(三)Java 中的日志框架 Logback、log4j2
现有的日志框架:
- 日志门面:JCL(Jakarta Common Logging)、SLF
- 日志实现:JUL(Java Util Logging)、log4j、logback、log4j2
logback
比 log4j
功能更加强大,性能更加好;log4j2
与 logback
功能相似,但前者的性能更好
1. JUL 学习
1.1 JUL 介绍
JUL:全称 Java Util Logging
,它是 Java 原生的日志框架,使用时不需要引入第三方类库,相对其它日志框架使用方便、学习简单,能够在小型应用中灵活使用。
架构:
- Loggers:记录器。应用程序通过获取
Logger
对象,调用其 API 来发布日志信息。Logger
对象通常是应用程序访问日志系统的入口程序; - Appenders:也被称为
Handlers
。每个Logger
都会关联一组Handlers
。Logger
会将日志交给关联的Handlers
处理,由Handlers
负责将日志做记录。Handlers
在此是抽象的。其具体的实现决定了日志记录的位置可以是控制台、文件等; - Layouts:也被称为
Formatters
。它负责对日志事件中的数据进行转换和格式化。Layouts
决定了数据在一条日志记录中的最终形式; - Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫。可以将
Level
和Loggers
、Appenders
做关联,以便于我们过滤消息; - Filters:过滤器。根据需要定制哪些消息会被记录,哪些消息会被放过。
【总结】:用户使用 Logger
来进行日志记录,Logger
持有多个 Handlers
,日志的输出操作是由 Handler
完成。在 Handler
输出日志前,会经过 Filters
的过滤,判断哪些日志级别放行,哪些日志级别拦截。Handler
会将日志内容输出到指定位置(控制台/文件)。Handler
在输出日志时会使用 Layout
,将输出内容进行排版。
1.2 快速入门
新建一个 SpringBoot 工程,引入一个 junit
依赖,POM 文件:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
入门案例:
public class JulTest {
public static final String CALSS_NAME = "com.zzc.sbexp.log.jul.JulTest";
@Test
public void testQuick() {
Logger logger = Logger.getLogger(CALSS_NAME);
// 输出日志 日志级别:info
logger.info("hello jul");
// 通过方法输出
logger.log(Level.INFO, "hello jul");
// 通过占位符输出变量值
String name = "zzc";
Integer age = 24;
logger.log(Level.INFO, "用户信息:{0}, {1}", new Object[]{name, age});
}
}
1.3 日志级别
日志级别从高至低:off
、server
、warning
、info
、config
、fine
、finer
、finest
、all
如果是:off
,那么就是关闭了日志级别输出;
如果是:all
,那么就是开启了日志级别输出。
Jul
默认级别是 info
,只要日志级别高于 info
,都会输出。
自定义日志级别:
步骤:
- 关闭系统默认配置
- 创建一个
Handler
。我这里是输出到控制台和磁盘,所以是ConsoleHandler
、FileHandler
; - 创建一个
Formatter
; - 将
Handler
与Formatter
进行关联; - 将
Handler
添加到Logger
中。
@Test
public void testLogConfig() throws IOException {
Logger logger = Logger.getLogger(CALSS_NAME);
// 关闭系统默认配置
logger.setUseParentHandlers(false);
// 创建控制台输出Handler
ConsoleHandler consoleHandler = new ConsoleHandler();
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 关联
consoleHandler.setFormatter(simpleFormatter);
// 创建文件输出Handler
FileHandler fileHandler = new FileHandler("logs.log");
fileHandler.setFormatter(simpleFormatter);
logger.addHandler(consoleHandler);
logger.addHandler(fileHandler);
// 配置日志级别
logger.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
fileHandler.setLevel(Level.WARNING);
logger.severe("server");
logger.warning("warning");
logger.info("info");
}
【注意】:logs.log 的父级路径必须得存在,否则,会报错。
自定义配置文件修改 RootLogger 的默认配置:
在 resource 路径下添加一个配置文件 logging.properties
,其内容为:
#RootLogger默认的处理器,可以配置多个,所有非手动解除父日志的子日志都将使用这些处理器
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
#RootLogger的日志级别(默认INFO),所有的Handler都受限于此日志级别,Handler的日志级别可以比RootLogger的日志级别
.level = INFO
java.util.logging.FileHandler.pattern = E:/logs/java%u.log
#单个日志文件大小,单位是bit,1024bit即为1kb
java.util.logging.FileHandler.limit = 1024 * 1024 * 10
#日志文件数量
java.util.logging.FileHandler.count = 1
#是否以追加方式添加日志内容
java.util.logging.FileHandler.append = true
java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.encoding = UTF-8
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
代码:
@Test
public void testLogProperties() throws IOException {
// 通过类加载加载配置文件
InputStream in = JulTest.class.getClassLoader().getResourceAsStream("logging.properties");
// 创建LogManger加载配置文件
LogManager logManager = LogManager.getLogManager();
logManager.readConfiguration(in);
Logger logger = Logger.getLogger(CALSS_NAME);
logger.severe("server");
logger.warning("warning");
logger.info("info");
logger.finest("finest");
}
自定义配置文件添加自定义的 Logger:
在 resource 路径下添加一个配置文件 logging.properties
,其内容为:
...
my.logger.handlers = java.util.logging.ConsoleHandler
my.logger.level = INFO
my.logger.useParentHandlers = false
代码:
@Test
public void testMyLogProperties() throws IOException {
// 通过类加载加载配置文件
InputStream in = JulTest.class.getClassLoader().getResourceAsStream("logging.properties");
// 创建LogManger加载配置文件
LogManager logManager = LogManager.getLogManager();
logManager.readConfiguration(in);
Logger logger = Logger.getLogger("my.logger");
logger.severe("server");
logger.warning("warning");
logger.info("info");
logger.finest("finest");
}
2. Log4j 学习
2.1 Log4j 介绍
Log4j
是 Apache
下的一款开源的日志框架,通过在项目中使用 log4j
,我们可以控制日志信息输出的位置(控制台、文件、数据库);也可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程,方便项目的调试。
Log4j 组件
Log4j
主要由 Loggers
(日志记录器)、Appenders
(输出端)、Layout
(日志格式化器)组成。
- Loggers:控制日志的输出级别与日志是否输出
- Appenders:指定日志的输出方式(控制台、文件)
- Layout:控制台日志信息的输出格式
1. Loggers
Loggers
:日志记录器,负责处理日志记录。实例的命名就是类的全限定名。Logger 的名字大小写敏感。其命名有继承机制。如:org.apache.commons
的 logger 会继承 org.apache
的 logger。
Log4j
中,有一个特殊的 logger,叫 root
,它是所有的 logger 的根。root logger 可以通过 Logger.getRootLogger()
方法获取。
2. Appenders
Appenders
用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。常用的目的地有以下几种:
- ConsoleAppender:将日志输出到控制台
- FileAppender:将日志输出到文件中
- DailyRollingFileAppender:将日志输出到一个日志文件,并且每天输出到一个新的文件
- RollingFileAppender:将日志输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时,产生一个新的文件
- JDBCAppender:将日志保存到数据库
3. Layouts
Layouts
用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。常用的 Layouts
:
- HTML Layout:格式化日志输出为 HTML 表格形式
- Simple Layout:简单的日志输出格式化。打印的日志格式为(info - message)
- Pattern Layout:最强大的格式化器。可以根据自定义格式输出日志,如果没有指定转换格式,则使用默认的转换格式
2.2 快速入门
新建一个 SpringBoot 工程,引入 log4j
依赖:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
入门案例:
public class Log4jTest {
@Test
public void testQuick() {
Logger logger = Logger.getLogger(Log4jTest.class);
logger.info("info");
}
}
执行后,查看控制台:
由此可知,需要进行系统配置。一般用配置文件进行配置,这里暂时先用代码进行配置。
@Test
public void testQuick() {
BasicConfigurator.configure();
Logger logger = Logger.getLogger(Log4jTest.class);
logger.info("info");
}
这样,日志信息就能正常输出。
2.3 日志级别
日志级别:
- fatal:严重错误。一般会造成系统崩溃并终止运行
- error:错误信息。不会影响系统运行
- warn:警告信息。可能会发生问题
- info:运行信息。数据连接、网络连接、IO操作等等
- debug:调试信息。一般在开发中使用,记录程序变量参数传递信息等等
- trace:追踪信息。记录程序所有的流程信息
Log4j
默认级别是 debug
,只要日志级别高于 debug
,都会输出。
2.4 使用配置文件
在 resources
下新建一个配置文件 log4j.properties
:
# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,console
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.SimpleLayout
在代码中使用:
@Test
public void testProperties() {
Logger logger = Logger.getLogger(Log4jTest.class);
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
1. 自定义配置 Appender 的格式
在 log4j.properties
配置文件中,我们定义了日志输出级别与输出端,在输出端中分别配置日志的输出格式:
# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,console
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#指定消息格式的内容
log4j.appender.console.layout.conversionPattern = [%p]%r %l %d{yyyy-MM-dd HH:mm:ss} %m%n
log4j
采用类似 C 语言的 printf()
函数的打印格式格式化日志信息,具体的占位符及其含义如下:
%m=输出代码中指定的信息
%p=输出优先级
%n=换行符
%r=输出自应用启动到输出该 log 信息耗费的毫秒数
%c=输出打印语句所属的类的全限定名
%t=输出产生该日志的线程全名
%d=输出服务器当前时间。默认为 ISO8601。可指定格式:%d{yyyy年MM月dd日 HH:mm:ss}
%l=输出日志发生位置。包括:类名、线程名及在代码中的行数
%F=输出日志消息产生时所在的文件名称
%L=输出代码中的行号
%%=输出一个 % 字符
可以在 % 与字符之间加上修饰符来控制最小宽度,最大宽度和文本的对其方式。如:
%5c=输出类的全限定名,最小宽度是5,类名小于5,默认情况下右对齐
%-5c=输出类的全限定名,最小宽度是5,类名小于5,“-” 指定左对齐,会有空格
%.5c=输出类的全限定名,最大宽度是5,类名大于5,就会将左边多出的字符截掉;小于5,不会有空格
%20.30c%=类名小于20,补空格、右对齐;大于30,就会将左边多出的字符截掉
2. 自定义配置文件的 FileAppender
log4j.properties
:将日志信息输出到文件 FileAppender
# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,console,rollFile
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.console.layout.conversionPattern = [%p]%r %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# 指定控制台日志输出的 appender
log4j.appender.file = org.apache.log4j.FileAppender
# 指定消息格式 layout
log4j.appender.file.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.file.layout.conversionPattern = [%p]%r %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# 指定日志文件的保存路径
log4j.appender.file.file = E:/temp/logs.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8
log4j.properties
:按照文件大小进行拆分
# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,console,rollFile
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.console.layout.conversionPattern = [%p]%r %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# 按照文件大小进行拆分的 appender 对象
log4j.appender.rollFile = org.apache.log4j.RollingFileAppender
# 指定消息格式 layout
log4j.appender.rollFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.rollFile.layout.conversionPattern = [%p]%r %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# 指定日志文件的保存路径
log4j.appender.rollFile.file = E:/temp/logs.log
# 指定日志文件的字符集
log4j.appender.rollFile.encoding = UTF-8
# 指定日志文件内容的大小
log4j.appender.rollFile.maxFileSize = 1MB
# 指定日志文件的数量
log4j.appender.rollFile.maxBackupIndex = 10
@Test
public void testPropertiesBySize() {
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");
}
}
log4j.properties
:按照时间大小进行拆分
# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,dailyFile
# 按照时间大小进行拆分的 appender 对象
log4j.appender.dailyFile = org.apache.log4j.DailyRollingFileAppender
# 指定消息格式 layout
log4j.appender.dailyFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.dailyFile.layout.conversionPattern = [%p]%r %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# 指定日志文件的保存路径
log4j.appender.dailyFile.file = E:/temp/logs.log
# 指定日志文件的字符集
log4j.appender.dailyFile.encoding = UTF-8
# 指定日志文件拆分的规则
log4j.appender.dailyFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
3. 自定义 Logger
log4j.properties
:
# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = trace,console
# 自定义 logger,也会继承RootLogger,所以也会输出到控制台
log4j.logger.com.zzc.sbexp.log.log4j = info,file
# apache 的logger,只有error级别及以上才会输出到控制台,并不会输出到文件
log4j.logger.org.apache = error
...
@Test
public void testMyLogger() {
Logger logger = Logger.getLogger(Log4jTest.class);
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
// apache 的 logger
Logger logger1 = Logger.getLogger(Logger.class);
logger1.fatal("fatal1");
logger1.error("error1");
logger1.warn("warn1");
logger1.info("info1");
logger1.debug("debug1");
logger1.trace("trace1");
}
运行后: