在本篇文章中,我们将聚焦于 Spring Boot 应用的日志记录(Logback配置),探讨错误日志机制、事件格式及日志事件中捕获的信息类型。同时,我们还会介绍如何配置日志属性,例如日志文件命名、基于大小或时间的文件滚动策略,以及如何在 Spring Boot 应用中集成这些配置。
日志是任何应用的重要组成部分,特别是在生产环境中,它有助于监控、故障排除和审计。在 Spring Boot 应用中使用 Logback 时,遵循最佳实践可以确保日志既有用又高效,同时避免潜在的安全风险。
最佳实践:
避免在日志中记录用户密码、信用卡号或个人身份信息(PII),以防止安全风险。
最佳实践:
为了防止日志文件过大影响性能和磁盘空间,推荐使用基于大小和时间的日志滚动策略。
最佳实践: 使用 SizeAndTimeBasedRollingPolicy
配置日志文件滚动策略,例如:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/tmp/logs/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>100</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
同步日志记录会阻塞主线程,影响应用性能。使用异步日志可以缓解这一问题。
最佳实践:
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE"/>
<queueSize>5000</queueSize>
<discardingThreshold>0</discardingThreshold>
</appender>
日志应当提供足够的上下文信息,使其易于理解和排查问题。
最佳实践:
示例:
log.error("订单处理失败,用户ID: {}, 订单ID: {}", userId, orderId, exception);
通过 logback-spring.xml
或外部属性文件管理日志配置,使其易于调整。
最佳实践:
logback-spring.xml
定义日志行为。在微服务架构中,集中式日志管理有助于快速定位问题。
最佳实践:
SyslogAppender
或 Logstash TCP appender
。MDC 允许在日志中自动插入请求 ID、用户 ID 等上下文信息,适用于多线程环境。
最佳实践:
MDC.put( "requestId" , requestId);
log.info( "正在处理请求" );
MDC.clear();
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{requestId}] - %msg%n</pattern>
最佳实践:
.gz
),节省磁盘空间。结构化日志便于搜索和分析,尤其适用于日志聚合工具(如 ELK、Splunk)。
示例(JSON 格式日志配置):
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<loggerName/>
<logLevel/>
<message/>
<threadName/>
<mdc/>
</providers>
</encoder>
</appender>
本篇文章主要介绍了 Logback 在 Spring Boot 3.3.4 中的最佳实践,下一篇(第4部分)将探讨如何利用 AOP(面向切面编程)来优化日志管理。
让我们将重点转移到在生产级应用程序中实施这些最佳实践。我们将探讨如何设置必要的依赖项和配置,以确保这些实践有效集成并发挥作用。
<!-- 日志记录 ========================================================== -->
< slf4j.version > 2.0.16 </ slf4j.version >
< logback.version > 1.5.8 </ logback.version >
< logstash.version > 8.0 </ logstash.version >
< janino.version > 3.1.12 </ janino.version >
< micrometer.version > 1.13.5 </ micrometer.version >
<!-- ================================================================= -->
<!-- SLF4J
/ LogBack / LogStash / Janino
/ Micrometer
/ Actuator
-->
<!-- ================================================================= -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.logstash.logback/logstash-logback-encoder -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>${logstash.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.codehaus.janino/janino -->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>${janino.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-core -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>${micrometer.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-prometheus -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>${micrometer.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring.boot.version}</version>
</dependency>
概括
现在让我们检查一下 Spring Boot “ application.properties” 文件中定义的服务和服务器属性,以及它们如何在 Logback Spring XML 配置中应用。此配置管理控制台、纯文本文件和基于 JSON 的文件的输出。此外,我们将探讨如何使用应用程序属性 logsing.format 在纯文本和 JSON 日志格式之间动态切换。
# 各个组件的日志级别
logging.level.org.springframework.boot.web.servlet =
DEBUGlogging.level.org.springframework.web.servlet.DispatcherServlet =
DEBUGlogging.level.org.springframework.security.web.FilterChainProxy =DEBUG
# 根级别日志
logging.level.root =INFO
# 指定 logback 配置文件
logging.config =classpath:logback-spring.xml
# 日志文件路径和滚动策略
logging.path =/tmp/logs/ ${service.api.name}
logging.file.name = ${service.api.name} .v ${server.version} .log
# 基于大小和时间的文件滚动名称的日志文件模式
logging.pattern.rolling-file-name = ${logging.path} / ${service.api.name} .v ${server.version} -%d{yyyy-MM-dd}-%i.log
# 最大单个文件大小 = 10 MBlogging.file.max
-size = 10 MB
# 日志的保留和总大小上限
# 保留日志文件 100 天,最大上限为 3 GB
login.file.max-history = 100
login.file.total-size-cap = 3 GB
logs.level.org.springframework.boot.web.servlet =DEBUG
logs.level.org.springframework.web.servlet.DispatcherServlet =DEBUG
logs.level.org.springframework.security.web.FilterChainProxy =DEBUG
日志记录.级别.org.springframework.boot.web.servlet=DEBUG
日志记录.级别.org.springframework.web.servlet.DispatcherServlet=DEBUG
日志记录.级别.org.springframework.安全.web.FilterChainProxy=DEBUG
日志记录.级别.根=INFO
logging.config =classpath:logback-spring.xml
logging.path=/tmp/logs/${service.api.name}
logging.file.name=${service.api.name}.v${server.version}
logging.path=/tmp/logs/${service.api.name}
logging.file.name=${service.api.name}.v${server.version}
logs.pattern.rolling-file-name = ${logging.path} / ${logging.file.name} -%d{yyyy-MM-dd}-%i
logging.file.max-size=10MB
logging.file.max-history=100
logging.file.total-size-cap=3GB
logging.file.max-history=100:
logging.file.total-size-cap=3GB:
通过使用这些属性,应用程序可以实现高效的日志管理,在捕获足够的信息和确保日志不会不受控制地增长之间取得平衡,从而导致潜在的磁盘空间问题。
此 logback-spring.xml 配置为 Spring Boot 应用程序定义了动态日志记录设置,允许使用应用程序配置(application.properties 或 application.yml)中定义的属性在JSON和纯文本日志记录格式之间切换。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="120 seconds">
<include resource="logback-spring-scope.xml"/>
<include resource="logback-spring-appenders-text.xml"/>
<include resource="logback-spring-appenders-json.xml"/>
<!-- 定义条件记录器 -->
<!-- 如果日志格式 == JSON -->
<if condition='property("LOG_FORMAT").contains("json")'>
<then>
<root level="INFO">
<appender-ref ref="STDOUT_JSON"/>
<appender-ref ref="ASYNC_JSON"/>
</root>
<logger level="info" name="ms-service-info">
<appender-ref ref="STDOUT_JSON"/>
<appender-ref ref="ASYNC_JSON"/>
</logger>
<logger level="debug" name="io.fusion.air.microservice.adapters.security">
<appender-ref ref="STDOUT_JSON"/>
<appender-ref ref="ASYNC_JSON"/>
</logger>
<logger level="trace" name="io.fusion.air.microservice.security.JsonWebTokenValidator">
<appender-ref ref="STDOUT_JSON"/>
<appender-ref ref="ASYNC_JSON"/>
</logger>
</then>
<!-- 如果日志格式 == PLAIN -->
<else>
<root level="INFO">
<appender-ref ref="STDOUT_TEXT"/>
<appender-ref ref="ASYNC_TEXT"/>
</root>
<logger level="info" name="ms-service-info">
<appender-ref ref="STDOUT_TEXT" />
<appender-ref ref="ASYNC_TEXT" />
</logger>
<logger level="debug" name="io.fusion.air.microservice.adapters.security">
<appender-ref ref="STDOUT_TEXT" />
<appender-ref ref="ASYNC_TEXT" />
</logger>
<logger level="trace" name="io.fusion.air.microservice.security.JsonWebTokenValidator">
<appender-ref ref="STDOUT_TEXT" />
<appender-ref ref="ASYNC_TEXT" />
</logger>
</else>
</if>
</configuration>
logback-spring.xml配置由多个文件组成,包括:
让我们逐一介绍每个部分并解释其目的:
<configuration scan="true" scanPeriod="120 seconds">
<included>
<springProperty scope="context" name="LOG_PATH" source="logging.path"/>
<springProperty scope="context" name="LOG_FILE" source="logging.file.name"/>
<springProperty scope="context" name="LOG_FILE_PATTERN" source="logging.pattern.rolling-file-name"/>
<springProperty scope="context" name="LOG_FILE_MAX_SIZE" source="logging.file.max-size"/>
<springProperty scope="context" name="LOG_FILE_MAX_HISTORY" source="logging.file.max-history"/>
<springProperty scope="context" name="LOG_FILE_SIZE_CAP" source="logging.file.total-size-cap"/>
<springProperty scope="context" name="LOG_FORMAT" source="logging.format"/>
</included>
这些 springProperty 元素从应用程序的 application.properties 或环境变量中提取值。
控制台 JSON 附加器(所有附加器均在 <included> 标签内)
<appender name="STDOUT_JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<loggerName/>
<logLevel/>
<message/>
<threadName/>
<mdc/>
</providers>
</encoder>
</appender>
文件 JSON 附加器(所有附加器均位于 <included> 标签内)
<appender name="FILE_JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${LOG_FILE}.json</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE_PATTERN}.json</fileNamePattern>
<maxFileSize>${LOG_FILE_MAX_SIZE}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY}</maxHistory>
<totalSizeCap>${LOG_FILE_SIZE_CAP}</totalSizeCap>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<loggerName/>
<logLevel/>
<message/>
<threadName/>
<mdc/>
</providers>
</encoder>
</appender>
LogStash JSON 搜索器 — logback-spring-appenders-json-logstash.xml
<!-- 定义 Logstash TCP Appender -->
<appender name="TCP_JSON" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!-- 您的 Logstash 服务器的地址 -->
<destination>logstash-host:5000</destination> <!-- 更改为您的 Logstash 主机和端口 -->
<!-- 可选:JSON 格式的编码器 -->
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp />
<loggerName />
<threadName />
<logLevel />
<message />
<context />
<mdc />
<stackTrace />
</providers>
</encoder>
<!-- 可选:如果 Logstash 暂时无法访问,则继续重试 -->
<connectionStrategy class="net.logstash.logback.appender.retry.ConnectionStrategy">
<minDelay>5000</minDelay> <!-- 5 秒后重试 -->
<maxDelay>60000</maxDelay> <!-- 最大重试延迟为 60 秒 -->
</connectionStrategy>
<!-- 可选:设置连接丢失时的重新连接延迟 -->
<reconnectionDelay>5000</reconnectionDelay> <!-- 如果断开连接,每 5 秒重新连接一次 -->
<!-- 可选:设置队列大小 -->
<queueSize>5000</queueSize> <!-- 如果 Logstash 不可用,则将事件记录到队列 -->
</appender>
控制台纯文本附加器(所有附加器均位于 <included> 标签内)
<appender name="STDOUT_TEXT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} |Service=%X{Service} |%-5level| %logger{36}:%L |IP=%X{IP}:%X{Port}| Proto=%X{Protocol}| URI=%X{URI}| User=%X{user}| ReqID=%X{ReqId}| %msg%n</pattern>
</encoder>
</appender>
文件纯文本附加器(所有附加器均在 <included> 标签内)
<appender name="FILE_TEXT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${LOG_FILE}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE_PATTERN}.log</fileNamePattern>
<maxFileSize>${LOG_FILE_MAX_SIZE}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY}</maxHistory>
<totalSizeCap>${LOG_FILE_SIZE_CAP}</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} |Service=%X{Service} |%-5level| %logger{36}:%L |IP=%X{IP}:%X{Port}| Proto=%X{Protocol}| URI=%X{URI}| User=%X{user}| ReqID=%X{ReqId}| %msg%n</pattern>
</encoder>
</appender>
使用异步日志记录,确保日志写入不会阻塞主线程,从而提高性能。
异步文本记录器(所有附加器均位于 <included> 标签内)
<appender name="ASYNC_TEXT" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE_TEXT" />
<queueSize>5000</queueSize>
<discardingThreshold>0</discardingThreshold>
</appender>
异步 JSON 记录器(所有附加器均位于 <included> 标签内)
配置根据logging.format属性(在application.properties文件中)有条件地在JSON和纯文本日志记录之间切换。
如果格式为 JSON
<if condition='property("LOG_FORMAT").contains("json")'>
<then>
<root level="INFO">
<appender-ref ref="STDOUT_JSON"/>
<appender-ref ref="ASYNC_JSON"/>
</root>
<logger level="info" name="ms-service-info">
<appender-ref ref="STDOUT_JSON"/>
<appender-ref ref="ASYNC_JSON"/>
</logger>
<logger level="debug" name="io.fusion.air.microservice.adapters.security">
<appender-ref ref="STDOUT_JSON"/>
<appender-ref ref="ASYNC_JSON"/>
</logger>
<logger level="trace" name="io.fusion.air.microservice.security.JsonWebTokenValidator">
<appender-ref ref="STDOUT_JSON"/>
<appender-ref ref="ASYNC_JSON"/>
</logger>
</then>
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。