日志门面
:提供统一的日志输出接口。
日志实现
:具体实现日志输出的代码。
使用 日志门面+日志实现框架 的方式,是为了:低耦合,日志的实现与业务代码通过 日志门面连接,在后续修改日志实现时,无需更改业务代码。
这是 门面设计模式(外观设计模式)的典型应用。
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。
同时还有 OFF、ALL 两个特别的日志级别,用来 关闭/打开 所有的日志。
log4j
:Apache 的一个开源项目。
日志级别 | 日志介绍 |
---|---|
OFF | 最高日志级别,关闭所有日志 |
FATAL | 将会导致引用程序退出的错误 |
ERROR | 发生错误事件,但仍不影响系统的继续运行 |
WARN | 警告,存在潜在的错误 |
INFO | 一般用在粗粒度级别上,强调应用程序的运行全程 |
DEBUG | 一般用在细粒度级别上,用于调试应用程序 |
ALL | 最低日志级别,打开所有日志 |
log4j2
:log4j 的升级版,参考了 logback 的设计,同时进行了问题修复。
异常优化:提供了一些异常处理机制,来解决在 logback 中,应用无法感知到 Appener 异常。
性能提升:相较于 log4j 和 logback,性能都有明显的提升。
自动重载配置:参考 logback 的参数修改自动更新机制,提供自动刷新参数的设置。
无垃圾机制:可以使用其设计的一套无垃圾机制(对象重用、内存缓冲),避免频繁的日志记录导致 JVM gc 压力过大。
logback
:SpringBoot 默认的日志框架。
由三个模块组成:
logback-core:logback 核心包,开发人员可以以次为基础搭建自身模块。
logback-classic:logback 对于 SLF4j 的实现,其中依赖了 logback-core 包。
logback-access:集成 Servlet 容器,实现 HTTP 访问日志的功能。
可以输出日志到文件、数据库、控制台中,还可以将日志文件进行压缩,功能很丰富。
日志级别(从高到低):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 的依赖即可。
优点二:SpringBoot 使用 logback 作为默认的日志实现,在 SpringBoot 项目中可以直接使用。
Logback 框架
可以自动识别 */classes/ 下的 logback.xml
文件。
logback-spring.xml
文件。同样,Log4j/Log4j2 框架
可以自动识别 */classes/ 下的 log4j.xml/log4j2.xml
文件。
log4j2-spring.xml
文件。JUL 框架
可以自动识别 */classes/ 下的 logging.properties 文件。
将配置文件放在 src/main/resources 下,项目构建时,文件就会加载到 */classes/ 下了。
<configuration scan="true" scanPeriod="10000">
<property name="layout1" value="[%level] -- %m -- [%d{yyyy-MM-dd HH:mm:ss}] %c %M %L [%thread] %n"/>
<property name="layout2"
value="[%-5level] - %d{yyyy-MM-dd HH:mm:ss} - %c:%M:%L %n[%-5level] - [%thread] - %msg %n%n"/>
<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"/>
<timestamp key="datetime" datePattern="yyyy-MM-dd"/>
<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>
<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>DEBUGlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
<target>
${black}
target>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
${layout2}
pattern>
encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUGlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<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>
<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>
<appender name="rollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<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">
<fileNamePattern>
${filePath}/${datetime}/logback-rollingFile%i.%d{yyyy-MM-dd}.log.gz
fileNamePattern>
<maxFileSize>
10MB
maxFileSize>
<maxHistory>30maxHistory>
rollingPolicy>
appender>
<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="consoleAppender"/>
appender>
<root level="ALL">
<appender-ref ref="asyncAppender"/>
<appender-ref ref="fileAppender"/>
root>
<logger name="com.domain.LogTool" level="trace" additivity="false">
<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
。
public class LogTool {
private static Logger LOGGER = LoggerFactory.getLogger(Log.class);
/**
* 添加自定义的 Logger 包下的 类,使用自定义的 Logger
*
* @param c 类的 Class 对象
* @param 泛型
*/
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
}
}
Logback-classic 依赖包含了 SLF4j 和 Logback-core 的依赖
,导入 Logback-classic 一个就可以了。
<dependencies>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
dependencies>
spring-boot-starter 依赖中包含了 SLF4j 和 Logback
,无需导入,当然也可以导入新的 logback 覆盖默认的版本。
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.4.5version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.4.5version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<version>2.4.5version>
<scope>testscope>
dependency>
dependencies>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>RELEASEversion>
<scope>testscope>
dependency>
dependencies>
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