revert(log): 还原 RecordableHttpRequest、RecordableHttpResponse

This commit is contained in:
2025-12-29 22:16:14 +08:00
parent 27cb543e8c
commit 4f8d7725e6
16 changed files with 494 additions and 281 deletions

View File

@@ -21,7 +21,6 @@ import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
@@ -29,24 +28,15 @@ import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.UriUtils;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.wrapper.RepeatReadRequestWrapper;
import top.continew.starter.core.wrapper.RepeatReadResponseWrapper;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/**
* Servlet 工具类
*
* @author Charles7c
* @author echo
* @since 1.0.0
*/
public class ServletUtils extends JakartaServletUtil {
@@ -118,138 +108,13 @@ public class ServletUtils extends JakartaServletUtil {
}
}
/**
* 获取请求方法
*
* @return {@link String }
* @since 2.11.0
*/
public static String getRequestMethod() {
HttpServletRequest request = getRequest();
return request != null ? request.getMethod() : null;
}
/**
* 获取请求参数
*
* @param name 参数名
* @return {@link String }
* @since 2.11.0
*/
public static String getRequestParameter(String name) {
HttpServletRequest request = getRequest();
return request != null ? request.getParameter(name) : null;
}
/**
* 获取请求 Ip
*
* @return {@link String }
* @since 2.11.0
*/
public static String getRequestIp() {
HttpServletRequest request = getRequest();
return request != null ? getClientIP(request) : null;
}
/**
* 获取请求头信息
*
* @return {@link Map }<{@link String }, {@link String }>
* @since 2.11.0
*/
public static Map<String, String> getRequestHeaders() {
HttpServletRequest request = getRequest();
return request != null ? getHeaderMap(request) : Collections.emptyMap();
}
/**
* 获取请求 URL包含 query 参数)
* <p>{@code http://localhost:8000/system/user?page=1&size=10}</p>
*
* @return {@link URI }
* @since 2.11.0
*/
public static URI getRequestUrl() {
HttpServletRequest request = getRequest();
if (request == null) {
return null;
}
String queryString = request.getQueryString();
if (CharSequenceUtil.isBlank(queryString)) {
return URI.create(request.getRequestURL().toString());
}
try {
StringBuilder urlBuilder = appendQueryString(queryString);
return new URI(urlBuilder.toString());
} catch (URISyntaxException e) {
String encoded = UriUtils.encodeQuery(queryString, StandardCharsets.UTF_8);
StringBuilder urlBuilder = appendQueryString(encoded);
return URI.create(urlBuilder.toString());
}
}
/**
* 获取请求路径
*
* @return {@link URI }
* @since 2.11.0
*/
public static String getRequestPath() {
HttpServletRequest request = getRequest();
return request != null ? request.getRequestURI() : null;
}
/**
* 获取请求 body 参数
*
* @return {@link String }
* @since 2.11.0
*/
public static String getRequestBody() {
HttpServletRequest request = getRequest();
if (request instanceof RepeatReadRequestWrapper wrapper && !wrapper.isMultipartContent(request)) {
String body = JakartaServletUtil.getBody(request);
return JSONUtil.isTypeJSON(body) ? body : null;
}
return null;
}
/**
* 获取请求参数
*
* @return {@link Map }<{@link String }, {@link Object }>
* @since 2.11.0
*/
public static Map<String, Object> getRequestParams() {
String body = getRequestBody();
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)
? JSONUtil.toBean(body, Map.class)
: Collections.unmodifiableMap(JakartaServletUtil.getParamMap(Objects.requireNonNull(getRequest())));
}
/**
* 获取响应状态
*
* @return int
* @since 2.11.0
*/
public static int getResponseStatus() {
HttpServletResponse response = getResponse();
return response != null ? response.getStatus() : -1;
}
/**
* 获取响应所有的头header信息
*
* @param response 响应对象{@link HttpServletResponse}
* @return header值
* @since 2.11.0
*/
public static Map<String, String> getResponseHeaders() {
HttpServletResponse response = getResponse();
if (response == null) {
return Collections.emptyMap();
}
public static Map<String, String> getHeaderMap(HttpServletResponse response) {
final Collection<String> headerNames = response.getHeaderNames();
final Map<String, String> headerMap = MapUtil.newHashMap(headerNames.size(), true);
for (String name : headerNames) {
@@ -258,32 +123,6 @@ public class ServletUtils extends JakartaServletUtil {
return headerMap;
}
/**
* 获取响应 body 参数
*
* @return {@link String }
* @since 2.11.0
*/
public static String getResponseBody() {
HttpServletResponse response = getResponse();
if (response instanceof RepeatReadResponseWrapper wrapper && !wrapper.isStreamingResponse()) {
String body = wrapper.getResponseContent();
return JSONUtil.isTypeJSON(body) ? body : null;
}
return null;
}
/**
* 获取响应参数
*
* @return {@link Map }<{@link String }, {@link Object }>
* @since 2.11.0
*/
public static Map<String, Object> getResponseParams() {
String body = getResponseBody();
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body) ? JSONUtil.toBean(body, Map.class) : null;
}
/**
* 获取 HTTP Session
*
@@ -349,20 +188,4 @@ public class ServletUtils extends JakartaServletUtil {
public static void writeJSON(HttpServletResponse response, String data) {
write(response, data, MediaType.APPLICATION_JSON_VALUE);
}
/**
* 追加查询字符串
*
* @param queryString 查询字符串
* @return {@link StringBuilder }
*/
private static StringBuilder appendQueryString(String queryString) {
HttpServletRequest request = getRequest();
if (request == null) {
return new StringBuilder();
}
return new StringBuilder().append(request.getRequestURL())
.append(StringConstants.QUESTION_MARK)
.append(queryString);
}
}

View File

@@ -16,6 +16,8 @@
package top.continew.starter.log.aspect;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@@ -23,6 +25,8 @@ import org.aspectj.lang.annotation.Pointcut;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.continew.starter.log.handler.LogHandler;
import top.continew.starter.log.http.servlet.RecordableServletHttpRequest;
import top.continew.starter.log.http.servlet.RecordableServletHttpResponse;
import top.continew.starter.log.model.AccessLogContext;
import top.continew.starter.log.model.LogProperties;
@@ -103,16 +107,22 @@ public class AccessLogAspect {
if (attributes == null) {
return joinPoint.proceed();
}
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
try {
// 开始访问日志记录
logHandler.accessLogStart(AccessLogContext.builder()
.startTime(startTime)
.request(new RecordableServletHttpRequest(request))
.properties(logProperties)
.build());
return joinPoint.proceed();
} finally {
Instant endTime = Instant.now();
logHandler.accessLogFinish(AccessLogContext.builder().endTime(endTime).build());
logHandler.accessLogFinish(AccessLogContext.builder()
.endTime(endTime)
.response(new RecordableServletHttpResponse(response))
.build());
}
}
}

