Java 日志
日志门面
:提供统一的日志输出接口。日志实现
:具体实现日志输出的代码。
日志门面与实现框架
- 使用 日志门面 日志实现框架 的方式,是为了:低耦合,日志的实现与业务代码通过 日志门面连接,在后续修改日志实现时,无需更改业务代码。
- 这是 门面设计模式(外观设计模式)的典型应用。
日志门面
SLF4j(Simple Logging Facade For Java)
:一个为 Java 程序提供的统一日志输出接口,就是一个接口,JCL(Jaka Commons Logging, Apache Commons Logging)
:Apache 提供的一个日志门面,提供统一的对外接口。
日志实现框架
JUL(Java util Logging)
:Java 原生的日志框架,使用时不需要引用第三方类库,使用方便。
- 7 个日志级别(从高到低):**SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST**。
代码语言:txt复制- 同时还有 **OFF、ALL 两个特别的日志级别,用来 关闭/打开 所有的日志**。
log4j
:Apache 的一个开源项目。
- 7 个日志级别(从高到低):**OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL**。
日志级别 | 日志介绍 |
---|---|
OFF | 最高日志级别,关闭所有日志 |
FATAL | 将会导致引用程序退出的错误 |
ERROR | 发生错误事件,但仍不影响系统的继续运行 |
WARN | 警告,存在潜在的错误 |
INFO | 一般用在粗粒度级别上,强调应用程序的运行全程 |
DEBUG | 一般用在细粒度级别上,用于调试应用程序 |
ALL | 最低日志级别,打开所有日志 |
log4j2
:log4j 的升级版,参考了 logback 的设计,同时进行了问题修复。
- **异常优化**:提供了一些异常处理机制,来解决在 logback 中,应用无法感知到 Appener 异常。
代码语言:txt复制- **性能提升**:相较于 log4j 和 logback,性能都有明显的提升。
代码语言:txt复制- **自动重载配置**:参考 logback 的参数修改自动更新机制,提供自动刷新参数的设置。
代码语言:txt复制- **无垃圾机制**:可以使用其设计的一套无垃圾机制(对象重用、内存缓冲),避免频繁的日志记录导致 JVM gc 压力过大。
logback
:SpringBoot 默认的日志框架。
- 由三个模块组成:
代码语言:txt复制 - logback-core:logback 核心包,开发人员可以以次为基础搭建自身模块。
代码语言:txt复制 - logback-classic:**logback 对于 SLF4j 的实现,其中依赖了 logback-core 包**。
代码语言:txt复制 - logback-access:集成 Servlet 容器,实现 HTTP 访问日志的功能。
代码语言:txt复制- **可以输出日志到文件、数据库、控制台中,还可以将日志文件进行压缩,功能很丰富**。
代码语言:txt复制- 日志级别(从高到低):**FATAL、ERROR、WARNING、INFO、DEBUG、TRACE**。
日志级别 | 日志介绍 |
---|---|
TRACE | 在线调试,默认不输出到控制台和文件 |
DEBUG | 在线调试、终端查看,默认输出到控制台,用于开发者查看日志流水 |
INFO | 报告程序进度、查看程序状态,用于跟踪程序进展 |
WARNING | 警告,程序出现错误,但是程序可以恢复,程序仍是正常状态 |
ERROR | 错误,程序发生错误后还可以运行,但是程序极有可能处于非正常状态,功能可能无法全部完成 |
FATAL | 致命错误,程序必须马上终止 |
总结
- 日志门面和实现框架的面世时间(从早到晚):Log4j -> JUL -> JCL -> SLF4j -> Logback -> Log4j2。
JCL 门面
优先寻找 Log4j 实现,退而求次则是 JUL 实现,最后才会使用内部提供的 SimpleLog 实现。- 而,
SLF4j 门面
是作为一个纯粹的日志门面,提供了 SLF4j 桥接器将 SLF4j 桥接到 log4j、JUL 实现去做日志输出。 - 后续 log4j 无法满足高性能要求后,SLF4j 制作者根据 SLF4j 接口写出了 logback 日志实现框架。
- log4j2 是 Apache 全面借鉴 SLF4j Logback 后推出的,添加了很多新的特性,还做了分离式设计。
推荐使用 SLF4j logback 的方式去做 Java 的日志输出
。
- 优点一:**logback 中实现 SLF4j 门面,在 Java 程序中直接引入 logback-classic 的依赖即可**。
代码语言:txt复制- 优点二:**SpringBoot 使用 logback 作为默认的日志实现**,在 SpringBoot 项目中可以直接使用。
SLF4j Logback 的实现
第一步:添加配置文件
Logback 框架
可以自动识别 */classes/ 下的logback.xml
文件。
- 在 SpringBoot 框架下,Logback 框架还可以自动识别 */classes/ 下的 ``logback-spring.xml`` 文件。
- 同样,
Log4j/Log4j2 框架
可以自动识别 */classes/ 下的log4j.xml/log4j2.xml
文件。
- 在 SpringBoot 框架下,Log4j2 框架还可以自动识别 */classes/ 下的 ``log4j2-spring.xml`` 文件。
JUL 框架
可以自动识别 */classes/ 下的 logging.properties 文件。- 将配置文件放在 src/main/resources 下,项目构建时,文件就会加载到 */classes/ 下了。
logback.xml
代码语言:html复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="10000">
<!-- scan 属性:当此属性设置为 true 时,配置文件如果发生改变,将会被重新加载,默认值为 true -->
<!--
scanPeriod属性:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
当 scan 为 true 时,此属性生效。默认的时间间隔为 1 分钟。
-->
<!-- debug 属性:当此属性设置为 true 时,将打印出 logback 内部日志信息,实时查看 logback 运行状态。默认值为 false。 -->
<!--
日志格式设置标识符:
%n:换行符;
%level:日志级别;
%-5level:日志级别(使用5个字符,并靠左对齐);
%msg、%m:日志消息(简写是:%m);
%logger、%M:日志输出者(就是哪个类做这个日志输出,这个应用是 Log 类);
%c:日志输出者输出日志时使用的方法;
%L:日志输出者输出日志时使用的方法中具体的行数;
%d{yyyy-MM-dd HH:mm:ss}:时间,大括号内的是时间格式,默认格式是 2023-02-27 15:15:01,877;
%thread:输出日志的进程名字;
-->
<!-- 日志 输出格式 1 -->
<property name="layout1" value="[%level] -- %m -- [%d{yyyy-MM-dd HH:mm:ss}] %c %M %L [%thread] %n"/>
<!-- 日志 输出格式 2 -->
<property name="layout2"
value="[%-5level] - %d{yyyy-MM-dd HH:mm:ss} - %c:%M:%L %n[%-5level] - [%thread] - %msg %n%n"/>
<!-- 输出格式 3(输出到 *.html 文件,不需要间隔) -->
<property name="layout3"
value="%level%d{yyyy-MM-dd HH:mm:ss}%c%M%L%thread%msg"/>
<!-- 文件输出时,文件的路径 -->
<property name="filePath" value="D:/file/projects/files/Java/journal/logback/"/>
<!-- 输出颜色: 红 -->
<property name="red" value="System.err"/>
<!-- 输出颜色: 黑 -->
<property name="black" value="System.out"/>
<!-- 每天的日期 String 串,用来生成文件夹 -->
<timestamp key="datetime" datePattern="yyyy-MM-dd"/>
<!--
配置 输出控制器:consoleAppender,控制台输出
-->
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<!-- 颜色 -->
<target>
${red}
</target>
<!-- 输出格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
${layout1}
</pattern>
</encoder>
</appender>
<!--
配置 输出控制器:consoleAppender1,控制台输出
-->
<appender name="consoleAppender1" class="ch.qos.logback.core.ConsoleAppender">
<!-- 颜色 -->
<target>
${red}
</target>
<!-- 输出格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
${layout2}
</pattern>
<!-- 设置字符集 -->
<charset>
UTF-8
</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
配置 输出控制器:consoleFilterAppender,控制台输出
在 consoleAppender 的基础上 添加 过滤器
-->
<appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
<!-- 颜色 -->
<target>
<!-- ${red} -->
${black}
</target>
<!-- 输出格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
${layout2}
</pattern>
</encoder>
<!-- 配置过滤器 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 设置 级别 -->
<level>DEBUG</level>
<!--
功能是:仅记录 设定好的级别的日志,可以用来设置 不同的日志输出到不同的 日志文件中;
当然,将配置值进行 对调后,就可以反向屏蔽(屏蔽指定级别的日志)
-->
<!-- 设置 级别 与 设置的级别 匹配时 就 打印 -->
<onMatch>ACCEPT</onMatch>
<!-- 设置 级别 与 设置的级别 不匹配时 就 屏蔽 -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
配置 输出控制器:htmlFileAppender,输出到 .html 文件
-->
<appender name="htmlFileAppender" class="ch.qos.logback.core.FileAppender">
<!-- 文件位置 -->
<file>
${filePath}/${datetime}/logback-html.html
</file>
<!-- 输出格式 -->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>
${layout3}
</pattern>
</layout>
</encoder>
</appender>
<!--
配置 输出控制器:fileAppender,输出到 .log 文件
-->
<appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
<!-- 文件位置 -->
<file>
${filePath}/${datetime}/logback-file.log
</file>
<!-- 输出格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
${layout2}
</pattern>
</encoder>
</appender>
<!--
配置 输出控制器:rollingFileAppender,输出到 .log 文件,可以 拆分 和 压缩
-->
<appender name="rollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--
文件位置
${filePath}:是文件存放目录路径,即:D:/file/projects/files/Java/journal/logback/
${datetime}:是在文件存放目录路径下的文件夹名称,即:yyyy-MM-dd
logback-rollingFile.log:文件名(包括后缀)
-->
<file>
${filePath}/${datetime}/logback-rollingFile.log
</file>
<!-- 输出格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
${layout2}
</pattern>
<!-- 设置字符集 -->
<charset>
UTF-8
</charset>
</encoder>
<!-- 指定拆分规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 默认格式:gz,-->
<!-- 声明文件名:按照 时间和格式 来 -->
<fileNamePattern>
<!-- %d{yyyy-MM-dd}是时间,%i 是防止同一天的文件重名(而 压缩包 有 *.gz、*.zip 等等) -->
${filePath}/${datetime}/logback-rollingFile%i.%d{yyyy-MM-dd}.log.gz
</fileNamePattern>
<!-- 单个文件最大的的大小:按照 文件大小 来拆分文件 -->
<maxFileSize>
10MB
</maxFileSize>
<!-- 文件数目(单位:个):大于 文件数目后 将会覆盖更早的日志压缩文件(不建议添加) -->
<!-- <minIndex>1</minIndex>-->
<!-- <maxIndex>3</maxIndex>-->
<!-- 文件保存最长时间(单位:天):过早的日志压缩文件将会删除 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<!-- 配置异步 appender-->
<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<!-- 导入实际的 Appender -->
<appender-ref ref="consoleAppender"/>
<!--
注意:这两个属性不能乱配置
1、<discardingThreshold></discardingThreshold>
阈值,
当队列剩余容量小于阈值时,trace debug info 这三个级别的日志将被抛弃,
默认值是:-1
2、<queueSize></queueSize>
队列的深度
默认值是:256
-->
</appender>
<!--
配置 日志记录器:root Logger
-->
<root level="ALL">
<!-- 引入 Appender -->
<appender-ref ref="asyncAppender"/>
<appender-ref ref="fileAppender"/>
</root>
<!-- 自定义 Logger -->
<logger name="com.domain.LogTool" level="trace" additivity="false">
<!--
additivity="false" 表示 不继承 rootLogger
-->
<!-- 引入 Appender -->
<appender-ref ref="consoleAppender1"/>
<appender-ref ref="htmlFileAppender"/>
<appender-ref ref="rollingFileAppender"/>
</logger>
</configuration>
- 注意:
自定义的 Logger 中 name="com.domain.LogTool"
,那么 LoggerFactory.getLogger(LogTool.class); 中传入 LogTool.java
时,才会使用自定义的 Logger
。 其他情况都是使用默认的 Logger(RootLogger)
。- 当然,
自定义的 Logger 中 name="com.domain"
时,LoggerFactory.getLogger(LogTool.class); 中传入 com.domain 内的类
时,也会使用自定义的 Logger
。
LogTool.java 工具类(非必要)
代码语言:java复制public class LogTool {
private static Logger LOGGER = LoggerFactory.getLogger(Log.class);
/**
* 添加自定义的 Logger 包下的 类,使用自定义的 Logger
*
* @param c 类的 Class 对象
* @param <T> 泛型
*/
public static <T> void setClassC(Class<T> c) {
if (c != null) {
LOGGER = LoggerFactory.getLogger(c);
}
}
/**
* 使用自定义的 Logger
*
* @param LOGGER Logger
*/
public static void setLOGGER(Logger LOGGER) {
Log.LOGGER = LOGGER;
}
/**
* 输出 日志信息(类对象版)
*
* @param object 类对象
* @param logLevel 日志级别
*/
public static void outputLog(Object object, LogLevel logLevel) {
switch (logLevel) {
case Trace:
LOGGER.trace("追踪 消息 - {}", object);
break;
case Debug:
LOGGER.debug("详细 消息 - {}", object);
break;
case Info:
LOGGER.info("关键 消息 - {}", object);
break;
case Warn:
LOGGER.warn("警告 消息 - {}", object);
break;
case Error:
LOGGER.error("错误 消息 - {}", object);
break;
default:
break;
}
}
/**
* 输出 日志信息(信息版)
*
* @param message 消息
* @param logLevel 日志级别
*/
public static void outputLog(String message, LogLevel logLevel) {
switch (logLevel) {
case Trace:
LOGGER.trace("追踪 消息 - { " message " }");
break;
case Debug:
LOGGER.debug("详细 消息 - { " message " }");
break;
case Info:
LOGGER.info("关键 消息 - { " message " }");
break;
case Warn:
LOGGER.warn("警告 消息 - { " message " }");
break;
case Error:
LOGGER.error("错误 消息 - { " message " }");
break;
default:
break;
}
}
/**
* Log 级别 枚举
*/
public enum LogLevel {
/**
* 追踪
*/
Trace,
/**
* 调试、详细
*/
Debug,
/**
* 关键、消息
*/
Info,
/**
* 警告
*/
Warn,
/**
* 错误
*/
Error
}
}
依赖导入
Java 程序
Logback-classic 依赖包含了 SLF4j 和 Logback-core 的依赖
,导入 Logback-classic 一个就可以了。
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
SpringBoot 项目
spring-boot-starter 依赖中包含了 SLF4j 和 Logback
,无需导入,当然也可以导入新的 logback 覆盖默认的版本。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.4.5</version>
</dependency>
<!--Spring boot Web容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.4.5</version>
<scope>test</scope>
</dependency>
</dependencies>
测试
代码语言:html复制<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
代码语言:java复制public class LogbackTest {
/**
* 测试 工具类 Log
*/
@Test
public void test() {
/**
* root Logger
*/
Log.setLOGGER(LoggerFactory.getLogger(LogbackTest.class));
for (int i = 0; i < 100; i ) {
Log.outputLog("trace", Log.LogLevel.Trace);
Log.outputLog("debug", Log.LogLevel.Debug);
Log.outputLog("info", Log.LogLevel.Info);
Log.outputLog("warn", Log.LogLevel.Warn);
Log.outputLog("error", Log.LogLevel.Error);
}
}
/**
* 测试 工具类 Log
*/
@Test
public void test1() {
/**
* 自定义 Logger
*/
for (int i = 0; i < 100; i ) {
Log.outputLog("trace", Log.LogLevel.Trace);
Log.outputLog("debug", Log.LogLevel.Debug);
Log.outputLog("info", Log.LogLevel.Info);
Log.outputLog("warn", Log.LogLevel.Warn);
Log.outputLog("error", Log.LogLevel.Error);
}
}
}
其他的日志门面与实现框架
- 第二建议使用的日志框架是:SLF4j Log4j2。
- 相应的源码可以到 Gitee 上查看,地址:LJM/journal