目录
- 日志门面与实现框架
- 日志门面
- 日志实现框架
- 总结
- SLF4j + Logback 的实现
- 第一步:添加配置文件
- logback.xml
- LogTool.java 工具类(非必要)
- 依赖导入
- Java 程序
- SpringBoot 项目
- 测试
- 其他的日志门面与实现框架
-
日志门面
:提供统一的日志输出接口。 -
日志实现
:具体实现日志输出的代码。
日志门面与实现框架
-
使用 日志门面+日志实现框架 的方式,是为了:低耦合,日志的实现与业务代码通过 日志门面连接,在后续修改日志实现时,无需更改业务代码。
-
这是 门面设计模式(外观设计模式)的典型应用。
日志门面
-
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 的一个开源项目。- 7 个日志级别(从高到低):OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL。
日志级别 日志介绍 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 项目中可以直接使用。
-
SLF4j + Logback 的实现
第一步:添加配置文件
-
Logback 框架
可以自动识别 */classes/ 下的logback.xml
文件。- 在 SpringBoot 框架下,Logback 框架还可以自动识别 */classes/ 下的
logback-spring.xml
文件。
- 在 SpringBoot 框架下,Logback 框架还可以自动识别 */classes/ 下的
-
同样,
Log4j/Log4j2 框架
可以自动识别 */classes/ 下的log4j.xml/log4j2.xml
文件。- 在 SpringBoot 框架下,Log4j2 框架还可以自动识别 */classes/ 下的
log4j2-spring.xml
文件。
- 在 SpringBoot 框架下,Log4j2 框架还可以自动识别 */classes/ 下的
-
JUL 框架
可以自动识别 */classes/ 下的 logging.properties 文件。 -
将配置文件放在 src/main/resources 下,项目构建时,文件就会加载到 */classes/ 下了。
logback.xml
<?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-ddlogback-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 这三个级别的日志将被抛弃,默认值是:-12、<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 工具类(非必要)
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>
测试
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>RELEASE</version><scope>test</scope></dependency>
</dependencies>
public class LogbackTest {/*** 测试 工具类 Log*/@Testpublic 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*/@Testpublic 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