mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-09 08:57:17 +08:00
refactor(log/core): 重构请求和响应信息获取
web 模块 ServletUtils整理 log/core 模块 删除 RecordableHttpRequest和 RecordableHttpResponse
This commit is contained in:
@@ -16,8 +16,6 @@
|
||||
|
||||
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;
|
||||
@@ -25,8 +23,6 @@ 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;
|
||||
|
||||
@@ -107,22 +103,16 @@ 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)
|
||||
.response(new RecordableServletHttpResponse(response, response.getStatus()))
|
||||
.build());
|
||||
logHandler.accessLogFinish(AccessLogContext.builder().endTime(endTime).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,8 +18,6 @@ package top.continew.starter.log.aspect;
|
||||
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
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;
|
||||
@@ -35,7 +33,6 @@ import top.continew.starter.log.dao.LogDao;
|
||||
import top.continew.starter.log.handler.LogHandler;
|
||||
import top.continew.starter.log.model.LogProperties;
|
||||
import top.continew.starter.log.model.LogRecord;
|
||||
import top.continew.starter.web.util.SpringWebUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Instant;
|
||||
@@ -79,7 +76,6 @@ public class LogAspect {
|
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
Instant startTime = Instant.now();
|
||||
// 指定规则不记录
|
||||
HttpServletRequest request = SpringWebUtils.getRequest();
|
||||
Method targetMethod = this.getMethod(joinPoint);
|
||||
Class<?> targetClass = joinPoint.getTarget().getClass();
|
||||
if (!isRequestRecord(targetMethod, targetClass)) {
|
||||
@@ -87,7 +83,7 @@ public class LogAspect {
|
||||
}
|
||||
String errorMsg = null;
|
||||
// 开始记录
|
||||
LogRecord.Started startedLogRecord = logHandler.start(startTime, request);
|
||||
LogRecord.Started startedLogRecord = logHandler.start(startTime);
|
||||
try {
|
||||
// 执行目标方法
|
||||
return joinPoint.proceed();
|
||||
@@ -97,8 +93,7 @@ public class LogAspect {
|
||||
} finally {
|
||||
try {
|
||||
Instant endTime = Instant.now();
|
||||
HttpServletResponse response = SpringWebUtils.getResponse();
|
||||
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, response, logProperties
|
||||
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, logProperties
|
||||
.getIncludes(), targetMethod, targetClass);
|
||||
// 记录异常信息
|
||||
if (errorMsg != null) {
|
||||
|
@@ -22,20 +22,15 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
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.web.util.ServletUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
@@ -55,19 +50,18 @@ public abstract class AbstractLogHandler implements LogHandler {
|
||||
private final TransmittableThreadLocal<AccessLogContext> logContextThread = new TransmittableThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public LogRecord.Started start(Instant startTime, HttpServletRequest request) {
|
||||
return LogRecord.start(startTime, new RecordableServletHttpRequest(request));
|
||||
public LogRecord.Started start(Instant startTime) {
|
||||
return LogRecord.start(startTime);
|
||||
}
|
||||
|
||||
@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, response, includeSet);
|
||||
LogRecord logRecord = this.finish(started, endTime, includeSet);
|
||||
// 记录日志描述
|
||||
if (includeSet.contains(Include.DESCRIPTION)) {
|
||||
this.logDescription(logRecord, targetMethod);
|
||||
@@ -80,11 +74,8 @@ public abstract class AbstractLogHandler implements LogHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogRecord finish(LogRecord.Started started,
|
||||
Instant endTime,
|
||||
HttpServletResponse response,
|
||||
Set<Include> includes) {
|
||||
return started.finish(endTime, new RecordableServletHttpResponse(response, response.getStatus()), includes);
|
||||
public LogRecord finish(LogRecord.Started started, Instant endTime, Set<Include> includes) {
|
||||
return started.finish(endTime, includes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,17 +165,15 @@ public abstract class AbstractLogHandler implements LogHandler {
|
||||
public void accessLogStart(AccessLogContext accessLogContext) {
|
||||
AccessLogProperties properties = accessLogContext.getProperties().getAccessLog();
|
||||
// 是否需要打印 规则: 是否打印开关 或 放行路径
|
||||
if (!properties.isEnabled() || AccessLogUtils.exclusionPath(accessLogContext.getProperties(), accessLogContext
|
||||
.getRequest()
|
||||
.getPath())) {
|
||||
if (!properties.isEnabled() || AccessLogUtils.exclusionPath(accessLogContext.getProperties(), ServletUtils
|
||||
.getReqPath())) {
|
||||
return;
|
||||
}
|
||||
// 构建上下文
|
||||
logContextThread.set(accessLogContext);
|
||||
RecordableHttpRequest request = accessLogContext.getRequest();
|
||||
String path = request.getPath();
|
||||
String param = AccessLogUtils.getParam(request, properties);
|
||||
log.info(param != null ? "[Start] [{}] {} param: {}" : "[Start] [{}] {}", request.getMethod(), path, param);
|
||||
String param = AccessLogUtils.getParam(properties);
|
||||
log.info(param != null ? "[Start] [{}] {} param: {}" : "[Start] [{}] {}", ServletUtils
|
||||
.getReqMethod(), ServletUtils.getReqPath(), param);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -194,11 +183,9 @@ public abstract class AbstractLogHandler implements LogHandler {
|
||||
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());
|
||||
log.info("[End] [{}] {} {} {}ms", ServletUtils.getReqMethod(), ServletUtils.getReqPath(), ServletUtils
|
||||
.getRespStatus(), timeTaken.toMillis());
|
||||
} finally {
|
||||
logContextThread.remove();
|
||||
}
|
||||
|
@@ -16,8 +16,6 @@
|
||||
|
||||
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;
|
||||
@@ -39,28 +37,25 @@ public interface LogHandler {
|
||||
* 开始日志记录
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param request 请求对象
|
||||
* @return 日志记录器
|
||||
*/
|
||||
LogRecord.Started start(Instant startTime, HttpServletRequest request);
|
||||
LogRecord.Started start(Instant startTime);
|
||||
|
||||
/**
|
||||
* 结束日志记录
|
||||
*
|
||||
* @param started 开始日志记录器
|
||||
* @param endTime 结束时间
|
||||
* @param response 响应对象
|
||||
* @param includes 包含信息
|
||||
* @return 日志记录
|
||||
*/
|
||||
LogRecord finish(LogRecord.Started started, Instant endTime, HttpServletResponse response, Set<Include> includes);
|
||||
LogRecord finish(LogRecord.Started started, Instant endTime, Set<Include> includes);
|
||||
|
||||
/**
|
||||
* 结束日志记录
|
||||
*
|
||||
* @param started 开始日志记录器-
|
||||
* @param endTime 结束时间
|
||||
* @param response 响应对象
|
||||
* @param includes 包含信息
|
||||
* @param targetMethod 目标方法
|
||||
* @param targetClass 目标类
|
||||
@@ -68,7 +63,6 @@ public interface LogHandler {
|
||||
*/
|
||||
LogRecord finish(LogRecord.Started started,
|
||||
Instant endTime,
|
||||
HttpServletResponse response,
|
||||
Set<Include> includes,
|
||||
Method targetMethod,
|
||||
Class<?> targetClass);
|
||||
|
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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 Wilkinson(Spring Boot Actuator)
|
||||
* @author Phillip Webb(Spring 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 请求参数
|
||||
*/
|
||||
Map<String, Object> getParam();
|
||||
|
||||
/**
|
||||
* 获取 IP
|
||||
*
|
||||
* @return IP
|
||||
*/
|
||||
String getIp();
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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 Wilkinson(Spring 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 响应参数
|
||||
*/
|
||||
Map<String, Object> getParam();
|
||||
}
|
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* 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.log.http.RecordableHttpRequest;
|
||||
import top.continew.starter.web.util.RepeatReadRequestWrapper;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 可记录的 HTTP 请求信息适配器
|
||||
*
|
||||
* @author Andy Wilkinson(Spring 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 Map<String, Object> getParam() {
|
||||
String body = this.getBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)
|
||||
? JSONUtil.toBean(body, Map.class)
|
||||
: Collections.unmodifiableMap(JakartaServletUtil.getParamMap(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIp() {
|
||||
return JakartaServletUtil.getClientIP(request);
|
||||
}
|
||||
|
||||
private StringBuilder appendQueryString(String queryString) {
|
||||
return new StringBuilder().append(request.getRequestURL())
|
||||
.append(StringConstants.QUESTION_MARK)
|
||||
.append(queryString);
|
||||
}
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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.json.JSONUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
import top.continew.starter.web.util.RepeatReadResponseWrapper;
|
||||
import top.continew.starter.log.http.RecordableHttpResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 可记录的 HTTP 响应信息适配器
|
||||
*
|
||||
* @author Andy Wilkinson(Spring 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, int status) {
|
||||
this.response = response;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@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 Map<String, Object> getParam() {
|
||||
String body = this.getBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body) ? JSONUtil.toBean(body, Map.class) : null;
|
||||
}
|
||||
}
|
@@ -16,9 +16,6 @@
|
||||
|
||||
package top.continew.starter.log.model;
|
||||
|
||||
import top.continew.starter.log.http.RecordableHttpRequest;
|
||||
import top.continew.starter.log.http.RecordableHttpResponse;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
@@ -39,16 +36,6 @@ public class AccessLogContext {
|
||||
*/
|
||||
private Instant endTime;
|
||||
|
||||
/**
|
||||
* 请求信息
|
||||
*/
|
||||
private final RecordableHttpRequest request;
|
||||
|
||||
/**
|
||||
* 响应信息
|
||||
*/
|
||||
private final RecordableHttpResponse response;
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
*/
|
||||
@@ -57,8 +44,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -70,14 +55,6 @@ public class AccessLogContext {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public RecordableHttpRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public RecordableHttpResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public LogProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
@@ -97,8 +74,6 @@ public class AccessLogContext {
|
||||
|
||||
private Instant startTime;
|
||||
private Instant endTime;
|
||||
private RecordableHttpRequest request;
|
||||
private RecordableHttpResponse response;
|
||||
private LogProperties properties;
|
||||
|
||||
private Builder() {
|
||||
@@ -114,16 +89,6 @@ 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;
|
||||
|
@@ -17,8 +17,6 @@
|
||||
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;
|
||||
@@ -80,22 +78,20 @@ public class LogRecord {
|
||||
/**
|
||||
* 开始记录日志
|
||||
*
|
||||
* @param request 请求信息
|
||||
* @return 日志记录器
|
||||
*/
|
||||
public static Started start(RecordableHttpRequest request) {
|
||||
return start(Instant.now(), request);
|
||||
public static Started start() {
|
||||
return start(Instant.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始记录日志
|
||||
*
|
||||
* @param timestamp 开始时间
|
||||
* @param request 请求信息
|
||||
* @return 日志记录器
|
||||
*/
|
||||
public static Started start(Instant timestamp, RecordableHttpRequest request) {
|
||||
return new Started(timestamp, request);
|
||||
public static Started start(Instant timestamp) {
|
||||
return new Started(timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,24 +101,20 @@ public class LogRecord {
|
||||
|
||||
private final Instant timestamp;
|
||||
|
||||
private final RecordableHttpRequest request;
|
||||
|
||||
private Started(Instant timestamp, RecordableHttpRequest request) {
|
||||
private Started(Instant timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束日志记录
|
||||
*
|
||||
* @param timestamp 结束时间
|
||||
* @param response 响应信息
|
||||
* @param includes 包含信息
|
||||
* @return 日志记录
|
||||
*/
|
||||
public LogRecord finish(Instant timestamp, RecordableHttpResponse response, Set<Include> includes) {
|
||||
LogRequest logRequest = new LogRequest(this.request, includes);
|
||||
LogResponse logResponse = new LogResponse(response, includes);
|
||||
public LogRecord finish(Instant timestamp, Set<Include> includes) {
|
||||
LogRequest logRequest = new LogRequest(includes);
|
||||
LogResponse logResponse = new LogResponse(includes);
|
||||
Duration duration = Duration.between(this.timestamp, timestamp);
|
||||
return new LogRecord(this.timestamp, logRequest, logResponse, duration);
|
||||
}
|
||||
|
@@ -22,7 +22,6 @@ import top.continew.starter.core.util.ExceptionUtils;
|
||||
import top.continew.starter.core.util.IpUtils;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
import top.continew.starter.log.enums.Include;
|
||||
import top.continew.starter.log.http.RecordableHttpRequest;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
@@ -81,15 +80,15 @@ public class LogRequest {
|
||||
*/
|
||||
private String os;
|
||||
|
||||
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;
|
||||
public LogRequest(Set<Include> includes) {
|
||||
this.method = ServletUtils.getReqMethod();
|
||||
this.url = ServletUtils.getReqUrl();
|
||||
this.ip = ServletUtils.getReqIp();
|
||||
this.headers = (includes.contains(Include.REQUEST_HEADERS)) ? ServletUtils.getReqHeaders() : null;
|
||||
if (includes.contains(Include.REQUEST_BODY)) {
|
||||
this.body = request.getBody();
|
||||
this.body = ServletUtils.getReqBody();
|
||||
} else if (includes.contains(Include.REQUEST_PARAM)) {
|
||||
this.param = request.getParam();
|
||||
this.param = ServletUtils.getReqParam();
|
||||
}
|
||||
this.address = (includes.contains(Include.IP_ADDRESS))
|
||||
? ExceptionUtils.exToNull(() -> IpUtils.getIpv4Address(this.ip))
|
||||
|
@@ -17,7 +17,7 @@
|
||||
package top.continew.starter.log.model;
|
||||
|
||||
import top.continew.starter.log.enums.Include;
|
||||
import top.continew.starter.log.http.RecordableHttpResponse;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -50,13 +50,13 @@ public class LogResponse {
|
||||
*/
|
||||
private Map<String, Object> param;
|
||||
|
||||
public LogResponse(RecordableHttpResponse response, Set<Include> includes) {
|
||||
this.status = response.getStatus();
|
||||
this.headers = (includes.contains(Include.RESPONSE_HEADERS)) ? response.getHeaders() : null;
|
||||
public LogResponse(Set<Include> includes) {
|
||||
this.status = ServletUtils.getRespStatus();
|
||||
this.headers = (includes.contains(Include.RESPONSE_HEADERS)) ? ServletUtils.getRespHeaders() : null;
|
||||
if (includes.contains(Include.RESPONSE_BODY)) {
|
||||
this.body = response.getBody();
|
||||
this.body = ServletUtils.getRespBody();
|
||||
} else if (includes.contains(Include.RESPONSE_PARAM)) {
|
||||
this.param = response.getParam();
|
||||
this.param = ServletUtils.getRespParam();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,9 +18,9 @@ package top.continew.starter.log.util;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
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.web.util.ServletUtils;
|
||||
import top.continew.starter.web.util.SpringWebUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -45,11 +45,10 @@ public class AccessLogUtils {
|
||||
/**
|
||||
* 获取参数信息
|
||||
*
|
||||
* @param request 请求
|
||||
* @param properties 属性
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String getParam(RecordableHttpRequest request, AccessLogProperties properties) {
|
||||
public static String getParam(AccessLogProperties properties) {
|
||||
// 是否需要打印请求参数
|
||||
if (!properties.isPrintRequestParam()) {
|
||||
return null;
|
||||
@@ -58,7 +57,7 @@ public class AccessLogUtils {
|
||||
// 参数为空返回空
|
||||
Map<String, Object> params;
|
||||
try {
|
||||
params = request.getParam();
|
||||
params = ServletUtils.getReqParam();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
@@ -27,12 +27,10 @@ import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import top.continew.starter.log.annotation.Log;
|
||||
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.AccessLogContext;
|
||||
import top.continew.starter.log.model.LogProperties;
|
||||
import top.continew.starter.log.model.LogRecord;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -64,14 +62,10 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
@NonNull HttpServletResponse response,
|
||||
@NonNull Object handler) {
|
||||
Instant startTime = Instant.now();
|
||||
logHandler.accessLogStart(AccessLogContext.builder()
|
||||
.startTime(startTime)
|
||||
.request(new RecordableServletHttpRequest(request))
|
||||
.properties(logProperties)
|
||||
.build());
|
||||
logHandler.accessLogStart(AccessLogContext.builder().startTime(startTime).properties(logProperties).build());
|
||||
// 开始日志记录
|
||||
if (this.isRequestRecord(handler, request)) {
|
||||
LogRecord.Started startedLogRecord = logHandler.start(startTime, request);
|
||||
LogRecord.Started startedLogRecord = logHandler.start(startTime);
|
||||
logTtl.set(startedLogRecord);
|
||||
}
|
||||
return true;
|
||||
@@ -84,10 +78,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
Exception e) {
|
||||
try {
|
||||
Instant endTime = Instant.now();
|
||||
logHandler.accessLogFinish(AccessLogContext.builder()
|
||||
.endTime(endTime)
|
||||
.response(new RecordableServletHttpResponse(response, response.getStatus()))
|
||||
.build());
|
||||
logHandler.accessLogFinish(AccessLogContext.builder().endTime(endTime).build());
|
||||
LogRecord.Started startedLogRecord = logTtl.get();
|
||||
if (null == startedLogRecord) {
|
||||
return;
|
||||
@@ -96,7 +87,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
HandlerMethod handlerMethod = (HandlerMethod)handler;
|
||||
Method targetMethod = handlerMethod.getMethod();
|
||||
Class<?> targetClass = handlerMethod.getBeanType();
|
||||
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, response, logProperties
|
||||
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, logProperties
|
||||
.getIncludes(), targetMethod, targetClass);
|
||||
logDao.add(logRecord);
|
||||
} catch (Exception ex) {
|
||||
|
@@ -17,14 +17,27 @@
|
||||
package top.continew.starter.web.util;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
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;
|
||||
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 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 工具类
|
||||
@@ -32,11 +45,25 @@ import java.util.Map;
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ServletUtils {
|
||||
public class ServletUtils extends JakartaServletUtil {
|
||||
|
||||
private ServletUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求属性
|
||||
*
|
||||
* @return {@link ServletRequestAttributes }
|
||||
*/
|
||||
public static ServletRequestAttributes getRequestAttributes() {
|
||||
try {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes)attributes;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器及其版本信息
|
||||
*
|
||||
@@ -85,13 +112,164 @@ public class ServletUtils {
|
||||
return userAgent.getOs().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 http request
|
||||
*
|
||||
* @return HttpServletRequest
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes attributes = getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
return attributes.getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求方法
|
||||
*
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String getReqMethod() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getMethod() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session
|
||||
*
|
||||
* @return HttpSession
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getSession() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 请求 字符串参数
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String getReqParameter(String name) {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getParameter(name) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 Ip
|
||||
*
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String getReqIp() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? getClientIP(request) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求头信息
|
||||
*
|
||||
* @return {@link Map }<{@link String }, {@link String }>
|
||||
*/
|
||||
public static Map<String, String> getReqHeaders() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? getHeaderMap(request) : Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求url 包含 query 参数
|
||||
* <p>http://localhost:8000/system/user?page=1&size=10</p>
|
||||
*
|
||||
* @return {@link URI }
|
||||
*/
|
||||
public static URI getReqUrl() {
|
||||
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 }
|
||||
*/
|
||||
public static String getReqPath() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getRequestURI() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 body 参数
|
||||
*
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String getReqBody() {
|
||||
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 }>
|
||||
*/
|
||||
public static Map<String, Object> getReqParam() {
|
||||
String body = getReqBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)
|
||||
? JSONUtil.toBean(body, Map.class)
|
||||
: Collections.unmodifiableMap(JakartaServletUtil.getParamMap(Objects.requireNonNull(getRequest())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 http response
|
||||
*
|
||||
* @return HttpServletResponse
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
ServletRequestAttributes attributes = getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
return attributes.getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应状态
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static int getRespStatus() {
|
||||
HttpServletResponse response = getResponse();
|
||||
return response != null ? response.getStatus() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应所有的头(header)信息
|
||||
*
|
||||
* @param response 响应对象{@link HttpServletResponse}
|
||||
* @return header值
|
||||
*/
|
||||
public static Map<String, String> getHeaderMap(HttpServletResponse response) {
|
||||
public static Map<String, String> getRespHeaders() {
|
||||
HttpServletResponse response = getResponse();
|
||||
if (response == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
final Collection<String> headerNames = response.getHeaderNames();
|
||||
final Map<String, String> headerMap = MapUtil.newHashMap(headerNames.size(), true);
|
||||
for (String name : headerNames) {
|
||||
@@ -99,4 +277,39 @@ public class ServletUtils {
|
||||
}
|
||||
return headerMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应 body 参数
|
||||
*
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String getRespBody() {
|
||||
HttpServletResponse response = getResponse();
|
||||
if (response instanceof RepeatReadResponseWrapper wrapper && !wrapper.isStreamingResponse()) {
|
||||
String body = wrapper.getResponseContent();
|
||||
return JSONUtil.isTypeJSON(body) ? body : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Map<String, Object> getRespParam() {
|
||||
String body = getRespBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body) ? JSONUtil.toBean(body, Map.class) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加查询字符串
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user