1. 日志概述
对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离开不日志。java领域存在多种日志框架,目前常用的日志框架包括Log4j 1,Log4j 2,Commons Logging,Slf4j,Logback,Jul。
1.1 日志框架
- Log4j: Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Gülcü首创的,现在则是Apache软件基金会的一个项目。
- Log4j 2: Apache Log4j 2是apache开发的一款Log4j的升级产品。
- Commons Logging: Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging。
- Slf4j: 类似于Commons Logging,是一套简易的Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。
- Logback: 一套日志组件的实现(Slf4j阵营)。
- Jul: (Java Util Logging),自Java1.4以来的官方日志实现。
1.2 框架历史
- 1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员。
- 期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入Log4j到java的标准库中,但Sun拒绝了。
- 2002年Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。在JUL出来以前,Log4j就已经成为一项成熟的技术,使得Log4j在选择上占据了一定的优势。
- 接着,Apache推出了Jakarta Commons Logging,JCL只是定义了一套日志接口(其内部也提供一个Simple Log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你应用代码里,只需调用Commons Logging的接口,底层实现可以是Log4j,也可以是Java Util Logging。
- 2006年Ceki Gülcü不适应Apache的工作方式,离开了Apache。然后创建了Slf4j(日志门面接口,类似于Commons Logging)和Logback(Slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述Logback的The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。
- 现今,Java日志领域被划分为两大阵营:Commons Logging阵营和Slf4j阵营。Commons Logging在Apache大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013年底有人分析了GitHub上30000个项目,统计出了最流行的100个Libraries,可以看出Slf4j的发展趋势更好。
- Apache眼看有被Logback反超的势头,于2012-07重写了Log4j 1.x,成立了新的项目Log4j 2, Log4j 2具有Logback的所有特性。
1.3 框架选择
在新项目中推荐使用 Slf4j + Logback 的组合
- Slf4j实现机制决定Slf4j限制较少,使用范围更广。由于Slf4j在编译期间,静态绑定本地的LOG库使得通用性要比Commons Logging要好。
- Logback拥有更好的性能。某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在Logback中需要3纳秒,而在Log4J中则需要30纳秒。LogBack创建记录器的速度也更快,只需要13毫秒,而在Log4J中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒,而Log4J需要2234纳秒,时间减少到了1/23。跟JUL相比的性能提高也是显著的。
- Commons Logging开销更高。
- Logback的所有文档是全面免费提供的,不象Log4J那样只提供部分免费文档而需要用户去购买付费文档。
2. 日志组件
日志组件包括三个部分,记录器、输出源和布局。
2.1 记录器
Loggers即记录器,它指定日志的级别,日志的何种级别需要记录。记录器只输出级别不低于设定级别的日志信息,假设Loggers级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出,而级别比INFO低的DEBUG则不会输出。
2.2.1 日志级别
- 等级:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
级别 | 描述 |
---|---|
ALL | ALL是最低等级的,用于打开所有日志记录 |
TRACE | TRACE比DEBUG更低,一般不使用 |
DEBUG | DEBUG指出细粒度信息事件对调试应用程序是非常有帮助的 |
INFO | INFO表明消息在粗粒度级别上突出强调应用程序的运行过程 |
WARN | WARN表明会出现潜在错误的情形 |
ERROR | ERROR指出虽然发生错误事件,但仍然不影响系统的继续运行 |
FATAL | FATAL指出每个严重的错误事件将会导致应用程序的退出 |
OFF | OFF是最高等级的,用于关闭所有日志记录 |
配置模式:
<!-- 日志输出级别 -->
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
2.2 输出源
Appenders即输出源,它指定日志输出的位置,日志需要输出到哪里。允许把日志输出到不同的地方,如控制台、文件等,可以根据天数或者文件大小产生新的文件,也可以以流的形式发送到其它地方。
ConsoleAppender:控制台
FileAppender:文件
DailyRollingFileAppender:每天产生一个日志文件
RollingFileAppender:文件大小到达指定尺寸的时候产生一个新的文件
WriterAppender:将日志信息以流格式发送到任意指定的地方
配置模式:
<!-- 日志输出到控制台 -->
<Console name="Console_Appender" target="SYSTEM_OUT"></Console>
<!-- 日志输出到文件 -->
<File name="File_Appender" fileName="ERROR.log" append="false"></File>
<!-- 日志输出到文件(文件大小到达指定尺寸的时候产生一个新的文件) -->
<RollingFile name="File_ERROR_Appender" fileName="ERROR.log"></RollingFile>
2.3 布局
Layouts即布局,它规定日志的输出格式,日志要以什么样子输出。根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式。
HTMLLayout:以HTML表格形式布局
PatternLayout:可以灵活地指定布局模式
SimpleLayout:包含日志信息的级别和信息字符串
TTCCLayout:包含日志产生的时间、线程、类别等信息
2.3.1 输出格式
格式 | 描述 |
---|---|
%c | 输出日志信息所属的类的全名 |
%d | 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式 |
%f | 输出日志信息所属的类的类名 |
%l | 输出日志事件的发生位置,即输出日志信息的语句处于它所在的类的第几行 |
%m | 输出代码中指定的信息 |
%n | 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n” |
%p | 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL |
%r | 输出自应用启动到输出该日志信息所耗费的毫秒数 |
%t | 输出产生该日志事件的线程名 |
配置模式:
<!-- 日志输出格式 -->
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] 【%-5level】 %class{36} %L %M - %msg%xEx%n"/>
3. 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="error">
<!-- 定义日志输出根目录 -->
<Properties>
<Property name="LOG_HOME">D:/log</Property>
</Properties>
<!-- 定义日志输出源 -->
<appenders>
<!-- 日志输出到控制台 -->
<Console name="Console_Appender" target="SYSTEM_OUT">
<!-- 定义日志输出级别即记录器 -->
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- 定义日志输出格式即布局 -->
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] 【%-5level】 %class{36} %L %M - %msg%xEx%n"/>
</Console>
<!-- 日志输出到文件 -->
<File name="File_Appender" fileName="${LOG_HOME}/logs/file.log" append="false">
<!-- 定义日志输出级别即记录器 -->
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- 定义日志输出格式即布局 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] 【%-5level %class{36}】 %L %M - %msg%xEx%n"/>
</File>
<!-- 日志输出到文件(文件大小到达指定尺寸的时候产生一个新的文件) -->
<RollingFile name="File_ERROR_Appender" fileName="${LOG_HOME}/logs/ERROR.log" filePattern="${LOG_HOME}/logs/$${date:yyyy-MM}/ERROR-%d{yyyy-MM-dd}-%i.log.gz">
<!-- 定义日志输出级别即记录器 -->
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- 定义日志输出格式即布局 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] 【%-5level】 %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<!-- 采用时间策略 -->
<TimeBasedTriggeringPolicy/>
<!-- 指定文件大小 -->
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingFile>
</appenders>
<loggers>
<root level="ALL">
<!-- 过滤无用日志信息 -->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<!-- 配置哪些输出源生效 -->
<appender-ref ref="Console_Appender"/>
<appender-ref ref="File_Appender"/>
<appender-ref ref="File_ERROR_Appender"/>
<appender-ref ref="File_WARN_Appender"/>
<appender-ref ref="File_INFO_Appender"/>
<appender-ref ref="File_DEBUG_Appender"/>
</root>
</loggers>
</configuration>
4. 入门案例
- GitEE地址:https://gitee.com/ghzhouwei/JavaBase.git
4.1 项目结构
4.2 创建工程
- 基于MAVEN的项目
4.3 项目依赖
<dependencies>
<!-- log4j-api -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
<!-- log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
</dependencies>
4.4 测试文件
package com.intest.log4j2.test;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* @ClassName: Log4jTest
* @Description: 日志测试类
* @Author: zhouWei
* @Date: 2020-11-4 - 11:23
*/
public class Log4jTest
{
private static Logger logger= LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
public static void main(String[] args)
{
for(int i = 0; i < 30000; i++)
{
// 记录error级别的信息
logger.error("log4j2日志输出:This is error message.");
// 记录warn级别的信息
logger.warn("log4j2日志输出:This is warn message.");
// 记录info级别的信息
logger.info("log4j2日志输出:This is info message.");
// 记录debug级别的信息
logger.debug("log4j2日志输出:This is debug message.");
}
}
}
4.5 日志配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="error">
<!-- 定义日志输出根目录 -->
<Properties>
<Property name="LOG_HOME">D:/log</Property>
</Properties>
<appenders>
<!-- 日志输出到控制台 -->
<Console name="Console_Appender" target="SYSTEM_OUT">
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] 【%-5level】 %class{36} %L %M - %msg%xEx%n"/>
</Console>
<!-- 日志输出到文件 -->
<File name="File_Appender" fileName="${LOG_HOME}/logs/file.log" append="false">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] 【%-5level %class{36}】 %L %M - %msg%xEx%n"/>
</File>
<!-- 日志输出到文件(文件大小到达指定尺寸的时候产生一个新的文件) -->
<RollingFile name="File_ERROR_Appender" fileName="${LOG_HOME}/logs/ERROR.log" filePattern="${LOG_HOME}/logs/$${date:yyyy-MM}/ERROR-%d{yyyy-MM-dd}-%i.log.gz">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] 【%-5level】 %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingFile>
<!-- 日志输出到文件(文件大小到达指定尺寸的时候产生一个新的文件) -->
<RollingFile name="File_WARN_Appender" fileName="${LOG_HOME}/logs/WARN.log" filePattern="${LOG_HOME}/logs/$${date:yyyy-MM}/WARN-%d{MM-dd-yyyy}-%i.log.gz">
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] 【%-5level】 %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingFile>
<!-- 日志输出到文件(文件大小到达指定尺寸的时候产生一个新的文件) -->
<RollingFile name="File_INFO_Appender" fileName="${LOG_HOME}/logs/INFO.log" filePattern="${LOG_HOME}/logs/$${date:yyyy-MM}/INFO-%d{MM-dd-yyyy}-%i.log.gz">
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] 【%-5level】 %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingFile>
<!-- 日志输出到文件(文件大小到达指定尺寸的时候产生一个新的文件) -->
<RollingFile name="File_DEBUG_Appender" fileName="${LOG_HOME}/logs/DEBUG.log" filePattern="${LOG_HOME}/logs/$${date:yyyy-MM}/DEBUG-%d{MM-dd-yyyy}-%i.log.gz">
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] 【%-5level】 %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingFile>
</appenders>
<loggers>
<root level="ALL">
<!-- 过滤无用日志信息 -->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<!-- 配置哪些输出源生效 -->
<appender-ref ref="Console_Appender"/>
<appender-ref ref="File_Appender"/>
<appender-ref ref="File_ERROR_Appender"/>
<appender-ref ref="File_WARN_Appender"/>
<appender-ref ref="File_INFO_Appender"/>
<appender-ref ref="File_DEBUG_Appender"/>
</root>
</loggers>
</configuration>
4.6 运行测试
- 控制台打印了四种级别的日志
- D:\log\logs目录下面也生成了对应的日志文件和日志压缩文件