SpringBoot中日志輸出規(guī)范的五種策略
一、統(tǒng)一日志格式配置策略
1.1 基本原理
統(tǒng)一的日志格式是團(tuán)隊(duì)協(xié)作的基礎(chǔ),可以提高日志的可讀性和可分析性。
SpringBoot允許開(kāi)發(fā)者自定義日志輸出格式,包括時(shí)間戳、日志級(jí)別、線(xiàn)程信息、類(lèi)名和消息內(nèi)容等。
1.2 實(shí)現(xiàn)方式
1.2.1 配置文件方式
在application.properties或application.yml中定義日志格式:
# application.properties
# 控制臺(tái)日志格式
logging.pattern.console=%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
# 文件日志格式
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
YAML格式配置:
logging:
pattern:
console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"
1.2.2 自定義Logback配置
對(duì)于更復(fù)雜的配置,可以使用logback-spring.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="CONSOLE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<property name="FILE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/archived/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
1.2.3 JSON格式日志配置
對(duì)于需要集中式日志分析的系統(tǒng),配置JSON格式日志更有利于日志處理:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.2</version>
</dependency>
<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.json</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>requestId</includeMdcKeyName>
<includeMdcKeyName>userId</includeMdcKeyName>
<customFields>{"application":"my-service","environment":"${ENVIRONMENT:-development}"}</customFields>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/archived/application.%d{yyyy-MM-dd}.%i.json</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
1.3 最佳實(shí)踐
- 環(huán)境區(qū)分:為不同環(huán)境配置不同的日志格式(開(kāi)發(fā)環(huán)境可讀性高,生產(chǎn)環(huán)境機(jī)器可解析)
<springProfile name="dev">
<!-- 開(kāi)發(fā)環(huán)境配置 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) %cyan(%logger{15}) - %msg%n</pattern>
</encoder>
</appender>
</springProfile>
<springProfile name="prod">
<!-- 生產(chǎn)環(huán)境配置 -->
<appender name="JSON_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
</springProfile>
- 添加關(guān)鍵信息:確保日志中包含足夠的上下文信息
%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] [%X{userId}] %-5level [%thread] %logger{36} - %msg%n
- 注意敏感信息:避免記錄密碼、令牌等敏感信息,必要時(shí)進(jìn)行脫敏處理
二、分級(jí)日志策略
2.1 基本原理
合理使用日志級(jí)別可以幫助區(qū)分不同重要程度的信息,便于問(wèn)題定位和系統(tǒng)監(jiān)控。
SpringBoot支持標(biāo)準(zhǔn)的日志級(jí)別:TRACE、DEBUG、INFO、WARN、ERROR。
2.2 實(shí)現(xiàn)方式
2.2.1 配置不同包的日志級(jí)別
# 全局日志級(jí)別 logging.level.root=INFO # 特定包的日志級(jí)別 logging.level.org.springframework.web=DEBUG logging.level.org.hibernate=ERROR logging.level.com.mycompany.app=DEBUG
2.2.2 基于環(huán)境的日志級(jí)別配置
# application.yml
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev
logging:
level:
root: INFO
com.mycompany.app: DEBUG
org.springframework: INFO
---
spring:
config:
activate:
on-profile: prod
logging:
level:
root: WARN
com.mycompany.app: INFO
org.springframework: WARN
2.2.3 編程式日志級(jí)別管理
@RestController
@RequestMapping("/api/logs")
public class LoggingController {
@Autowired
private LoggingSystem loggingSystem;
@PutMapping("/level/{package}/{level}")
public void changeLogLevel(
@PathVariable("package") String packageName,
@PathVariable("level") String level) {
LogLevel logLevel = LogLevel.valueOf(level.toUpperCase());
loggingSystem.setLogLevel(packageName, logLevel);
}
}
2.3 日志級(jí)別使用規(guī)范
建立清晰的日志級(jí)別使用規(guī)范對(duì)團(tuán)隊(duì)協(xié)作至關(guān)重要:
- ERROR:系統(tǒng)錯(cuò)誤、應(yīng)用崩潰、服務(wù)不可用等嚴(yán)重問(wèn)題
try {
// 業(yè)務(wù)操作
} catch (Exception e) {
log.error("Failed to process payment for order: {}", orderId, e);
throw new PaymentProcessingException("Payment processing failed", e);
}
- WARN:不影響當(dāng)前功能但需要注意的問(wèn)題
if (retryCount > maxRetries / 2) {
log.warn("High number of retries detected for operation: {}, current retry: {}/{}",
operationType, retryCount, maxRetries);
}
- INFO:重要業(yè)務(wù)流程、系統(tǒng)狀態(tài)變更等信息
log.info("Order {} has been successfully processed with {} items",
order.getId(), order.getItems().size());
- DEBUG:調(diào)試信息,詳細(xì)的處理流程
log.debug("Processing product with ID: {}, name: {}, category: {}",
product.getId(), product.getName(), product.getCategory());
- TRACE:最詳細(xì)的追蹤信息,一般用于框架內(nèi)部
log.trace("Method execution path: class={}, method={}, params={}",
className, methodName, Arrays.toString(args));
2.4 最佳實(shí)踐
- 默認(rèn)使用INFO級(jí)別:生產(chǎn)環(huán)境默認(rèn)使用INFO級(jí)別,開(kāi)發(fā)環(huán)境可使用DEBUG
- 合理劃分包結(jié)構(gòu):按功能或模塊劃分包,便于精細(xì)控制日志級(jí)別
- 避免日志爆炸:謹(jǐn)慎使用DEBUG和TRACE級(jí)別,避免產(chǎn)生大量無(wú)用日志
- 條件日志:使用條件判斷減少不必要的字符串拼接開(kāi)銷(xiāo)
// 推薦方式
if (log.isDebugEnabled()) {
log.debug("Complex calculation result: {}", calculateComplexResult());
}
// 避免這樣使用
log.debug("Complex calculation result: " + calculateComplexResult());
三、日志切面實(shí)現(xiàn)策略
3.1 基本原理
使用AOP(面向切面編程)可以集中處理日志記錄,避免在每個(gè)方法中手動(dòng)編寫(xiě)重復(fù)的日志代碼。尤其適合API調(diào)用日志、方法執(zhí)行時(shí)間統(tǒng)計(jì)等場(chǎng)景。
3.2 實(shí)現(xiàn)方式
3.2.1 基礎(chǔ)日志切面
@Aspect
@Component
@Slf4j
public class LoggingAspect {
@Pointcut("execution(* com.mycompany.app.service.*.*(..))")
public void serviceLayer() {}
@Around("serviceLayer()")
public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
log.info("Executing: {}.{}", className, methodName);
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - startTime;
log.info("Executed: {}.{} in {} ms", className, methodName, executionTime);
return result;
} catch (Exception e) {
log.error("Exception in {}.{}: {}", className, methodName, e.getMessage(), e);
throw e;
}
}
}
3.2.2 API請(qǐng)求響應(yīng)日志切面
@Aspect
@Component
@Slf4j
public class ApiLoggingAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || " +
"@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
"@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
"@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
"@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
public void apiMethods() {}
@Around("apiMethods()")
public Object logApiCall(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest();
String requestURI = request.getRequestURI();
String httpMethod = request.getMethod();
String clientIP = request.getRemoteAddr();
log.info("API Request - Method: {} URI: {} Client: {}", httpMethod, requestURI, clientIP);
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
log.info("API Response - Method: {} URI: {} Duration: {} ms Status: SUCCESS",
httpMethod, requestURI, duration);
return result;
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
log.error("API Response - Method: {} URI: {} Duration: {} ms Status: ERROR Message: {}",
httpMethod, requestURI, duration, e.getMessage(), e);
throw e;
}
}
}
3.2.3 自定義注解實(shí)現(xiàn)有選擇的日志記錄
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogExecutionTime {
String description() default "";
}
@Aspect
@Component
@Slf4j
public class CustomLogAspect {
@Around("@annotation(logExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
String description = logExecutionTime.description();
String methodName = joinPoint.getSignature().getName();
log.info("Starting {} - {}", methodName, description);
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - startTime;
log.info("Completed {} - {} in {} ms", methodName, description, executionTime);
return result;
} catch (Exception e) {
long executionTime = System.currentTimeMillis() - startTime;
log.error("Failed {} - {} after {} ms: {}", methodName, description,
executionTime, e.getMessage(), e);
throw e;
}
}
}
使用示例:
@Service
public class OrderService {
@LogExecutionTime(description = "Process order payment")
public PaymentResult processPayment(Order order) {
// 處理支付邏輯
}
}
3.3 最佳實(shí)踐
- 合理定義切點(diǎn):避免過(guò)于寬泛的切點(diǎn)定義,防止產(chǎn)生過(guò)多日志
- 注意性能影響:記錄詳細(xì)參數(shù)和結(jié)果可能帶來(lái)性能開(kāi)銷(xiāo),需權(quán)衡取舍
- 異常處理:確保日志切面本身不會(huì)拋出異常,影響主業(yè)務(wù)流程
- 避免敏感信息:敏感數(shù)據(jù)進(jìn)行脫敏處理后再記錄
// 敏感信息脫敏示例
private String maskCardNumber(String cardNumber) {
if (cardNumber == null || cardNumber.length() < 8) {
return "***";
}
return "******" + cardNumber.substring(cardNumber.length() - 4);
}
四、MDC上下文跟蹤策略
4.1 基本原理
MDC (Mapped Diagnostic Context) 是一種用于存儲(chǔ)請(qǐng)求級(jí)別上下文信息的工具,它可以在日志框架中保存和傳遞這些信息,特別適合分布式系統(tǒng)中的請(qǐng)求跟蹤。
4.2 實(shí)現(xiàn)方式
4.2.1 配置MDC過(guò)濾器
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MdcLoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
// 生成唯一請(qǐng)求ID
String requestId = UUID.randomUUID().toString().replace("-", "");
MDC.put("requestId", requestId);
// 添加用戶(hù)信息(如果有)
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
MDC.put("userId", authentication.getName());
}
// 添加請(qǐng)求信息
MDC.put("clientIP", request.getRemoteAddr());
MDC.put("userAgent", request.getHeader("User-Agent"));
MDC.put("httpMethod", request.getMethod());
MDC.put("requestURI", request.getRequestURI());
// 設(shè)置響應(yīng)頭,便于客戶(hù)端跟蹤
response.setHeader("X-Request-ID", requestId);
filterChain.doFilter(request, response);
} finally {
// 清理MDC上下文,防止內(nèi)存泄漏
MDC.clear();
}
}
}
4.2.2 日志格式中包含MDC信息
<property name="CONSOLE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] [%X{userId}] %-5level [%thread] %logger{36} - %msg%n"/>
4.2.3 分布式追蹤集成
與Spring Cloud Sleuth和Zipkin集成,實(shí)現(xiàn)全鏈路追蹤:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
spring.application.name=my-service spring.sleuth.sampler.probability=1.0 spring.zipkin.base-url=http://localhost:9411
4.2.4 手動(dòng)管理MDC上下文
@Service
public class BackgroundJobService {
private static final Logger log = LoggerFactory.getLogger(BackgroundJobService.class);
@Async
public CompletableFuture<Void> processJob(String jobId, Map<String, String> context) {
// 保存原有MDC上下文
Map<String, String> previousContext = MDC.getCopyOfContextMap();
try {
// 設(shè)置新的MDC上下文
MDC.put("jobId", jobId);
if (context != null) {
context.forEach(MDC::put);
}
log.info("Starting background job processing");
// 執(zhí)行業(yè)務(wù)邏輯
// ...
log.info("Completed background job processing");
return CompletableFuture.completedFuture(null);
} finally {
// 恢復(fù)原有MDC上下文或清除
if (previousContext != null) {
MDC.setContextMap(previousContext);
} else {
MDC.clear();
}
}
}
}
4.3 最佳實(shí)踐
- 唯一請(qǐng)求標(biāo)識(shí):為每個(gè)請(qǐng)求生成唯一ID,便于追蹤完整請(qǐng)求鏈路
- 傳遞MDC上下文:在異步處理和線(xiàn)程池中正確傳遞MDC上下文
- 合理選擇MDC信息:記錄有價(jià)值的上下文信息,但避免過(guò)多信息造成日志膨脹
- 與分布式追蹤結(jié)合:與Sleuth、Zipkin等工具結(jié)合,提供完整的分布式追蹤能力
// 自定義線(xiàn)程池配置,傳遞MDC上下文
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("MyAsync-");
// 包裝原始Executor,傳遞MDC上下文
executor.setTaskDecorator(runnable -> {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
runnable.run();
} finally {
MDC.clear();
}
};
});
executor.initialize();
return executor;
}
}
五、異步日志策略
5.1 基本原理
在高性能系統(tǒng)中,同步記錄日志可能成為性能瓶頸,特別是在I/O性能受限的環(huán)境下。
異步日志通過(guò)將日志操作從主線(xiàn)程中分離,可以顯著提升系統(tǒng)性能。
5.2 實(shí)現(xiàn)方式
5.2.1 Logback異步配置
<configuration>
<!-- 定義日志內(nèi)容和格式 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 配置詳情... -->
</appender>
<!-- 異步appender -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
<queueSize>512</queueSize>
<discardingThreshold>0</discardingThreshold>
<includeCallerData>false</includeCallerData>
<neverBlock>false</neverBlock>
</appender>
<root level="INFO">
<appender-ref ref="ASYNC" />
</root>
</configuration>
5.2.2 Log4j2異步配置
添加依賴(lài):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
配置Log4j2:
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<!-- 異步Appender -->
<Async name="AsyncFile">
<AppenderRef ref="RollingFile"/>
<BufferSize>1024</BufferSize>
</Async>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="AsyncFile"/>
</Root>
</Loggers>
</Configuration>
5.2.3 性能優(yōu)化配置
針對(duì)Log4j2進(jìn)行更高級(jí)的性能優(yōu)化:
<Configuration status="WARN" packages="com.mycompany.logging">
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>
<Appenders>
<!-- 使用MappedFile提高I/O性能 -->
<RollingRandomAccessFile name="RollingFile"
fileName="logs/app.log"
filePattern="logs/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="25 MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingRandomAccessFile>
<!-- 使用更高性能的Async配置 -->
<Async name="AsyncFile" bufferSize="2048">
<AppenderRef ref="RollingFile"/>
<DisruptorBlockingQueue />
</Async>
</Appenders>
<Loggers>
<!-- 降低某些高頻日志的級(jí)別 -->
<Logger name="org.hibernate.SQL" level="debug" additivity="false">
<AppenderRef ref="AsyncFile" level="debug"/>
</Logger>
<Root level="info">
<AppenderRef ref="AsyncFile"/>
</Root>
</Loggers>
</Configuration>
5.2.4 自定義異步日志記錄器
對(duì)于特殊需求,可以實(shí)現(xiàn)自定義的異步日志記錄器:
@Component
public class AsyncLogger {
private static final Logger log = LoggerFactory.getLogger(AsyncLogger.class);
private final ExecutorService logExecutor;
public AsyncLogger() {
this.logExecutor = Executors.newSingleThreadExecutor(r -> {
Thread thread = new Thread(r, "async-logger");
thread.setDaemon(true);
return thread;
});
// 確保應(yīng)用關(guān)閉時(shí)處理完所有日志
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
logExecutor.shutdown();
try {
if (!logExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
log.warn("AsyncLogger executor did not terminate in the expected time.");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}));
}
public void info(String format, Object... arguments) {
logExecutor.submit(() -> log.info(format, arguments));
}
public void warn(String format, Object... arguments) {
logExecutor.submit(() -> log.warn(format, arguments));
}
public void error(String format, Object... arguments) {
Throwable throwable = extractThrowable(arguments);
if (throwable != null) {
logExecutor.submit(() -> log.error(format, arguments));
} else {
logExecutor.submit(() -> log.error(format, arguments));
}
}
private Throwable extractThrowable(Object[] arguments) {
if (arguments != null && arguments.length > 0) {
Object lastArg = arguments[arguments.length - 1];
if (lastArg instanceof Throwable) {
return (Throwable) lastArg;
}
}
return null;
}
}
5.3 最佳實(shí)踐
- 隊(duì)列大小設(shè)置:根據(jù)系統(tǒng)吞吐量和內(nèi)存情況設(shè)置合理的隊(duì)列大小
- 丟棄策略配置:在高負(fù)載情況下,可以考慮丟棄低優(yōu)先級(jí)的日志
<AsyncAppender name="ASYNC" queueSize="512" discardingThreshold="20">
<!-- 當(dāng)隊(duì)列剩余容量低于20%時(shí),會(huì)丟棄TRACE, DEBUG和INFO級(jí)別的日志 -->
</AsyncAppender>
- 異步日志的注意事項(xiàng):
- 異步日志可能導(dǎo)致異常堆棧信息不完整
- 系統(tǒng)崩潰時(shí)可能丟失最后一批日志
- 需要權(quán)衡性能和日志完整性
- 合理使用同步與異步:
- 關(guān)鍵操作日志(如金融交易)使用同步記錄確??煽啃?/li>
- 高頻但不關(guān)鍵的日志(如訪問(wèn)日志)使用異步記錄提高性能
// 同步記錄關(guān)鍵業(yè)務(wù)日志
log.info("Transaction completed: id={}, amount={}, status={}",
transaction.getId(), transaction.getAmount(), transaction.getStatus());
// 異步記錄高頻統(tǒng)計(jì)日志
asyncLogger.info("API usage stats: endpoint={}, count={}, avgResponseTime={}ms",
endpoint, requestCount, avgResponseTime);
另外,性能要求較高的應(yīng)用推薦使用log4j2的異步模式,性能遠(yuǎn)高于logback。
六、總結(jié)
這些策略不是相互排斥的,而是可以結(jié)合使用,共同構(gòu)建完整的日志體系。
在實(shí)際應(yīng)用中,應(yīng)根據(jù)項(xiàng)目規(guī)模、團(tuán)隊(duì)情況和業(yè)務(wù)需求,選擇合適的日志規(guī)范策略組合。
好的日志實(shí)踐不僅能幫助開(kāi)發(fā)者更快地定位和解決問(wèn)題,還能為系統(tǒng)性能優(yōu)化和安全審計(jì)提供重要依據(jù)。
以上就是SpringBoot中日志輸出規(guī)范的五種策略的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot日志輸出規(guī)范的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java中的動(dòng)態(tài)代理(jdk和Cglib)實(shí)現(xiàn)詳解
這篇文章主要介紹了java中的動(dòng)態(tài)代理(jdk和Cglib)的相關(guān)資料,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-04-04
Spring中的@RestController注解詳細(xì)解析
這篇文章主要介紹了Spring中的@RestController注解詳細(xì)解析,@RestController 是 Spring Framework 中的一個(gè)注解,用于標(biāo)識(shí)一個(gè)類(lèi)為 RESTful Web 服務(wù)的控制器(Controller),處理 HTTP 請(qǐng)求并返回相應(yīng)的數(shù)據(jù),2024-01-01
如何解決UnsupportedOperationException異常問(wèn)題
這篇文章主要介紹了如何解決UnsupportedOperationException異常問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
Spring Cloud Feign組成配置過(guò)程解析
這篇文章主要介紹了Spring Cloud Feign組成配置過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
SpringBoot 集成 Jasypt 對(duì)數(shù)據(jù)庫(kù)加密以及踩坑的記錄分享
這篇文章主要介紹了SpringBoot 集成 Jasypt 對(duì)數(shù)據(jù)庫(kù)加密以及踩坑,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
java多線(xiàn)程解決生產(chǎn)者消費(fèi)者問(wèn)題
這篇文章主要介紹了java多線(xiàn)程解決生產(chǎn)者消費(fèi)者問(wèn)題的方法,實(shí)例分析了java采用多線(xiàn)程的方法解決生產(chǎn)者消費(fèi)者問(wèn)題的相關(guān)技巧,需要的朋友可以參考下2015-05-05

