131 lines
4.8 KiB
Java
131 lines
4.8 KiB
Java
package com.ainovel.server.config;
|
||
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
import org.slf4j.MDC;
|
||
import org.springframework.context.annotation.Bean;
|
||
import org.springframework.context.annotation.Configuration;
|
||
import org.springframework.core.task.TaskDecorator;
|
||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||
import org.springframework.web.server.WebFilter;
|
||
import reactor.core.publisher.Hooks;
|
||
|
||
import jakarta.annotation.PostConstruct;
|
||
import java.util.Map;
|
||
import java.util.UUID;
|
||
|
||
/**
|
||
* 日志配置,包括MDC跟踪信息和日志格式设置
|
||
*/
|
||
@Configuration
|
||
public class LoggingConfiguration {
|
||
|
||
private static final Logger logger = LoggerFactory.getLogger(LoggingConfiguration.class);
|
||
|
||
/**
|
||
* 设置Reactor上下文传播MDC
|
||
*/
|
||
@PostConstruct
|
||
public void init() {
|
||
logger.info("配置Reactor上下文传播MDC");
|
||
// 启用自动上下文传播 (需要 io.micrometer:context-propagation 依赖)
|
||
Hooks.enableAutomaticContextPropagation();
|
||
logger.info("已启用Reactor自动MDC传播");
|
||
|
||
// 全局错误 Hook,确保丢弃/运算符错误也能被规范记录
|
||
Hooks.onErrorDropped(e -> logger.error("Reactor onErrorDropped 错误: {}", e.toString(), e));
|
||
|
||
}
|
||
|
||
/**
|
||
* WebFlux请求过滤器,用于设置MDC上下文
|
||
*/
|
||
@Bean
|
||
public WebFilter mdcAndLoggingFilter() {
|
||
return (exchange, chain) -> {
|
||
long startTime = System.currentTimeMillis();
|
||
ServerHttpRequest request = exchange.getRequest();
|
||
|
||
// --- MDC 设置 开始 ---
|
||
String originalTraceId = request.getHeaders().getFirst("X-Trace-ID");
|
||
final String traceId = (originalTraceId == null)
|
||
? UUID.randomUUID().toString().replace("-", "")
|
||
: originalTraceId;
|
||
MDC.put("traceId", traceId);
|
||
|
||
String userId = request.getHeaders().getFirst("X-User-Id");
|
||
if (userId != null) {
|
||
MDC.put("userId", userId);
|
||
}
|
||
|
||
final String path = request.getPath().value();
|
||
MDC.put("path", path);
|
||
// --- MDC 设置 结束 ---
|
||
|
||
// 对健康检查与监控采集等低价值请求不打印日志
|
||
if (path != null && path.startsWith("/actuator/prometheus")) {
|
||
return chain.filter(exchange)
|
||
.doFinally(signalType -> MDC.clear());
|
||
}
|
||
|
||
// --- 请求日志 开始 ---
|
||
final String finalUserId = userId; // effectively final for lambda
|
||
logger.info("请求开始: 方法={} URI={} 追踪ID={} 用户ID={}",
|
||
request.getMethod(),
|
||
request.getURI(),
|
||
traceId,
|
||
finalUserId != null ? finalUserId : "N/A");
|
||
// --- 请求日志 结束 ---
|
||
|
||
// 附加响应日志和MDC清理
|
||
return chain.filter(exchange)
|
||
.doOnSuccess(aVoid -> {
|
||
long duration = System.currentTimeMillis() - startTime;
|
||
int statusCode = exchange.getResponse().getStatusCode() != null ? exchange.getResponse().getStatusCode().value() : 0;
|
||
logger.info("请求结束: 状态={} 耗时={}ms 追踪ID={} 路径={}",
|
||
statusCode, duration, traceId, path);
|
||
})
|
||
.doOnError(throwable -> {
|
||
long duration = System.currentTimeMillis() - startTime;
|
||
logger.error("请求错误: {} 耗时={}ms 追踪ID={} 路径={}",
|
||
throwable.getMessage(), duration, traceId, path, throwable);
|
||
})
|
||
.doFinally(signalType -> MDC.clear()); // 清理MDC
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 任务装饰器,用于异步任务间传递MDC
|
||
*/
|
||
@Bean
|
||
public TaskDecorator mdcTaskDecorator() {
|
||
return task -> {
|
||
Map<String, String> contextMap = MDC.getCopyOfContextMap();
|
||
return () -> {
|
||
try {
|
||
if (contextMap != null) {
|
||
MDC.setContextMap(contextMap);
|
||
}
|
||
task.run();
|
||
} finally {
|
||
MDC.clear();
|
||
}
|
||
};
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 请求日志过滤器
|
||
*/
|
||
/* @Bean
|
||
@ConditionalOnProperty(name = "logging.request", havingValue = "true")
|
||
public CommonsRequestLoggingFilter requestLoggingFilter() {
|
||
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
|
||
filter.setIncludeQueryString(true);
|
||
filter.setIncludePayload(true);
|
||
filter.setMaxPayloadLength(10000);
|
||
filter.setIncludeHeaders(false);
|
||
filter.setAfterMessagePrefix("Request data: ");
|
||
return filter;
|
||
} */
|
||
} |