diff --git a/pom.xml b/pom.xml
index 55899e5..b13688f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,8 +5,8 @@
4.0.0
cn.zhangdeman
- exception
- 1.0-SNAPSHOT
+ springboot-common
+ 0.0.1-SNAPSHOT
21
@@ -19,11 +19,6 @@
spring-context
6.1.14
-
- org.projectlombok
- lombok
- 1.18.30
-
jakarta.servlet
jakarta.servlet-api
@@ -36,6 +31,38 @@
4.13.2
test
+
+ org.projectlombok
+ lombok
+ 1.18.38
+ provided
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.17
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.19.0
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.19.0
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.19.0
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 3.4.7-SNAPSHOT
+
diff --git a/src/main/java/cn/zhangdeman/filter/HttpField.java b/src/main/java/cn/zhangdeman/filter/HttpField.java
new file mode 100644
index 0000000..e25465a
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/HttpField.java
@@ -0,0 +1,9 @@
+package cn.zhangdeman.filter;
+
+// http 一些枚举key
+public class HttpField {
+ private static final String HEADER_TRACE_ID = "X-Front-Trace-Id"; // 透传过来的trace id
+ private static final String HEADER_CLIENT_IP = "X-Client-Ip"; // 请求来源IP
+ private static final String HEADER_USER_AGENT = "User-Agent"; // 请求来源 ua
+ private static final String HEADER_AUTHORIZATION = "Authorization"; // 请求来源 authorization
+}
diff --git a/src/main/java/cn/zhangdeman/filter/LogField.java b/src/main/java/cn/zhangdeman/filter/LogField.java
new file mode 100644
index 0000000..cec5bdc
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/LogField.java
@@ -0,0 +1,22 @@
+package cn.zhangdeman.filter;
+
+// 日志相关数据的枚举值
+public class LogField {
+ // 定义日志类型
+ public static final String LOG_TYPE_ACCESS = "access"; // 服务请求记录
+ public static final String LOG_TYPE_INPUT = "input"; // 请求输入信息
+ public static final String LOG_TYPE_OUTPUT = "output"; // 请求输出信息
+ public static final String LOG_TYPE_API = "api"; // 接口请求记录
+ public static final String LOG_TYPE_MONITOR = "monitor"; // 系统监控
+ public static final String LOG_TYPE_DATABASE = "database"; // 数据库
+ public static final String LOG_TYPE_REDIS = "redis"; // redis日志
+ public static final String LOG_TYPE_KAFKA = "kafka"; // kafka日志
+ // 定义各种日志文件名称
+ public static final String LOG_ACCESS_NAME = "access.log"; // 访问日志
+ public static final String LOG_REQUEST_NAME = "request.log"; // 请求日志
+ public static final String LOG_API_NAME = "api.log"; // 三方调用日志
+ public static final String LOG_MONITOR_NAME = "monitor.log"; // 监控日志
+ public static final String LOG_DATABASE_NAME = "database.log"; // 数据库日志
+ public static final String LOG_CACHE_NAME = "cache.log"; // 缓存日志
+ public static final String LOG_MESSAGE_NAME = "message.log"; // 消息队列日志
+}
diff --git a/src/main/java/cn/zhangdeman/filter/Main.java b/src/main/java/cn/zhangdeman/filter/Main.java
new file mode 100644
index 0000000..5c853b6
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/Main.java
@@ -0,0 +1,7 @@
+package cn.zhangdeman.filter;
+
+
+public class Main {
+ public static void main(String[] args) {
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/zhangdeman/filter/Ordered.java b/src/main/java/cn/zhangdeman/filter/Ordered.java
new file mode 100644
index 0000000..da9676f
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/Ordered.java
@@ -0,0 +1,5 @@
+package cn.zhangdeman.filter;
+
+public class Ordered {
+ public static final int REQUEST_ID = -10000; // 请求ID生成过滤器
+}
diff --git a/src/main/java/cn/zhangdeman/filter/RecordField.java b/src/main/java/cn/zhangdeman/filter/RecordField.java
new file mode 100644
index 0000000..21f765c
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/RecordField.java
@@ -0,0 +1,37 @@
+package cn.zhangdeman.filter;
+
+// 记录的各种字段枚举值
+public class RecordField {
+ public static final String TRACE_ID = "trace_id"; // trace_id
+ public static final String REQUEST_ID = "request_id"; // request_id
+ public static final String START_TIMESTAMP = "start_timestamp"; // 开始请求时间
+ public static final String FINISH_TIMESTAMP = "finish_timestamp"; // 完成请求时间
+ public static final String SERVER_IP = "server_ip"; // 服务器IP
+ public static final String SERVER_HOSTNAME = "server_hostname"; // 服务器名称
+ public static final String COST = "cost"; // 请求耗时
+ public static final String CONTEXT_DATA = "context_data"; // 本条日志的上下文信息
+ // 请求信息
+ public static final String REQUEST_INFO = "request_info"; // 请求信息
+ public static final String REQUEST_UA = "user_agent"; // 请求ua
+ public static final String REQUEST_CLIENT_IP = "client_ip"; // 请求客户端
+ public static final String REQUEST_METHOD = "method"; // 请求方法
+ public static final String REQUEST_CONTENT_TYPE = "content_type"; // 请求类型
+ public static final String REQUEST_QUERY = "_query"; // query参数
+ public static final String REQUEST_BODY = "body"; // body参数
+ public static final String REQUEST_HEADER = "header"; // header参数
+ public static final String REQUEST_COOKIE = "cookie"; // cookie参数
+ public static final String REQUEST_PATH_PARAM = "path_param"; // path参数
+ public static final String REQUEST_SIZE = "size"; // 参数大小
+ public static final String REQUEST_URI = "uri"; // 请求接口
+
+ public static final String RUNTIME_THREAD_CONTEXT = "runtime_thread_context"; // 运行时线程上下文
+
+ public static final String RESPONSE_INFO = "response_data"; // 接口响应数据
+ public static final String RESPONSE_CODE = "code"; // 接口响应数据状态码
+ public static final String RESPONSE_MESSAGE = "message"; // 接口响应数据状态码描述
+ public static final String RESPONSE_DATA = "data"; // 接口响应数据
+ public static final String RESPONSE_ERROR_DETAIL = "err_detail"; // 异常详细信息
+ public static final String RESPONSE_HTTP_CODE = "http_code"; // 苍蝇状态码,默认200
+ public static final String RESPONSE_HEADER = "header"; // 响应header
+ public static final String RESPONSE_COOKIE = "cookie"; // 响应header
+}
diff --git a/src/main/java/cn/zhangdeman/filter/Request.java b/src/main/java/cn/zhangdeman/filter/Request.java
new file mode 100644
index 0000000..3c25e1c
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/Request.java
@@ -0,0 +1,41 @@
+package cn.zhangdeman.filter;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+// 请求相关数据
+@Getter
+@Setter
+@JsonIgnoreProperties(ignoreUnknown = true) // 忽略未知属性字段
+public class Request {
+ @JsonIgnore
+ HttpServletRequest httpServletRequest;// 原始请求
+ @JsonProperty(RecordField.REQUEST_UA)
+ private String userAgent; // 客户端ua
+ @JsonProperty(RecordField.REQUEST_CLIENT_IP)
+ private String clientIp; // 客户端IP
+ @JsonProperty(RecordField.REQUEST_METHOD)
+ private String requestMethod; // 请求方法
+ @JsonProperty(RecordField.REQUEST_CONTENT_TYPE)
+ private String requestContentType; // 请求类型
+ @JsonProperty(RecordField.REQUEST_QUERY)
+ private Map requestQuery; // 请求query
+ @JsonProperty(RecordField.REQUEST_HEADER)
+ private Map requestHeader; // 请求header
+ @JsonProperty(RecordField.REQUEST_COOKIE)
+ private Map requestCookie; // 请求cookie
+ @JsonProperty(RecordField.REQUEST_PATH_PARAM)
+ private Map requestPathParam; // path 参数
+ @JsonProperty(RecordField.REQUEST_BODY)
+ private String requestBody; // 请求body
+ @JsonIgnore
+ private byte[] requestBodyRaw; // 原始请求body
+ @JsonProperty(RecordField.REQUEST_URI)
+ private String requestUri; // 请求接口
+}
diff --git a/src/main/java/cn/zhangdeman/filter/Response.java b/src/main/java/cn/zhangdeman/filter/Response.java
new file mode 100644
index 0000000..cb0e54f
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/Response.java
@@ -0,0 +1,48 @@
+package cn.zhangdeman.filter;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+// 响应数据
+@Getter
+@Setter
+@JsonIgnoreProperties(ignoreUnknown = true) // 忽略未知属性字段
+public class Response {
+ @JsonProperty(RecordField.RESPONSE_CODE)
+ private String code; // 响应业务状态码
+ @JsonProperty(RecordField.RESPONSE_MESSAGE)
+ private String message; // 响应消息
+ @JsonProperty(RecordField.RESPONSE_DATA)
+ private Object data; // 响应数据
+ @JsonProperty(RecordField.RESPONSE_ERROR_DETAIL)
+ private Object errorDetail; // 异常详情
+ @JsonProperty(RecordField.RESPONSE_HTTP_CODE)
+ private Integer httpCode = 200; // 响应http状态码
+ @JsonProperty(RecordField.RESPONSE_HEADER)
+ private Map header; // 响应头
+ @JsonProperty(RecordField.RESPONSE_COOKIE)
+ private Map cookie; // 响应cookie
+ @JsonIgnore
+ private HttpServletResponse httpServletResponse; // 响应实例
+
+ public void addHeader(String key, String value) {
+ if (null == this.header) {
+ this.header = new HashMap<>();
+ }
+ this.header.put(key, value);
+ }
+
+ public void addCookie(String key, String value) {
+ if (null == this.cookie) {
+ this.cookie = new HashMap<>();
+ }
+ this.cookie.put(key, value);
+ }
+}
diff --git a/src/main/java/cn/zhangdeman/filter/RuntimeContext.java b/src/main/java/cn/zhangdeman/filter/RuntimeContext.java
new file mode 100644
index 0000000..a654311
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/RuntimeContext.java
@@ -0,0 +1,62 @@
+package cn.zhangdeman.filter;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Map;
+
+// 初始化的请求信息
+@Getter
+@Setter
+@JsonIgnoreProperties(ignoreUnknown = true) // 忽略未知属性字段
+public class RuntimeContext implements Serializable {
+ @JsonProperty(RecordField.REQUEST_ID)
+ private String requestId; // 本次请求request id
+ @JsonProperty(RecordField.COST)
+ private Long cost; // 请求耗时
+ @JsonProperty(RecordField.SERVER_IP)
+ private String serverIp; // 服务器Ip
+ @JsonProperty(RecordField.SERVER_HOSTNAME)
+ private String serverHostname; // 服务器hostname
+ @JsonProperty(RecordField.FINISH_TIMESTAMP)
+ private Long finishTimeStamp; // 完成请求时间
+ @JsonProperty(RecordField.TRACE_ID)
+ private String traceId; // 全局trace id
+ @JsonProperty(RecordField.START_TIMESTAMP)
+ private Long startTimeStamp; // 开始请求时间
+ @JsonProperty(RecordField.CONTEXT_DATA)
+ private Map logData; // 本条日志的上下文信息
+ @JsonProperty(RecordField.REQUEST_INFO)
+ private Request requestInfo;// 请求信息
+ @JsonProperty(RecordField.RESPONSE_INFO)
+ private Response responseInfo; // 响应信息
+
+ // 序列化
+ @Override
+ public String toString() {
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ return mapper.writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // 序列化
+ public String serializable() {
+ return toString();
+ }
+
+ // 反序列化
+ public RuntimeContext unserializable(String json) throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ return mapper.readValue(json, new TypeReference() {
+ });
+ }
+}
diff --git a/src/main/java/cn/zhangdeman/filter/filter/RequestInitFilter.java b/src/main/java/cn/zhangdeman/filter/filter/RequestInitFilter.java
new file mode 100644
index 0000000..d182740
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/filter/filter/RequestInitFilter.java
@@ -0,0 +1,129 @@
+package cn.zhangdeman.filter;
+
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.annotation.WebFilter;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpFilter;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.core.annotation.Order;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+
+// 初始化请求
+@WebFilter("/*") // 所有接口请求均会触发次过滤器
+@Order(Ordered.REQUEST_ID) // 足够小, 保证最先执行
+public class RequestInitFilter extends HttpFilter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public void doFilter(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws IOException, ServletException {
+ RuntimeContext runtimeContext = new RuntimeContext(); // 运行时上下文数据
+ setBaseInfo(httpServletRequest,httpServletResponse, runtimeContext); // 设置基础信息
+ setRequestQuery(runtimeContext.getRequestInfo()); // 设置请求query信息
+ setRequestBody(runtimeContext.getRequestInfo()); // 设置请求Body
+ setRequestHeaderAndCookie(runtimeContext.getRequestInfo()); // 填充请求信息: header + cookie
+ setRequestId(runtimeContext); // 设置请求ID, 每次请求需要重新生成
+ httpServletRequest.setAttribute(RecordField.RUNTIME_THREAD_CONTEXT, runtimeContext); // 记录到请求上下文中
+ // 继续向后执行
+ filterChain.doFilter(httpServletRequest, httpServletResponse);
+ System.out.println("init request do filter 请求执行完成");
+ }
+
+ // 设置基础信息
+ private void setBaseInfo(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, RuntimeContext runtimeContext) {
+ // request 信息
+ Request request = new Request();
+ request.setHttpServletRequest(httpServletRequest);
+ request.setClientIp(httpServletRequest.getRemoteAddr()); // client ip
+ request.setRequestMethod(httpServletRequest.getMethod()); // 请求类型
+ request.setRequestContentType(httpServletRequest.getContentType()); // 请求类型
+ request.setRequestUri(httpServletRequest.getRequestURI()); // 请求uri
+ runtimeContext.setRequestInfo(request); // 请求信息
+ // server 信息
+ runtimeContext.setServerIp(httpServletRequest.getLocalAddr()); // 服务器IP
+ runtimeContext.setServerHostname(httpServletRequest.getLocalName()); // 服务器名称
+ runtimeContext.setStartTimeStamp(System.currentTimeMillis()); // 开始请求时间
+ // 响应信息
+ Response response = new Response();
+ response.setHttpServletResponse(httpServletResponse);
+ response.setHeader(new HashMap<>());
+ response.setCookie(new HashMap<>());
+ runtimeContext.setResponseInfo(response); // 响应信息
+ }
+
+ // 设置请求Query
+ private void setRequestQuery(Request request) {
+ Map query = new HashMap<>();
+ HttpServletRequest httpServletRequest = request.getHttpServletRequest(); // 原始请求实例
+ Map queryParamList = httpServletRequest.getParameterMap(); // query参数列表
+ for (Map.Entry entity : queryParamList.entrySet()) {
+ if (entity.getValue().length == 0) {
+ // 参数值为空
+ query.put(entity.getKey(), "");
+ } else {
+ // 多个参数值, 取第一个
+ query.put(entity.getKey(), entity.getValue()[0]);
+ }
+ }
+ request.setRequestQuery(query);
+ }
+
+ // 设置请求Body
+ private void setRequestBody(Request request) throws IOException {
+ HttpServletRequest httpServletRequest = request.getHttpServletRequest(); // 原始请求实例
+ ServletInputStream servletInputStream = httpServletRequest.getInputStream();
+ byte[] requestBody = servletInputStream.readAllBytes();
+ request.setRequestBodyRaw(requestBody); // 原始请求Body
+ request.setRequestBody(Arrays.toString(requestBody)); // 请求body
+ }
+
+
+ // 设置请求信息: header + cookie
+ private void setRequestHeaderAndCookie(Request request) {
+ HttpServletRequest httpServletRequest = request.getHttpServletRequest(); // 原始请求实例
+
+ request.setRequestHeader(new HashMap<>()); // 请求header
+ request.setRequestCookie(new HashMap<>()); // 请求cookie
+ // 全部请求header
+ Enumeration enumeration = httpServletRequest.getHeaderNames();
+ while (enumeration.hasMoreElements()) {
+ String name = enumeration.nextElement();
+ if (name.equalsIgnoreCase("cookie")) {
+ // cookie单独摘出来处理
+ continue;
+ }
+ String value = httpServletRequest.getHeader(name);
+ request.getRequestHeader().put(name, value);
+ }
+ // 全部请求cookie
+ Cookie[] cookieList = httpServletRequest.getCookies();
+ for (Cookie cookie : cookieList) {
+ request.getRequestCookie().put(cookie.getName(), cookie.getValue());
+ }
+ }
+
+ // 设置请求ID
+ private void setRequestId(RuntimeContext runtimeContext) {
+ }
+
+ // 响应信息
+ private void setResponse(Response response) {
+ }
+}