diff --git a/pom.xml b/pom.xml
index 89155a8..47b526c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,12 @@
spring-boot-starter-web
3.4.7-SNAPSHOT
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+ 7.4
+
diff --git a/src/main/java/cn/zhangdeman/context/Request.java b/src/main/java/cn/zhangdeman/context/Request.java
index f55d63a..b794271 100644
--- a/src/main/java/cn/zhangdeman/context/Request.java
+++ b/src/main/java/cn/zhangdeman/context/Request.java
@@ -4,17 +4,20 @@ import cn.zhangdeman.consts.RecordField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
import java.util.Map;
// 请求相关数据
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true) // 忽略未知属性字段
-public class Request {
+public class Request implements Serializable {
@JsonProperty(RecordField.REQUEST_UA)
private String userAgent; // 客户端ua
@JsonProperty(RecordField.REQUEST_CLIENT_IP)
@@ -37,4 +40,14 @@ public class Request {
private byte[] requestBodyRaw; // 原始请求body
@JsonProperty(RecordField.REQUEST_URI)
private String requestUri; // 请求接口
+ // 序列化
+ @Override
+ public String toString() {
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ return mapper.writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/src/main/java/cn/zhangdeman/logger/BaseProvider.java b/src/main/java/cn/zhangdeman/logger/BaseProvider.java
new file mode 100644
index 0000000..6ad2a58
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/logger/BaseProvider.java
@@ -0,0 +1,111 @@
+package cn.zhangdeman.logger;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.status.Status;
+import cn.zhangdeman.consts.RecordField;
+import cn.zhangdeman.context.RuntimeContext;
+import com.fasterxml.jackson.core.JsonGenerator;
+import jakarta.servlet.http.HttpServletRequest;
+import net.logstash.logback.composite.JsonProvider;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.io.IOException;
+
+// 基础provider, 简化真正provider逻辑 + 实现公共方法
+public abstract class BaseProvider implements JsonProvider {
+ // 获取请求上下文
+ protected RuntimeContext getCurrentRequest() {
+ try {
+ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ if (null == requestAttributes) {
+ return null;
+ }
+ HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
+ return (RuntimeContext) httpServletRequest.getAttribute(RecordField.RUNTIME_THREAD_CONTEXT);
+ } catch (Exception e) {
+ return null; // 非 HTTP 请求环境
+ }
+ }
+
+ // 写入公共字段
+ protected void writeCommonField(RuntimeContext runtimeContext, JsonGenerator gen, String logType) throws IOException {
+ gen.writeStringField(RecordField.REQUEST_ID, runtimeContext.getRequestId());
+ gen.writeStringField(RecordField.TRACE_ID, runtimeContext.getTraceId());
+ gen.writeStringField(RecordField.SERVER_IP, runtimeContext.getServerIp());
+ gen.writeStringField(RecordField.SERVER_HOSTNAME, runtimeContext.getServerHostname());
+ gen.writeStringField(RecordField.LOGGER_TYPE, logType);
+ gen.writeStringField(RecordField.REQUEST_METHOD, runtimeContext.getRequestInfo().getRequestMethod());
+ gen.writeStringField(RecordField.REQUEST_URI, runtimeContext.getRequestInfo().getRequestUri());
+ gen.writeStringField(RecordField.REQUEST_CLIENT_IP, runtimeContext.getRequestInfo().getClientIp());
+ gen.writeNumberField(RecordField.COST, System.currentTimeMillis() - runtimeContext.getStartTimeStamp()); // 代表的是从请求开始, 到打印日志这一刻, 花费了多长时间
+ gen.writeStringField(RecordField.REQUEST_CONTENT_TYPE, runtimeContext.getRequestInfo().getRequestContentType() == null ? "" : runtimeContext.getRequestInfo().getRequestContentType());
+ }
+
+ @Override
+ public void prepareForDeferredProcessing(ILoggingEvent iLoggingEvent) {
+
+ }
+
+ @Override
+ public void start() {
+
+ }
+
+ @Override
+ public void stop() {
+
+ }
+
+ @Override
+ public boolean isStarted() {
+ return false;
+ }
+
+ @Override
+ public void setContext(Context context) {
+
+ }
+
+ @Override
+ public Context getContext() {
+ return null;
+ }
+
+ @Override
+ public void addStatus(Status status) {
+
+ }
+
+ @Override
+ public void addInfo(String s) {
+
+ }
+
+ @Override
+ public void addInfo(String s, Throwable throwable) {
+
+ }
+
+ @Override
+ public void addWarn(String s) {
+
+ }
+
+ @Override
+ public void addWarn(String s, Throwable throwable) {
+
+ }
+
+ @Override
+ public void addError(String s) {
+
+ }
+
+ @Override
+ public void addError(String s, Throwable throwable) {
+
+ }
+}
diff --git a/src/main/java/cn/zhangdeman/logger/LogTypeEnum.java b/src/main/java/cn/zhangdeman/logger/LogTypeEnum.java
new file mode 100644
index 0000000..11195fb
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/logger/LogTypeEnum.java
@@ -0,0 +1,16 @@
+package cn.zhangdeman.logger;
+
+import lombok.Getter;
+
+@Getter
+public enum LogTypeEnum {
+ REQUEST_INPUT("input", "请求输入信息"),
+ REQUEST_OUTPUT("output", "请求响应信息"),
+ ;
+ private final String logType;
+ private final String description;
+ LogTypeEnum(String logType, String description) {
+ this.logType = logType;
+ this.description = description;
+ }
+}
diff --git a/src/main/java/cn/zhangdeman/logger/RequestInfoLogProvider.java b/src/main/java/cn/zhangdeman/logger/RequestInfoLogProvider.java
new file mode 100644
index 0000000..4fe4d9b
--- /dev/null
+++ b/src/main/java/cn/zhangdeman/logger/RequestInfoLogProvider.java
@@ -0,0 +1,23 @@
+package cn.zhangdeman.logger;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import cn.zhangdeman.consts.RecordField;
+import cn.zhangdeman.context.RuntimeContext;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import java.io.IOException;
+
+// 记录请求信息
+public class RequestInfoLogProvider extends BaseProvider {
+ @Override
+ public void writeTo(JsonGenerator gen, ILoggingEvent event) throws IOException {
+ RuntimeContext runtimeContext = getCurrentRequest();
+ if (null == runtimeContext) {
+ // 非 http 请求不应该用到这个provider
+ return;
+ }
+ // 请求输入日志
+ writeCommonField(runtimeContext, gen, LogTypeEnum.REQUEST_INPUT.getLogType());
+ gen.writeStringField(RecordField.REQUEST_INFO, runtimeContext.getRequestInfo().toString());
+ }
+}