refactor(log): 优化访问日志相关配置属性名称

This commit is contained in:
2025-03-25 22:16:43 +08:00
parent da5e162a2a
commit 4c385927b4
14 changed files with 159 additions and 147 deletions

View File

@@ -22,8 +22,6 @@ import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.continew.starter.log.handler.LogHandler;
@@ -44,7 +42,6 @@ import java.time.Instant;
@Aspect
public class AccessLogAspect {
private static final Logger log = LoggerFactory.getLogger(AccessLogAspect.class);
private final LogProperties logProperties;
private final LogHandler logHandler;
@@ -113,7 +110,8 @@ public class AccessLogAspect {
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
try {
logHandler.processAccessLogStartReq(AccessLogContext.builder()
// 开始访问日志记录
logHandler.accessLogStart(AccessLogContext.builder()
.startTime(startTime)
.request(new RecordableServletHttpRequest(request))
.properties(logProperties)
@@ -121,7 +119,7 @@ public class AccessLogAspect {
return joinPoint.proceed();
} finally {
Instant endTime = Instant.now();
logHandler.processAccessLogEndReq(AccessLogContext.builder()
logHandler.accessLogFinish(AccessLogContext.builder()
.endTime(endTime)
.response(new RecordableServletHttpResponse(response, response.getStatus()))
.build());

View File

@@ -42,6 +42,7 @@ import java.net.URISyntaxException;
* @author Venil NoronhaSpring Boot Actuator
* @author Madhura BhaveSpring Boot Actuator
* @author Charles7c
* @author echo
* @since 1.1.0
*/
public class LogFilter extends OncePerRequestFilter implements Ordered {
@@ -66,15 +67,15 @@ public class LogFilter extends OncePerRequestFilter implements Ordered {
return;
}
boolean isMatch = logProperties.isMatch(request.getRequestURI());
boolean isExcludeUri = logProperties.isMatch(request.getRequestURI());
// 处理可重复读取的请求
HttpServletRequest requestWrapper = (isMatch || !this.isRequestWrapper(request))
HttpServletRequest requestWrapper = (isExcludeUri || !this.isRequestWrapper(request))
? request
: new RepeatReadRequestWrapper(request);
// 处理可重复读取的响应
HttpServletResponse responseWrapper = (isMatch || !this.isResponseWrapper(response))
HttpServletResponse responseWrapper = (isExcludeUri || !this.isResponseWrapper(response))
? response
: new RepeatReadResponseWrapper(response);

View File

@@ -171,11 +171,10 @@ public abstract class AbstractLogHandler implements LogHandler {
}
@Override
public void processAccessLogStartReq(AccessLogContext accessLogContext) {
public void accessLogStart(AccessLogContext accessLogContext) {
AccessLogProperties properties = accessLogContext.getProperties().getAccessLog();
// 是否需要打印 规则: 是否打印开关 或 放行路径
if (!properties.isPrint() || accessLogContext.getProperties()
.getAccessLog()
.isMatch(accessLogContext.getRequest().getPath())) {
return;
}
@@ -184,22 +183,23 @@ public abstract class AbstractLogHandler implements LogHandler {
RecordableHttpRequest request = accessLogContext.getRequest();
String path = request.getPath();
String param = AccessLogUtils.getParam(request, properties);
log.info(param != null ? "[Start] [{}] {} {}" : "[Start] [{}] {}", request.getMethod(), path, param);
log.info(param != null ? "[Start] [{}] {} param: {}" : "[Start] [{}] {}", request.getMethod(), path, param);
}
@Override
public void processAccessLogEndReq(AccessLogContext accessLogContext) {
public void accessLogFinish(AccessLogContext accessLogContext) {
AccessLogContext logContext = logContextThread.get();
if (ObjectUtil.isNotEmpty(logContext)) {
try {
RecordableHttpRequest request = logContext.getRequest();
RecordableHttpResponse response = accessLogContext.getResponse();
Duration timeTaken = Duration.between(logContext.getStartTime(), accessLogContext.getEndTime());
log.info("[End] [{}] {} {} {}ms", request.getMethod(), request.getPath(), response
.getStatus(), timeTaken.toMillis());
} finally {
logContextThread.remove();
}
if (ObjectUtil.isEmpty(logContext)) {
return;
}
try {
RecordableHttpRequest request = logContext.getRequest();
RecordableHttpResponse response = accessLogContext.getResponse();
Duration timeTaken = Duration.between(logContext.getStartTime(), accessLogContext.getEndTime());
log.info("[End] [{}] {} {} {}ms", request.getMethod(), request.getPath(), response.getStatus(), timeTaken
.toMillis());
} finally {
logContextThread.remove();
}
}
}

View File

@@ -30,6 +30,7 @@ import java.util.Set;
* 日志处理器
*
* @author Charles7c
* @author echo
* @since 2.8.0
*/
public interface LogHandler {
@@ -100,16 +101,18 @@ public interface LogHandler {
Set<Include> getIncludes(Set<Include> includes, Method targetMethod, Class<?> targetClass);
/**
* 处理访问日志开始请求
* 开始访问日志记录
*
* @param accessLogContext 访问日志上下文
* @since 2.10.0
*/
void processAccessLogStartReq(AccessLogContext accessLogContext);
void accessLogStart(AccessLogContext accessLogContext);
/**
* 处理访问日志 结束请求
* 结束访问日志记录
*
* @param accessLogContext 访问日志上下文
* @since 2.10.0
*/
void processAccessLogEndReq(AccessLogContext accessLogContext);
void accessLogFinish(AccessLogContext accessLogContext);
}

View File

@@ -25,6 +25,7 @@ import java.util.Map;
* @author Andy WilkinsonSpring Boot Actuator
* @author Phillip WebbSpring Boot Actuator
* @author Charles7c
* @author echo
* @see RecordableHttpResponse
* @since 1.1.0
*/
@@ -45,11 +46,13 @@ public interface RecordableHttpRequest {
URI getUrl();
/**
* 获取 IP
* 获取路径
* <p>/foo/bar</p>
*
* @return IP
* @return 路径
* @since 2.10.0
*/
String getIp();
String getPath();
/**
* 获取请求头
@@ -73,9 +76,9 @@ public interface RecordableHttpRequest {
Map<String, Object> getParam();
/**
* 获取路径 - 格式 /system/dept
* 获取 IP
*
* @return {@link String }
* @return IP
*/
String getPath();
String getIp();
}

View File

@@ -35,6 +35,8 @@ import java.util.Map;
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Charles7c
* @author echo
* @since 1.1.0
*/
public final class RecordableServletHttpRequest implements RecordableHttpRequest {
@@ -66,8 +68,8 @@ public final class RecordableServletHttpRequest implements RecordableHttpRequest
}
@Override
public String getIp() {
return JakartaServletUtil.getClientIP(request);
public String getPath() {
return request.getRequestURI();
}
@Override
@@ -90,8 +92,8 @@ public final class RecordableServletHttpRequest implements RecordableHttpRequest
}
@Override
public String getPath() {
return request.getRequestURI();
public String getIp() {
return JakartaServletUtil.getClientIP(request);
}
private StringBuilder appendQueryString(String queryString) {

View File

@@ -30,6 +30,8 @@ import java.util.Map;
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Charles7c
* @author echo
* @since 1.1.0
*/
public final class RecordableServletHttpResponse implements RecordableHttpResponse {

View File

@@ -25,7 +25,7 @@ import java.time.Instant;
* 访问日志上下文
*
* @author echo
* @since 2.8.3
* @since 2.10.0
*/
public class AccessLogContext {
@@ -90,7 +90,11 @@ public class AccessLogContext {
return new Builder();
}
/**
* 访问日志上下文构建者
*/
public static class Builder {
private Instant startTime;
private Instant endTime;
private RecordableHttpRequest request;

View File

@@ -16,76 +16,71 @@
package top.continew.starter.log.model;
import top.continew.starter.web.util.SpringWebUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 访问日志输出配置
* 访问日志配置属性
*
* @author echo
* @since 2.8.3
* @author Charles7c
* @since 2.10.0
*/
public class AccessLogProperties {
/**
* 是否打印日志,开启后可打印访问日志(类似于 Nginx access log
* 是否打印访问日志(类似于 Nginx access log
* <p>
* 不记录日志也支持开启打印访问日志
* 不记录请求日志也支持开启打印访问日志
* </p>
*/
private boolean isPrint = false;
/**
* 放行路由
* 是否打印请求参数body/query/form
* <p>开启后,访问日志会打印请求参数</p>
*/
private List<String> excludePatterns = new ArrayList<>();
/**
* 是否记录请求参数body/query/form
* <p>开启后会在日志中输出请求参数</p>
*/
private boolean isReqParams = false;
private boolean isPrintRequestParam = false;
/**
* 是否自动截断超长参数值(如 base64、大文本
* <p>开启后会对超过指定长度的参数值进行截断处理</p>
* <p>开启后超过指定长度的参数值将会自动截断处理</p>
*/
private boolean truncateLongParams = false;
private boolean longParamTruncate = false;
/**
* 超长参数检测阈值(单位:字符)
* <p>当参数值长度超过此值时,触发截断规则</p>
* <p>默认2000</p>
* <p>默认2000,仅在 {@link #longParamTruncate} 启用时生效</p>
*/
private int ultraLongParamThreshold = 2000;
private int longParamThreshold = 2000;
/**
* 超长参数最大展示长度(单位:字符)
* <p>当参数超过ultraLongParamThreshold时强制截断到此长度</p>
* <p>默认50</p>
* 超长参数最大保留长度(单位:字符)
* <p>当参数超过 {@link #longParamThreshold} 时,强制截断到此长度</p>
* <p>默认50,仅在 {@link #longParamTruncate} 启用时生效</p>
*/
private int ultraLongParamMaxLength = 50;
private int longParamMaxLength = 50;
/**
* 截断后追加的后缀符号(如配置 "..." 会让截断内容更直观)
* <p>建议配置 3-5 个非占宽字符,默认为空不追加</p>
* <p>建议配置 3-5 个非占宽字符,默认为 ...</p>
* <p>仅在 {@link #longParamTruncate} 启用时生效</p>
*/
private String truncateSuffix = "...";
private String longParamSuffix = "...";
/**
* 是否过滤敏感参数
* <p>开启后会对敏感参数进行过滤,默认不过滤</p>
*/
private boolean isSensitiveParams = false;
private boolean isParamSensitive = false;
/**
* 敏感参数字段列表password,token,idCard
* <p>支持精确匹配(区分大小写)</p>
* <p>示例值password,oldPassword</p>
*/
private List<String> sensitiveParamList = new ArrayList<>();
private List<String> sensitiveParams = new ArrayList<>();
public boolean isPrint() {
return isPrint;
@@ -95,77 +90,59 @@ public class AccessLogProperties {
isPrint = print;
}
public List<String> getExcludePatterns() {
return excludePatterns;
public boolean isPrintRequestParam() {
return isPrintRequestParam;
}
public void setExcludePatterns(List<String> excludePatterns) {
this.excludePatterns = excludePatterns;
public void setPrintRequestParam(boolean printRequestParam) {
isPrintRequestParam = printRequestParam;
}
public boolean isReqParams() {
return isReqParams;
public boolean isLongParamTruncate() {
return longParamTruncate;
}
public void setReqParams(boolean reqParams) {
isReqParams = reqParams;
public void setLongParamTruncate(boolean longParamTruncate) {
this.longParamTruncate = longParamTruncate;
}
public boolean isTruncateLongParams() {
return truncateLongParams;
public int getLongParamThreshold() {
return longParamThreshold;
}
public void setTruncateLongParams(boolean truncateLongParams) {
this.truncateLongParams = truncateLongParams;
public void setLongParamThreshold(int longParamThreshold) {
this.longParamThreshold = longParamThreshold;
}
public int getUltraLongParamThreshold() {
return ultraLongParamThreshold;
public int getLongParamMaxLength() {
return longParamMaxLength;
}
public void setUltraLongParamThreshold(int ultraLongParamThreshold) {
this.ultraLongParamThreshold = ultraLongParamThreshold;
public void setLongParamMaxLength(int longParamMaxLength) {
this.longParamMaxLength = longParamMaxLength;
}
public int getUltraLongParamMaxLength() {
return ultraLongParamMaxLength;
public String getLongParamSuffix() {
return longParamSuffix;
}
public void setUltraLongParamMaxLength(int ultraLongParamMaxLength) {
this.ultraLongParamMaxLength = ultraLongParamMaxLength;
public void setLongParamSuffix(String longParamSuffix) {
this.longParamSuffix = longParamSuffix;
}
public String getTruncateSuffix() {
return truncateSuffix;
public boolean isParamSensitive() {
return isParamSensitive;
}
public void setTruncateSuffix(String truncateSuffix) {
this.truncateSuffix = truncateSuffix;
public void setParamSensitive(boolean paramSensitive) {
isParamSensitive = paramSensitive;
}
public boolean isSensitiveParams() {
return isSensitiveParams;
public List<String> getSensitiveParams() {
return sensitiveParams;
}
public void setSensitiveParams(boolean sensitiveParams) {
isSensitiveParams = sensitiveParams;
}
public List<String> getSensitiveParamList() {
return sensitiveParamList;
}
public void setSensitiveParamList(List<String> sensitiveParamList) {
this.sensitiveParamList = sensitiveParamList;
}
/**
* 是否匹配放行路由
*
* @param uri 请求 URI
* @return 是否匹配
*/
public boolean isMatch(String uri) {
return this.getExcludePatterns().stream().anyMatch(pattern -> SpringWebUtils.isMatch(uri, pattern));
public void setSensitiveParams(List<String> sensitiveParams) {
this.sensitiveParams = sensitiveParams;
}
}

View File

@@ -26,14 +26,14 @@ import java.util.List;
import java.util.Map;
/**
* 访问日志工具类
*
* @author echo
* @since 2025/03/25 19:16
**/
* @author Charles7c
* @since 2.10.0
*/
public class AccessLogUtils {
public AccessLogUtils() {
}
/**
* 获取参数信息
*
@@ -42,8 +42,8 @@ public class AccessLogUtils {
* @return {@link String }
*/
public static String getParam(RecordableHttpRequest request, AccessLogProperties properties) {
// 是否需要输出参数
if (!properties.isReqParams()) {
// 是否需要打印请求参数
if (!properties.isPrintRequestParam()) {
return null;
}
@@ -54,16 +54,16 @@ public class AccessLogUtils {
}
// 是否需要对特定入参脱敏
if (properties.isSensitiveParams()) {
params = filterSensitiveParams(params, properties.getSensitiveParamList());
if (properties.isParamSensitive()) {
params = filterSensitiveParams(params, properties.getSensitiveParams());
}
// 是否自动截断超长参数值
if (properties.isTruncateLongParams()) {
params = truncateLongParams(params, properties.getUltraLongParamThreshold(), properties
.getUltraLongParamMaxLength(), properties.getTruncateSuffix());
if (properties.isLongParamTruncate()) {
params = truncateLongParams(params, properties.getLongParamThreshold(), properties
.getLongParamMaxLength(), properties.getLongParamSuffix());
}
return "param:" + JSONUtil.toJsonStr(params);
return JSONUtil.toJsonStr(params);
}
/**
@@ -80,9 +80,7 @@ public class AccessLogUtils {
Map<String, Object> filteredParams = new HashMap<>(params);
for (String sensitiveKey : sensitiveParams) {
if (filteredParams.containsKey(sensitiveKey)) {
filteredParams.put(sensitiveKey, "***");
}
filteredParams.computeIfPresent(sensitiveKey, (key, value) -> "***");
}
return filteredParams;
}
@@ -115,4 +113,7 @@ public class AccessLogUtils {
}
return truncatedParams;
}
private AccessLogUtils() {
}
}

View File

@@ -64,7 +64,7 @@ public class LogInterceptor implements HandlerInterceptor {
@NonNull HttpServletResponse response,
@NonNull Object handler) {
Instant startTime = Instant.now();
logHandler.processAccessLogStartReq(AccessLogContext.builder()
logHandler.accessLogStart(AccessLogContext.builder()
.startTime(startTime)
.request(new RecordableServletHttpRequest(request))
.properties(logProperties)
@@ -84,7 +84,7 @@ public class LogInterceptor implements HandlerInterceptor {
Exception e) {
try {
Instant endTime = Instant.now();
logHandler.processAccessLogEndReq(AccessLogContext.builder()
logHandler.accessLogFinish(AccessLogContext.builder()
.endTime(endTime)
.response(new RecordableServletHttpResponse(response, response.getStatus()))
.build());