View File

@@ -17,6 +17,8 @@
package top.continew.starter.log.aspect;
import cn.hutool.core.text.CharSequenceUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
@@ -27,6 +29,7 @@ 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.core.util.ServletUtils;
import top.continew.starter.log.annotation.Log;
import top.continew.starter.log.dao.LogDao;
import top.continew.starter.log.handler.LogHandler;
@@ -75,6 +78,7 @@ public class LogAspect {
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Instant startTime = Instant.now();
// 指定规则不记录
HttpServletRequest request = ServletUtils.getRequest();
Method targetMethod = this.getMethod(joinPoint);
Class<?> targetClass = joinPoint.getTarget().getClass();
if (!isRecord(targetMethod, targetClass)) {
@@ -82,7 +86,7 @@ public class LogAspect {
}
String errorMsg = null;
// 开始记录
LogRecord.Started startedLogRecord = logHandler.start(startTime);
LogRecord.Started startedLogRecord = logHandler.start(startTime, request);
try {
// 执行目标方法
return joinPoint.proceed();
@@ -92,7 +96,8 @@ public class LogAspect {
} finally {
try {
Instant endTime = Instant.now();
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, logProperties
HttpServletResponse response = ServletUtils.getResponse();
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, response, logProperties
.getIncludes(), targetMethod, targetClass);
// 记录异常信息
if (errorMsg != null) {

View File

@@ -56,18 +56,14 @@ public class LogFilter extends OncePerRequestFilter {
return;
}
// 包装可重复读取请求及响应
boolean isExcludeUri = logProperties.isMatch(request.getRequestURI());
// 处理可重复读取的请求
HttpServletRequest requestWrapper = (isExcludeUri || !this.isRequestWrapper(request))
? request
: new RepeatReadRequestWrapper(request);
// 处理可重复读取的响应
HttpServletResponse responseWrapper = (isExcludeUri || !this.isResponseWrapper(response))
? response
: new RepeatReadResponseWrapper(response);
filterChain.doFilter(requestWrapper, responseWrapper);
// 如果响应被包装了,复制缓存数据到原始响应

View File

@@ -23,15 +23,20 @@ import com.alibaba.ttl.TransmittableThreadLocal;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.continew.starter.log.annotation.Log;
import top.continew.starter.log.enums.Include;
import top.continew.starter.log.http.RecordableHttpRequest;
import top.continew.starter.log.http.RecordableHttpResponse;
import top.continew.starter.log.http.servlet.RecordableServletHttpRequest;
import top.continew.starter.log.http.servlet.RecordableServletHttpResponse;
import top.continew.starter.log.model.AccessLogContext;
import top.continew.starter.log.model.AccessLogProperties;
import top.continew.starter.log.model.LogRecord;
import top.continew.starter.log.util.AccessLogUtils;
import top.continew.starter.core.util.ServletUtils;
import java.lang.reflect.Method;
import java.time.Duration;
@@ -74,18 +79,19 @@ public abstract class AbstractLogHandler implements LogHandler {
}
@Override
public LogRecord.Started start(Instant startTime) {
return LogRecord.start(startTime);
public LogRecord.Started start(Instant startTime, HttpServletRequest request) {
return LogRecord.start(startTime, new RecordableServletHttpRequest(request));
}
@Override
public LogRecord finish(LogRecord.Started started,
Instant endTime,
HttpServletResponse response,
Set<Include> includes,
Method targetMethod,
Class<?> targetClass) {
Set<Include> includeSet = this.getIncludes(includes, targetMethod, targetClass);
LogRecord logRecord = this.finish(started, endTime, includeSet);
LogRecord logRecord = this.finish(started, endTime, response, includeSet);
// 记录日志描述
if (includeSet.contains(Include.DESCRIPTION)) {
this.logDescription(logRecord, targetMethod);
@@ -98,8 +104,11 @@ public abstract class AbstractLogHandler implements LogHandler {
}
@Override
public LogRecord finish(LogRecord.Started started, Instant endTime, Set<Include> includes) {
return started.finish(endTime, includes);
public LogRecord finish(LogRecord.Started started,
Instant endTime,
HttpServletResponse response,
Set<Include> includes) {
return started.finish(endTime, new RecordableServletHttpResponse(response), includes);
}
/**
@@ -186,30 +195,33 @@ public abstract class AbstractLogHandler implements LogHandler {
}
@Override
public void accessLogStart(AccessLogContext accessLogContext) {
AccessLogProperties properties = accessLogContext.getProperties().getAccessLog();
// 是否需要打印 规则: 是否打印开关 或 放行路径
if (!properties.isEnabled() || AccessLogUtils.exclusionPath(accessLogContext.getProperties(), ServletUtils
.getRequestPath())) {
public void accessLogStart(AccessLogContext context) {
AccessLogProperties properties = context.getProperties().getAccessLog();
// 是否需要打印
if (!properties.isEnabled() || AccessLogUtils.exclusionPath(context.getProperties(), context.getRequest()
.getPath())) {
return;
}
// 构建上下文
logContextThread.set(accessLogContext);
String param = AccessLogUtils.getParam(properties);
log.info(param != null ? "[Start] [{}] {} param: {}" : "[Start] [{}] {}", ServletUtils
.getRequestMethod(), ServletUtils.getRequestPath(), param);
logContextThread.set(context);
RecordableHttpRequest request = context.getRequest();
String param = AccessLogUtils.getParam(request, properties);
log.info(param != null ? "[Start] [{}] {} param: {}" : "[Start] [{}] {}", request.getMethod(), request
.getPath(), param);
}
@Override
public void accessLogFinish(AccessLogContext accessLogContext) {
public void accessLogFinish(AccessLogContext context) {
AccessLogContext logContext = logContextThread.get();
if (ObjectUtil.isEmpty(logContext)) {
return;
}
try {
Duration timeTaken = Duration.between(logContext.getStartTime(), accessLogContext.getEndTime());
log.info("[End] [{}] {} {} {}ms", ServletUtils.getRequestMethod(), ServletUtils
.getRequestPath(), ServletUtils.getResponseStatus(), timeTaken.toMillis());
RecordableHttpRequest request = logContext.getRequest();
RecordableHttpResponse response = context.getResponse();
Duration timeTaken = Duration.between(logContext.getStartTime(), context.getEndTime());
log.info("[End] [{}] {} {} {}ms", request.getMethod(), request.getPath(), response.getStatus(), timeTaken
.toMillis());
} finally {
logContextThread.remove();
}

View File

@@ -16,6 +16,8 @@
package top.continew.starter.log.handler;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import top.continew.starter.log.enums.Include;
import top.continew.starter.log.model.AccessLogContext;
import top.continew.starter.log.model.LogRecord;
@@ -46,25 +48,28 @@ public interface LogHandler {
* 开始日志记录
*
* @param startTime 开始时间
* @param request 请求对象
* @return 日志记录器
*/
LogRecord.Started start(Instant startTime);
LogRecord.Started start(Instant startTime, HttpServletRequest request);
/**
* 结束日志记录
*
* @param started 开始日志记录器
* @param endTime 结束时间
* @param response 响应对象
* @param includes 包含信息
* @return 日志记录
*/
LogRecord finish(LogRecord.Started started, Instant endTime, Set<Include> includes);
LogRecord finish(LogRecord.Started started, Instant endTime, HttpServletResponse response, Set<Include> includes);
/**
* 结束日志记录
*
* @param started 开始日志记录器-
* @param endTime 结束时间
* @param response 响应对象
* @param includes 包含信息
* @param targetMethod 目标方法
* @param targetClass 目标类
@@ -72,6 +77,7 @@ public interface LogHandler {
*/
LogRecord finish(LogRecord.Started started,
Instant endTime,
HttpServletResponse response,
Set<Include> includes,
Method targetMethod,
Class<?> targetClass);

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.log.http;
import java.net.URI;
import java.util.Map;
/**
* 可记录的 HTTP 请求信息
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Phillip WebbSpring Boot Actuator
* @author Charles7c
* @author echo
* @see RecordableHttpResponse
* @since 1.1.0
*/
public interface RecordableHttpRequest {
/**
* 获取请求方式
*
* @return 请求方式
*/
String getMethod();
/**
* 获取 URL
*
* @return URL
*/
URI getUrl();
/**
* 获取路径
* <p>/foo/bar</p>
*
* @return 路径
* @since 2.10.0
*/
String getPath();
/**
* 获取请求头
*
* @return 请求头
*/
Map<String, String> getHeaders();
/**
* 获取请求体
*
* @return 请求体
*/
String getBody();
/**
* 获取请求参数
*
* @return 请求参数
*/
String getParams();
/**
* 获取 IP
*
* @return IP
*/
String getIp();
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.log.http;
import java.util.Map;
/**
* 可记录的 HTTP 响应信息
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Charles7c
* @see RecordableHttpRequest
* @since 1.1.0
*/
public interface RecordableHttpResponse {
/**
* 获取状态码
*
* @return 状态码
*/
int getStatus();
/**
* 获取响应头
*
* @return 响应头
*/
Map<String, String> getHeaders();
/**
* 获取响应体
*
* @return 响应体
*/
String getBody();
/**
* 获取响应参数
*
* @return 响应参数
*/
String getParams();
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.log.http.servlet;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.util.UriUtils;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.wrapper.RepeatReadRequestWrapper;
import top.continew.starter.log.http.RecordableHttpRequest;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* 可记录的 HTTP 请求信息适配器
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Charles7c
* @author echo
* @since 1.1.0
*/
public final class RecordableServletHttpRequest implements RecordableHttpRequest {
private final HttpServletRequest request;
public RecordableServletHttpRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public String getMethod() {
return request.getMethod();
}
@Override
public URI getUrl() {
String queryString = request.getQueryString();
if (CharSequenceUtil.isBlank(queryString)) {
return URI.create(request.getRequestURL().toString());
}
try {
StringBuilder urlBuilder = this.appendQueryString(queryString);
return new URI(urlBuilder.toString());
} catch (URISyntaxException e) {
String encoded = UriUtils.encodeQuery(queryString, StandardCharsets.UTF_8);
StringBuilder urlBuilder = this.appendQueryString(encoded);
return URI.create(urlBuilder.toString());
}
}
@Override
public String getPath() {
return request.getRequestURI();
}
@Override
public Map<String, String> getHeaders() {
return JakartaServletUtil.getHeaderMap(request);
}
@Override
public String getBody() {
if (request instanceof RepeatReadRequestWrapper wrapper && !wrapper.isMultipartContent(request)) {
String body = JakartaServletUtil.getBody(request);
return JSONUtil.isTypeJSON(body) ? body : null;
}
return null;
}
@Override
public String getParams() {
String body = this.getBody();
return CharSequenceUtil.isNotBlank(body) ? body : JSONUtil.toJsonStr(JakartaServletUtil.getParamMap(request));
}
@Override
public String getIp() {
return JakartaServletUtil.getClientIP(request);
}
/**
* URL 追加查询字符串
*
* @param queryString 查询字符串
* @return StringBuilder
*/
private StringBuilder appendQueryString(String queryString) {
return new StringBuilder().append(request.getRequestURL())
.append(StringConstants.QUESTION_MARK)
.append(queryString);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.log.http.servlet;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletResponse;
import top.continew.starter.core.util.ServletUtils;
import top.continew.starter.core.wrapper.RepeatReadResponseWrapper;
import top.continew.starter.log.http.RecordableHttpResponse;
import java.util.Map;
/**
* 可记录的 HTTP 响应信息适配器
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Charles7c
* @author echo
* @since 1.1.0
*/
public final class RecordableServletHttpResponse implements RecordableHttpResponse {
private final HttpServletResponse response;
private final int status;
public RecordableServletHttpResponse(HttpServletResponse response) {
this.response = response;
this.status = response.getStatus();
}
@Override
public int getStatus() {
return this.status;
}
@Override
public Map<String, String> getHeaders() {
return ServletUtils.getHeaderMap(response);
}
@Override
public String getBody() {
if (response instanceof RepeatReadResponseWrapper wrapper && !wrapper.isStreamingResponse()) {
String body = wrapper.getResponseContent();
return JSONUtil.isTypeJSON(body) ? body : null;
}
return null;
}
@Override
public String getParams() {
return this.getBody();
}
}

View File

@@ -16,6 +16,9 @@
package top.continew.starter.log.model;
import top.continew.starter.log.http.RecordableHttpRequest;
import top.continew.starter.log.http.RecordableHttpResponse;
import java.time.Instant;
/**
@@ -36,6 +39,16 @@ public class AccessLogContext {
*/
private Instant endTime;
/**
* 请求信息
*/
private final RecordableHttpRequest request;
/**
* 响应信息
*/
private final RecordableHttpResponse response;
/**
* 配置信息
*/
@@ -44,6 +57,8 @@ public class AccessLogContext {
private AccessLogContext(Builder builder) {
this.startTime = builder.startTime;
this.endTime = builder.endTime;
this.request = builder.request;
this.response = builder.response;
this.properties = builder.properties;
}
@@ -55,6 +70,14 @@ public class AccessLogContext {
return endTime;
}
public RecordableHttpRequest getRequest() {
return request;
}
public RecordableHttpResponse getResponse() {
return response;
}
public LogProperties getProperties() {
return properties;
}
@@ -74,6 +97,8 @@ public class AccessLogContext {
private Instant startTime;
private Instant endTime;
private RecordableHttpRequest request;
private RecordableHttpResponse response;
private LogProperties properties;
private Builder() {
@@ -89,6 +114,16 @@ public class AccessLogContext {
return this;
}
public Builder request(RecordableHttpRequest request) {
this.request = request;
return this;
}
public Builder response(RecordableHttpResponse response) {
this.response = response;
return this;
}
public Builder properties(LogProperties properties) {
this.properties = properties;
return this;

View File

@@ -17,6 +17,8 @@
package top.continew.starter.log.model;
import top.continew.starter.log.enums.Include;
import top.continew.starter.log.http.RecordableHttpRequest;
import top.continew.starter.log.http.RecordableHttpResponse;
import java.time.Duration;
import java.time.Instant;
@@ -78,20 +80,22 @@ public class LogRecord {
/**
* 开始记录日志
*
* @param request 请求信息
* @return 日志记录器
*/
public static Started start() {
return start(Instant.now());
public static Started start(RecordableHttpRequest request) {
return start(Instant.now(), request);
}
/**
* 开始记录日志
*
* @param timestamp 开始时间
* @param request 请求信息
* @return 日志记录器
*/
public static Started start(Instant timestamp) {
return new Started(timestamp);
public static Started start(Instant timestamp, RecordableHttpRequest request) {
return new Started(timestamp, request);
}
/**
@@ -101,20 +105,24 @@ public class LogRecord {
private final Instant timestamp;
private Started(Instant timestamp) {
private final RecordableHttpRequest request;
private Started(Instant timestamp, RecordableHttpRequest request) {
this.timestamp = timestamp;
this.request = request;
}
/**
* 结束日志记录
*
* @param timestamp 结束时间
* @param response 响应信息
* @param includes 包含信息
* @return 日志记录
*/
public LogRecord finish(Instant timestamp, Set<Include> includes) {
LogRequest logRequest = new LogRequest(includes);
LogResponse logResponse = new LogResponse(includes);
public LogRecord finish(Instant timestamp, RecordableHttpResponse response, Set<Include> includes) {
LogRequest logRequest = new LogRequest(this.request, includes);
LogResponse logResponse = new LogResponse(response, includes);
Duration duration = Duration.between(this.timestamp, timestamp);
return new LogRecord(this.timestamp, logRequest, logResponse, duration);
}

View File

@@ -22,6 +22,7 @@ import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.IpUtils;
import top.continew.starter.core.util.ServletUtils;
import top.continew.starter.log.enums.Include;
import top.continew.starter.log.http.RecordableHttpRequest;
import java.net.URI;
import java.util.Map;
@@ -63,7 +64,7 @@ public class LogRequest {
/**
* 请求参数
*/
private Map<String, Object> param;
private String params;
/**
* IP 归属地
@@ -80,15 +81,15 @@ public class LogRequest {
*/
private String os;
public LogRequest(Set<Include> includes) {
this.method = ServletUtils.getRequestMethod();
this.url = ServletUtils.getRequestUrl();
this.ip = ServletUtils.getRequestIp();
this.headers = (includes.contains(Include.REQUEST_HEADERS)) ? ServletUtils.getRequestHeaders() : null;
public LogRequest(RecordableHttpRequest request, Set<Include> includes) {
this.method = request.getMethod();
this.url = request.getUrl();
this.ip = request.getIp();
this.headers = (includes.contains(Include.REQUEST_HEADERS)) ? request.getHeaders() : null;
if (includes.contains(Include.REQUEST_BODY)) {
this.body = ServletUtils.getRequestBody();
this.body = request.getBody();
} else if (includes.contains(Include.REQUEST_PARAM)) {
this.param = ServletUtils.getRequestParams();
this.params = request.getParams();
}
this.address = (includes.contains(Include.IP_ADDRESS))
? ExceptionUtils.exToNull(() -> IpUtils.getIpv4Address(this.ip))
@@ -148,12 +149,12 @@ public class LogRequest {
this.body = body;
}
public Map<String, Object> getParam() {
return param;
public String getParams() {
return params;
}
public void setParam(Map<String, Object> param) {
this.param = param;
public void setParams(String params) {
this.params = params;
}
public String getAddress() {

View File

@@ -17,7 +17,7 @@
package top.continew.starter.log.model;
import top.continew.starter.log.enums.Include;
import top.continew.starter.core.util.ServletUtils;
import top.continew.starter.log.http.RecordableHttpResponse;
import java.util.Map;
import java.util.Set;
@@ -48,15 +48,15 @@ public class LogResponse {
/**
* 响应参数
*/
private Map<String, Object> param;
private String params;
public LogResponse(Set<Include> includes) {
this.status = ServletUtils.getResponseStatus();
this.headers = (includes.contains(Include.RESPONSE_HEADERS)) ? ServletUtils.getResponseHeaders() : null;
public LogResponse(RecordableHttpResponse response, Set<Include> includes) {
this.status = response.getStatus();
this.headers = (includes.contains(Include.RESPONSE_HEADERS)) ? response.getHeaders() : null;
if (includes.contains(Include.RESPONSE_BODY)) {
this.body = ServletUtils.getResponseBody();
this.body = response.getBody();
} else if (includes.contains(Include.RESPONSE_PARAM)) {
this.param = ServletUtils.getResponseParams();
this.params = response.getParams();
}
}
@@ -84,11 +84,11 @@ public class LogResponse {
this.body = body;
}
public Map<String, Object> getParam() {
return param;
public String getParams() {
return params;
}
public void setParam(Map<String, Object> param) {
this.param = param;
public void setParams(String params) {
this.params = params;
}
}

View File

@@ -17,14 +17,15 @@
package top.continew.starter.log.util;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import top.continew.starter.core.util.SpringWebUtils;
import top.continew.starter.log.http.RecordableHttpRequest;
import top.continew.starter.log.model.AccessLogProperties;
import top.continew.starter.log.model.LogProperties;
import top.continew.starter.core.util.ServletUtils;
import top.continew.starter.core.util.SpringWebUtils;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -48,37 +49,42 @@ public class AccessLogUtils {
/**
* 获取参数信息
*
* @param request 请求对象
* @param properties 属性
* @return {@link String }
*/
public static String getParam(AccessLogProperties properties) {
public static String getParam(RecordableHttpRequest request, AccessLogProperties properties) {
// 是否需要打印请求参数
if (!properties.isPrintRequestParam()) {
return null;
}
// 参数为空返回空
Object params;
try {
params = getAccessLogReqParam();
} catch (Exception e) {
String params = request.getParams();
if (CharSequenceUtil.isBlank(params)) {
return null;
}
if (ObjectUtil.isEmpty(params)) {
return null;
Object paramObj;
if (JSONUtil.isTypeJSONArray(params)) {
paramObj = JSONUtil.toBean(params, List.class);
} else if (JSONUtil.isTypeJSONObject(params)) {
paramObj = JSONUtil.toBean(params, Map.class);
} else {
paramObj = params;
}
// 是否需要对特定入参脱敏
if (properties.isParamSensitive()) {
params = processSensitiveParams(params, properties.getSensitiveParams());
paramObj = processSensitiveParams(paramObj, properties.getSensitiveParams());
}
// 是否自动截断超长参数值
if (properties.isLongParamTruncate()) {
params = processTruncateLongParams(params, properties.getLongParamThreshold(), properties
paramObj = processTruncateLongParams(paramObj, properties.getLongParamThreshold(), properties
.getLongParamMaxLength(), properties.getLongParamSuffix());
}
return JSONUtil.toJsonStr(params);
return JSONUtil.toJsonStr(paramObj);
}
/**
@@ -106,7 +112,7 @@ public class AccessLogUtils {
return filterSensitiveParams((Map<String, Object>)params, sensitiveParams);
} else if (params instanceof List) {
return ((List<?>)params).stream()
.filter(item -> item instanceof Map)
.filter(Map.class::isInstance)
.map(item -> filterSensitiveParams((Map<String, Object>)item, sensitiveParams))
.collect(Collectors.toList());
}
@@ -183,25 +189,4 @@ public class AccessLogUtils {
}
return truncatedParams;
}
/**
* 获取访问日志请求参数
*
* @return {@link Object }
*/
private static Object getAccessLogReqParam() {
String body = ServletUtils.getRequestBody();
if (CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)) {
try {
if (JSONUtil.isTypeJSONArray(body)) {
return JSONUtil.toBean(body, List.class);
} else {
return JSONUtil.toBean(body, Map.class);
}
} catch (Exception e) {
return null;
}
}
return Collections.unmodifiableMap(ServletUtils.getRequestParams());
}
}

View File

@@ -24,10 +24,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import top.continew.starter.log.dao.LogDao;
import top.continew.starter.log.handler.LogHandler;
import top.continew.starter.log.http.servlet.RecordableServletHttpRequest;
import top.continew.starter.log.http.servlet.RecordableServletHttpResponse;
import top.continew.starter.log.model.AccessLogContext;
import top.continew.starter.log.model.LogProperties;
import top.continew.starter.log.dao.LogDao;
import top.continew.starter.log.handler.LogHandler;
import top.continew.starter.log.model.LogRecord;
import java.lang.reflect.Method;
@@ -58,10 +60,15 @@ public class LogInterceptor implements HandlerInterceptor {
@NonNull HttpServletResponse response,
@NonNull Object handler) {
Instant startTime = Instant.now();
logHandler.accessLogStart(AccessLogContext.builder().startTime(startTime).properties(logProperties).build());
// 访问日志
logHandler.accessLogStart(AccessLogContext.builder()
.startTime(startTime)
.request(new RecordableServletHttpRequest(request))
.properties(logProperties)
.build());
// 开始日志记录
if (this.isRecord(handler)) {
LogRecord.Started startedLogRecord = logHandler.start(startTime);
LogRecord.Started startedLogRecord = logHandler.start(startTime, request);
logTtl.set(startedLogRecord);
}
return true;
@@ -74,7 +81,11 @@ public class LogInterceptor implements HandlerInterceptor {
Exception e) {
try {
Instant endTime = Instant.now();
logHandler.accessLogFinish(AccessLogContext.builder().endTime(endTime).build());
// 访问日志
logHandler.accessLogFinish(AccessLogContext.builder()
.endTime(endTime)
.response(new RecordableServletHttpResponse(response))
.build());
LogRecord.Started startedLogRecord = logTtl.get();
if (startedLogRecord == null) {
return;
@@ -83,7 +94,7 @@ public class LogInterceptor implements HandlerInterceptor {
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method targetMethod = handlerMethod.getMethod();
Class<?> targetClass = handlerMethod.getBeanType();
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, logProperties
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, response, logProperties
.getIncludes(), targetMethod, targetClass);
logDao.add(logRecord);
} catch (Exception ex) {