feat(log/aop): 新增 log-aop 组件模块(基于 AOP 实现日志记录)

This commit is contained in:
liquor
2024-12-11 09:03:09 +00:00
committed by Charles7c
parent 75874171db
commit 7c3f15a6f6
21 changed files with 614 additions and 11 deletions

View File

@@ -1,65 +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.core.annotation;
import top.continew.starter.log.core.enums.Include;
import java.lang.annotation.*;
/**
* 日志注解
* <p>用于接口方法或类上,辅助 Spring Doc 使用效果最佳</p>
*
* @author Charles7c
* @since 1.1.0
*/
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
/**
* 日志描述(仅用于接口方法上)
* <p>
* 优先级:@Log("描述") > @Operation(summary="描述")
* </p>
*/
String value() default "";
/**
* 所属模块(用于接口方法或类上)
* <p>
* 优先级: 接口方法上的 @Log(module = "模块") > 接口类上的 @Log(module = "模块") > @Tag(name = "模块") 内容
* </p>
*/
String module() default "";
/**
* 包含信息(在全局配置基础上扩展包含信息)
*/
Include[] includes() default {};
/**
* 排除信息(在全局配置基础上减少包含信息)
*/
Include[] excludes() default {};
/**
* 是否忽略日志记录(用于接口方法或类上)
*/
boolean ignore() default false;
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package top.continew.starter.log.core.model;
package top.continew.starter.log.core.http.recordable;
import java.net.URI;
import java.util.Map;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package top.continew.starter.log.core.model;
package top.continew.starter.log.core.http.recordable;
import java.util.Map;

View File

@@ -0,0 +1,104 @@
/*
* 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.core.http.recordable.impl;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.UriUtils;
import org.springframework.web.util.WebUtils;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.log.core.http.recordable.RecordableHttpRequest;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;
/**
* 可记录的 HTTP 请求信息适配器
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Charles7c
*/
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 getIp() {
return JakartaServletUtil.getClientIP(request);
}
@Override
public Map<String, String> getHeaders() {
return JakartaServletUtil.getHeaderMap(request);
}
@Override
public String getBody() {
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (null != wrapper) {
String body = StrUtil.utf8Str(wrapper.getContentAsByteArray());
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(request.getParameterMap());
}
private StringBuilder appendQueryString(String queryString) {
return new StringBuilder().append(request.getRequestURL())
.append(StringConstants.QUESTION_MARK)
.append(queryString);
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.core.http.recordable.impl;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;
import top.continew.starter.log.core.http.recordable.RecordableHttpResponse;
import top.continew.starter.web.util.ServletUtils;
import java.util.Map;
/**
* 可记录的 HTTP 响应信息适配器
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Charles7c
*/
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() {
ContentCachingResponseWrapper wrapper = WebUtils
.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (null != wrapper) {
String body = StrUtil.utf8Str(wrapper.getContentAsByteArray());
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;
}
}

View File

@@ -17,6 +17,8 @@
package top.continew.starter.log.core.model;
import top.continew.starter.log.core.enums.Include;
import top.continew.starter.log.core.http.recordable.RecordableHttpRequest;
import top.continew.starter.log.core.http.recordable.RecordableHttpResponse;
import java.time.Duration;
import java.time.Instant;
@@ -63,6 +65,11 @@ public class LogRecord {
*/
private final Instant timestamp;
/**
* 错误信息
*/
private String errorMsg;
public LogRecord(Instant timestamp, LogRequest request, LogResponse response, Duration timeTaken) {
this.timestamp = timestamp;
this.request = request;
@@ -164,4 +171,11 @@ public class LogRecord {
public Instant getTimestamp() {
return timestamp;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}

View File

@@ -21,6 +21,7 @@ import org.springframework.http.HttpHeaders;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.IpUtils;
import top.continew.starter.log.core.enums.Include;
import top.continew.starter.log.core.http.recordable.RecordableHttpRequest;
import top.continew.starter.web.util.ServletUtils;
import java.net.URI;

View File

@@ -17,6 +17,7 @@
package top.continew.starter.log.core.model;
import top.continew.starter.log.core.enums.Include;
import top.continew.starter.log.core.http.recordable.RecordableHttpResponse;
import java.util.Map;
import java.util.Set;