“凌晨三点的调试现场,曾续缘的技术锦囊准时上线!这是你今晚第N次搜索解决方案?点击关注,下次遇到难题直接来主页搜答案💡”
Java日志发展历程
Java日志系统的实现是Java生态系统中的一个重要组成部分,其发展历程反映了Java社区对日志记录需求的不断演进和改进。
-
早期Java日志(Java 1.0 - 1.3)
在Java的早期版本中,日志功能相对较弱,开发人员通常使用
System.out.println
或System.err.println
来打印信息,这种方法简单但缺乏灵活性,无法满足生产环境中对日志管理的需求。 -
Java Logging API(Java 1.4)
2002年,Java 1.4发布,引入了
java.util.logging
(JUL)作为Java标准库的一部分,这是Java官方提供的日志框架。JUL提供基本的日志功能,包括日志级别控制、日志处理器的配置等,但它在企业级应用中的表现并不十分突出,因为它的功能相对有限,且扩展性不如其他框架。 -
Log4j
在Java官方日志API出现之前,Log4j就已经成为Java社区广泛使用的日志框架。Log4j由Ceki Gülcü创建,提供了丰富的功能,如灵活的配置、多种日志输出目的地、以及强大的日志级别和过滤器设置。Log4j很快成为了Java日志的事实标准。
-
Commons Logging
为了解决不同日志框架之间的兼容性问题,Apache推出了Commons Logging(JCL)。它提供了一个日志抽象层,允许开发者使用统一的接口,底层可以切换不同的日志实现,如Log4j或JUL。
-
SLF4J 和 Logback
Ceki Gülcü创建了另一个日志抽象层——SLF4J(Simple Logging Facade for Java),以及一个新的实现——Logback。SLF4J与Commons Logging类似,但它提供了更好的类型安全,并且能够更简单地与实际的日志框架绑定。Logback作为Log4j的替代品,提供了更好的性能和更丰富的功能。
-
Log4j2
随着时间的推移,Log4j被升级为Log4j2,它提供了更快的性能、更好的API设计、以及强大的插件和配置系统。Log4j2在许多方面都是对Logback的直接改进,包括在锁的开销、垃圾收集的压力等方面。
-
现代日志框架的发展
随着微服务、容器化和云原生应用的发展,日志系统也不断进化。例如,日志系统开始支持结构化日志输出,便于与日志分析工具集成,支持日志的集中管理和分析。
日志抽象
Spring Boot的日志抽象由spring-boot-starter-logging
模块提供,它依赖于Logback和SLF4J。SLF4J是日志框架的抽象层,它允许我们在部署时切换具体的日志实现。而Logback是SLF4J的实现,也是Spring Boot的默认日志框架。
spring-boot-starter-logging
是 Spring Boot 的一个起步依赖(starter),它为 Spring Boot 应用程序提供了一套完整的日志解决方案。这个模块包含了日志框架的抽象层 SLF4J 和 Logback,它们共同构成了 Spring Boot 默认的日志框架。
SLF4J(Simple Logging Facade for Java)
SLF4J 是一个抽象层,它提供了统一的日志接口,允许开发者使用统一的编程接口,而无需关心底层的日志实现。这样做的目的是为了在部署时能够灵活地切换不同的日志框架,例如 Logback、Log4j 或 java.util.logging,而不需要修改代码。
SLF4J 的主要组件包括:
- SLF4J API:提供日志记录的接口和抽象类。
- SLF4J binding:将 SLF4J API 绑定到具体的日志实现框架上,如 logback-classic 或 log4j-over-slf4j。
- SLF4J bridges:允许将现有的日志框架(如 java.util.logging、Log4j)转换为 SLF4J,从而可以在不修改代码的情况下迁移到 SLF4J。
Logback
Logback 是 SLF4J 的原生实现,由 Log4j 的原作者 Ceki Gülcü 开发。Logback 提供了丰富的特性,包括自动重新加载配置文件、高级过滤器和格式化器等。Logback 的性能优于 Log4j,特别是在垃圾收集和同步开销方面。
Logback 的主要组件包括:
- logback-classic:实现了 SLF4J API,是 Logback 的核心模块。
- logback-core:提供了 Logback 的核心功能,如日志管理、过滤器、日志滚动等。
- logback-access:与 Servlet 容器集成,提供了 HTTP 访问日志的功能。
Spring Boot 中的日志抽象
在 Spring Boot 中,spring-boot-starter-logging
负责自动配置日志系统。当我们添加了这个起步依赖到我们的项目中时,Spring Boot 会自动配置 Logback 作为日志框架。Spring Boot 的自动配置会根据应用程序的属性和类路径上的库来配置日志系统。
Spring Boot 还提供了一个名为 LoggerFactory
的工厂类,它用于创建 Logger
实例。在运行时,LoggerFactory
会绑定到具体的日志实现上,例如 Logback。
使用 Spring Boot 日志抽象
在 Spring Boot 应用程序中,可以通过以下方式来使用日志:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MySpringBootApplication {private static final Logger logger = LoggerFactory.getLogger(MySpringBootApplication.class);public static void main(String[] args) {logger.info("Hello, World!");}
}
在这个例子中,我们通过 LoggerFactory
获取了一个 Logger
实例,然后使用它来记录一条信息级别的日志消息。由于 Spring Boot 默认使用 Logback,这个 Logger
实例实际上是 Logback 的实例。
通过这种方式,应用程序代码只需要与 SLF4J API 交互,而无需关心底层的日志实现。这使得应用程序更加灵活,可以在不修改代码的情况下切换到不同的日志框架。
日志配置
Spring Boot允许通过外部配置文件来配置日志,这些配置文件可以是application.properties
或application.yml
。我们可以在配置文件中设置日志级别、日志输出格式、日志文件路径等。
日志级别配置
在 Spring Boot 中,我们可以为不同的日志记录器(loggers)设置日志级别。日志级别决定了哪些日志消息会被记录下来。常见的日志级别包括:
TRACE
: 最详细的日志级别,用于诊断问题。DEBUG
: 用于调试和诊断问题。INFO
: 用于常规信息,默认级别。WARN
: 用于警告信息,表示潜在的问题。ERROR
: 用于错误信息,表示已经发生的问题。OFF
: 禁用所有日志输出。
在 application.properties
或 application.yml
文件中,我们可以这样设置日志级别:
application.properties
示例:
# 设置所有日志记录器的日志级别为 INFO
logging.level.root=INFO
# 设置 org.springframework.web 包的日志级别为 DEBUG
logging.level.org.springframework.web=DEBUG
# 设置 org.hibernate 包的日志级别为 ERROR
logging.level.org.hibernate=ERROR
application.yml
示例:
logging:level:root: INFOorg.springframework.web: DEBUGorg.hibernate: ERROR
日志输出格式配置
Spring Boot 允许开发者自定义日志输出格式。默认的日志输出格式通常包括以下信息:
- 时间戳:日志消息发生的时间。
- 日志级别:消息的级别,如 INFO、WARN 等。
- 进程 ID:正在运行的应用程序的进程 ID。
- 线程名:生成日志消息的线程名。
- 日志消息:实际的日志信息。
日志输出格式决定了日志消息在控制台或日志文件中的显示方式。Spring Boot 允许我们自定义日志输出格式。例如,在 application.properties
中:
# 设置日志输出格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
在 application.yml
中:
logging:pattern:console: '%d{yyyy-MM-dd HH:mm:ss} - %msg%n'file: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n'
在这里,%d
表示日期和时间,%msg
表示日志消息,%n
表示换行符,%thread
表示线程名,%-5level
表示日志级别,%logger{36}
表示日志记录器名。
日志文件配置
Spring Boot 还允许我们配置日志文件的位置和名称。例如:
# 设置日志文件名(如果不设置,默认为 spring.log)
logging.file.name=myapp.log
# 设置日志文件路径(如果同时设置了 logging.file.name,则此设置无效)
logging.file.path=/var/log
在 application.yml
中:
logging:file:name: myapp.logpath: /var/log
日志分组配置
日志分组功能允许我们将多个相关的日志记录器归为一个组,并为这个组设置统一的日志级别。例如:
# 定义一个名为 "Tomcat" 的日志组,包含两个日志记录器
logging.group.tomcat=org.apache.catalina,org.apache.coyote
# 设置 "Tomcat" 组的日志级别为 WARN
logging.level.tomcat=WARN
在 application.yml
中:
logging:group:tomcat:- org.apache.catalina- org.apache.coyotelevel:tomcat: WARN
自定义日志配置
如果需要更高级的日志配置,Spring Boot 支持自定义 Logback、Log4j2 或 Java Util Logging 的配置文件。
只需要在类路径下提供相应的配置文件,Spring Boot 就会自动加载它。
- 对于 Logback,我们可以提供
logback-spring.xml
或logback.xml
文件。 - 对于 Log4j2,我们可以提供
log4j2-spring.xml
或log4j2.xml
文件。 - 对于 Java Util Logging,我们可以提供
logging.properties
文件。
这些自定义配置文件允许我们进行更详细的配置,例如设置日志滚动策略、定义复杂的日志过滤规则等。
日志切分配置
日志切分是一种管理日志文件的技术,它允许我们根据时间或文件大小来分割日志文件。这样做的好处包括:
- 避免单个日志文件过大:大型的日志文件难以管理和分析。
- 便于日志归档:可以按时间顺序存储和检索日志文件。
- 方便日志轮换:可以定期清理旧的日志文件,以节省磁盘空间。
要在 Spring Boot 中配置日志切分,我们需要在 Logback 的配置文件中定义一个 RollingFileAppender
,并设置 SizeAndTimeBasedRollingPolicy
。
logback-spring.xml 示例:
<configuration><appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>myapp.log</file><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志文件的名字会根据时间和大小来命名 --><fileNamePattern>myapp-%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- 每个日志文件的最大大小 --><maxFileSize>100MB</maxFileSize><!-- 日志文件保留的最长时间,这里设置为30天 --><maxHistory>30</maxHistory><!-- 总的日志文件大小限制,这里设置为3GB --><totalSizeCap>3GB</totalSizeCap></rollingPolicy><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern></encoder></appender><root level="INFO"><appender-ref ref="ROLLING" /></root>
</configuration>
在这个配置中:
fileNamePattern
定义了日志文件的命名模式,包括日期和索引%i
。maxFileSize
设置了单个日志文件的最大大小,超过这个大小就会触发切分。maxHistory
设置了日志文件保留的最长时间,超过这个时间范围的日志文件将被删除。totalSizeCap
设置了所有日志文件的总大小限制,超过这个限制的日志文件将被删除。
日志切分的触发条件
日志切分会基于以下条件触发:
- 日志文件大小:当当前日志文件的大小超过
maxFileSize
指定的值时,日志文件会被切分。 - 时间:每天一个新的日志文件会被创建,这是通过
fileNamePattern
中的日期格式%d{yyyy-MM-dd}
实现的。
日志文件的命名和索引
在日志切分时,Logback 会在文件名中添加一个索引 %i
,以区分不同的日志文件。例如,如果我们的日志文件名为 myapp.log
,切分后的文件名可能为 myapp-2023-04-01.0.log
、myapp-2023-04-01.1.log
等。每个索引代表一个切分后的文件。
日志文件的清理
Logback 会根据 maxHistory
和 totalSizeCap
的设置来清理旧的日志文件。maxHistory
控制日志文件的保留时间,而 totalSizeCap
控制所有日志文件的总大小。
日志文件配置选项
配置元素类别 | 配置元素 | 描述 |
---|---|---|
根配置元素 | <configuration> | 配置文件的根元素。 |
配置文件属性 | <property> | 定义属性,可以在配置文件中复用。 |
配置文件变量 | <variable> | 定义变量,可以在配置文件中复用。 |
Appender | <appender> | 定义日志的输出目的地。 |
<ConsoleAppender> | 控制台输出。 | |
<FileAppender> | 文件输出。 | |
<RollingFileAppender> | 滚动文件输出。 | |
<SocketAppender> | 远程日志输出。 | |
<SMTPAppender> | 通过 SMTP 发送日志邮件。 | |
Encoder | <encoder> | 定义日志的格式。 |
<PatternLayout> | 定义日志输出的格式模式。 | |
RollingPolicy | <rollingPolicy> | 定义日志文件滚动策略。 |
<TimeBasedRollingPolicy> | 基于时间的滚动策略。 | |
<SizeAndTimeBasedRollingPolicy> | 基于大小和时间的滚动策略。 | |
TriggeringPolicy | <triggeringPolicy> | 定义日志文件触发的策略。 |
<SizeBasedTriggeringPolicy> | 基于文件大小的触发策略。 | |
Logger | <logger> | 定义特定包或类的日志级别。 |
<root> | 定义根日志级别。 | |
Filter | <filter> | 定义日志过滤器。 |
<ThresholdFilter> | 基于日志级别的过滤器。 | |
<RegexFilter> | 基于正则表达式的过滤器。 | |
<LevelFilter> | 基于日志级别的过滤器。 | |
转换器 | <conversionRule> | 定义自定义的转换规则。 |
以下是一个包含多个配置选项的 logback-spring.xml
示例:
<configuration><!-- 定义属性 --><property name="LOG_PATH" value="/home/generator"/><property name="LOG_NAME" value="generator.log"/><property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} - %msg%n"/><!-- 控制台输出 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${LOG_PATTERN}</pattern></encoder></appender><!-- 文件输出 --><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/${LOG_NAME}</file><encoder><pattern>${LOG_PATTERN}</pattern></encoder><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- 日志文件的名字会根据时间和大小来命名 --><fileNamePattern>${LOG_PATH}/log-archives/generator-%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- 每个日志文件的最大大小 --><maxFileSize>100MB</maxFileSize><!-- 日志文件保留的最长时间,这里设置为30天 --><maxHistory>30</maxHistory><!-- 总的日志文件大小限制,这里设置为3GB --><totalSizeCap>3GB</totalSizeCap></rollingPolicy></appender><!-- 根日志级别 --><root level="INFO"><appender-ref ref="FILE"/></root><!-- 特定包的日志级别 --><logger name="org.springframework.web" level="DEBUG"/><logger name="com.example" level="DEBUG"/><!-- 过滤器示例 --><appender name="FILTERED_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>logs/filtered.log</file><encoder><pattern>${LOG_PATTERN}</pattern></encoder><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>logs/filtered-%d{yyyy-MM-dd}.log</fileNamePattern></rollingPolicy><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender>
</configuration>
主要解释:
<pattern>
配置:这个配置使得日志输出时,每条日志消息都会以日期时间 - 日志消息
的格式显示,并且每条消息之间会换行。<file>
配置:应用程序启动时会创建一个名为 generator.log 的文件,位于 /home/generator/ 目录下。<fileNamePattern>
配置:这个配置表示日志文件会根据日期和索引进行滚动。例如,如果当前活动日志文件达到 100MB,它会被重命名为 generator-2023-10-01.0.log,然后创建一个新的 generator.log 文件继续记录日志。
日志系统切换
虽然Spring Boot默认使用Logback,但我们也可以切换到其他日志系统,如Log4j2。要做到这一点,我们需要从spring-boot-starter-logging
中排除Logback,并添加我们选择的日志系统的依赖。
在 Spring Boot 中,虽然 Logback 是默认的日志框架,但我们可以根据项目需求切换到其他日志系统,如 Log4j2。这种切换通常涉及到改变项目的依赖配置,以便排除默认的 Logback 依赖,并添加所选日志系统的依赖。
排除 Logback 依赖
首先,我们需要从 spring-boot-starter-logging
中排除 Logback 的依赖。这可以通过在项目的构建配置文件中(如 Maven 的 pom.xml
或 Gradle 的 build.gradle
)显式地排除 Logback 相关的依赖来实现。
Maven pom.xml
示例:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId><exclusions><exclusion><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></exclusion><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusion></exclusion></exclusions>
</dependency>
Gradle build.gradle
示例:
dependencies {implementation('org.springframework.boot:spring-boot-starter-logging') {exclude group: 'ch.qos.logback', module: 'logback-classic'}
}
添加 Log4j2 依赖
在排除 Logback 之后,我们需要添加 Log4j2 的依赖。Spring Boot 提供了一个名为 spring-boot-starter-log4j2
的起步依赖,它包含了 Log4j2 和必要的配置。
Maven pom.xml
示例:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
Gradle build.gradle
示例:
dependencies {implementation 'org.springframework.boot:spring-boot-starter-log4j2'
}
配置 Log4j2
一旦添加了 Log4j2 的依赖,Spring Boot 会自动配置 Log4j2 作为日志系统。我们可以通过在 application.properties
或 application.yml
文件中设置相应的属性来配置 Log4j2。
application.properties
示例:
logging.level.root=INFO
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR
注意事项
- 当我们切换到 Log4j2 时,确保移除了所有 Logback 的配置文件,如
logback.xml
或logback-spring.xml
。 - 如果我们使用的是 Log4j2 的 XML 或 YAML 配置文件,确保它们在类路径的根目录下。
- 在某些情况下,可能需要手动配置 Log4j2,例如在
log4j2.xml
文件中定义日志 appenders 和 loggers。
参考链接:
https://developer.aliyun.com/article/1090032
参考链接:https://cengxuyuan.cn