This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new b9ef7d526a fix: defer access-log header serialization off the request
thread (#6581) (#7527)
b9ef7d526a is described below
commit b9ef7d526ab0cc79a6874cb665c8add1f4df2989
Author: wy471x <[email protected]>
AuthorDate: Sun Oct 11 22:34:50 2026 +0800
fix: defer access-log header serialization off the request thread (#6581)
(#7527)
---
.../logging/common/AbstractLoggingPlugin.java | 2 +-
.../common/body/LoggingServerHttpResponse.java | 4 +-
.../logging/common/entity/ShenyuRequestLog.java | 47 ++++++++++++++++++++
.../common/entity/ShenyuRequestLogTest.java | 50 ++++++++++++++++++++++
4 files changed, 100 insertions(+), 3 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/AbstractLoggingPlugin.java
b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/AbstractLoggingPlugin.java
index 75411631fa..3c9ad03be7 100644
---
a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/AbstractLoggingPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/AbstractLoggingPlugin.java
@@ -106,7 +106,7 @@ public abstract class AbstractLoggingPlugin<L extends
ShenyuRequestLog> extends
requestInfo.setRequestUri(request.getURI().toString());
requestInfo.setMethod(request.getMethod().name());
requestInfo.setRequestMethod(request.getMethod().name());
-
requestInfo.setRequestHeader(LogCollectUtils.getHeaders(request.getHeaders()));
+ requestInfo.setRequestHeader(() ->
LogCollectUtils.getHeaders(request.getHeaders()));
requestInfo.setQueryParams(request.getURI().getQuery());
requestInfo.setClientIp(HostAddressUtils.acquireIp(exchange));
requestInfo.setUserAgent(request.getHeaders().getFirst(GenericLoggingConstant.USER_AGENT));
diff --git
a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/body/LoggingServerHttpResponse.java
b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/body/LoggingServerHttpResponse.java
index 28faf12c91..506cb6551d 100644
---
a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/body/LoggingServerHttpResponse.java
+++
b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/body/LoggingServerHttpResponse.java
@@ -147,7 +147,7 @@ public class LoggingServerHttpResponse<L extends
ShenyuRequestLog> extends Serve
if (Objects.nonNull(getStatusCode())) {
logInfo.setStatus(getStatusCode().value());
}
- logInfo.setResponseHeader(LogCollectUtils.getHeaders(getHeaders()));
+ logInfo.setResponseHeader(() ->
LogCollectUtils.getHeaders(getHeaders()));
logInfo.setTraceId(getTraceId());
final MediaType mediaType =
exchange.getResponse().getHeaders().getContentType();
if (MediaTypeUtils.isByteType(mediaType)) {
@@ -274,7 +274,7 @@ public class LoggingServerHttpResponse<L extends
ShenyuRequestLog> extends Serve
logInfo.setModule(shenyuContext.getModule());
long costTime =
DateUtils.acquireMillisBetween(shenyuContext.getStartDateTime(),
LocalDateTime.now());
logInfo.setUpstreamResponseTime(costTime);
-
logInfo.setResponseHeader(LogCollectUtils.getHeaders(exchange.getResponse().getHeaders()));
+ logInfo.setResponseHeader(() ->
LogCollectUtils.getHeaders(exchange.getResponse().getHeaders()));
logInfo.setRpcType(shenyuContext.getRpcType());
logInfo.setMethod(shenyuContext.getMethod());
if (StringUtils.isNotBlank(shenyuContext.getRpcType())) {
diff --git
a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/entity/ShenyuRequestLog.java
b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/entity/ShenyuRequestLog.java
index 2c5ccaeafb..6d0f9c7f57 100644
---
a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/entity/ShenyuRequestLog.java
+++
b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/main/java/org/apache/shenyu/plugin/logging/common/entity/ShenyuRequestLog.java
@@ -17,6 +17,9 @@
package org.apache.shenyu.plugin.logging.common.entity;
+import java.util.Objects;
+import java.util.function.Supplier;
+
/**
* shenyu gateway access log.
*/
@@ -34,6 +37,18 @@ public class ShenyuRequestLog {
private String responseHeader;
+ /**
+ * Deferred source of the request header json, resolved on first read.
+ * Transient so the log clients never serialize it.
+ */
+ private transient Supplier<String> requestHeaderSupplier;
+
+ /**
+ * Deferred source of the response header json, resolved on first read.
+ * Transient so the log clients never serialize it.
+ */
+ private transient Supplier<String> responseHeaderSupplier;
+
private String queryParams;
private String requestBody;
@@ -224,6 +239,10 @@ public class ShenyuRequestLog {
* @return requestHeader
*/
public String getRequestHeader() {
+ if (Objects.nonNull(requestHeaderSupplier)) {
+ requestHeader = requestHeaderSupplier.get();
+ requestHeaderSupplier = null;
+ }
return requestHeader;
}
@@ -234,6 +253,18 @@ public class ShenyuRequestLog {
*/
public void setRequestHeader(final String requestHeader) {
this.requestHeader = requestHeader;
+ this.requestHeaderSupplier = null;
+ }
+
+ /**
+ * set requestHeader lazily, so the header json is built by whoever reads
it instead of
+ * the request thread that produced the log entry.
+ *
+ * @param requestHeaderSupplier supplier of the serialized request header
json
+ */
+ public void setRequestHeader(final Supplier<String> requestHeaderSupplier)
{
+ this.requestHeaderSupplier = requestHeaderSupplier;
+ this.requestHeader = null;
}
/**
@@ -242,6 +273,10 @@ public class ShenyuRequestLog {
* @return responseHeader
*/
public String getResponseHeader() {
+ if (Objects.nonNull(responseHeaderSupplier)) {
+ responseHeader = responseHeaderSupplier.get();
+ responseHeaderSupplier = null;
+ }
return responseHeader;
}
@@ -252,6 +287,18 @@ public class ShenyuRequestLog {
*/
public void setResponseHeader(final String responseHeader) {
this.responseHeader = responseHeader;
+ this.responseHeaderSupplier = null;
+ }
+
+ /**
+ * set responseHeader lazily, so the header json is built by whoever reads
it instead of
+ * the response thread that produced the log entry.
+ *
+ * @param responseHeaderSupplier supplier of the serialized response
header json
+ */
+ public void setResponseHeader(final Supplier<String>
responseHeaderSupplier) {
+ this.responseHeaderSupplier = responseHeaderSupplier;
+ this.responseHeader = null;
}
/**
diff --git
a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/test/java/org/apache/shenyu/plugin/logging/common/entity/ShenyuRequestLogTest.java
b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/test/java/org/apache/shenyu/plugin/logging/common/entity/ShenyuRequestLogTest.java
index 21bcc69326..eda1a32121 100644
---
a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/test/java/org/apache/shenyu/plugin/logging/common/entity/ShenyuRequestLogTest.java
+++
b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-common/src/test/java/org/apache/shenyu/plugin/logging/common/entity/ShenyuRequestLogTest.java
@@ -17,10 +17,12 @@
package org.apache.shenyu.plugin.logging.common.entity;
+import org.apache.shenyu.common.utils.GsonUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* The Test Case For ShenyuRequestLog.
@@ -90,6 +92,54 @@ public class ShenyuRequestLogTest {
Assertions.assertEquals(shenyuRequestLog.getRequestHeader(), "{}");
}
+ @Test
+ public void testRequestHeaderIsSerializedLazily() {
+ AtomicInteger invocations = new AtomicInteger();
+ shenyuRequestLog.setRequestHeader(() -> {
+ invocations.incrementAndGet();
+ return "{\"X-source\":\"mock test\"}";
+ });
+ Assertions.assertEquals(0, invocations.get());
+ Assertions.assertEquals("{\"X-source\":\"mock test\"}",
shenyuRequestLog.getRequestHeader());
+ Assertions.assertEquals(1, invocations.get());
+ Assertions.assertEquals("{\"X-source\":\"mock test\"}",
shenyuRequestLog.getRequestHeader());
+ Assertions.assertEquals(1, invocations.get());
+ }
+
+ @Test
+ public void testResponseHeaderIsSerializedLazily() {
+ AtomicInteger invocations = new AtomicInteger();
+ shenyuRequestLog.setResponseHeader(() -> {
+ invocations.incrementAndGet();
+ return "{\"X-response\":\"mock test\"}";
+ });
+ Assertions.assertEquals(0, invocations.get());
+ Assertions.assertEquals("{\"X-response\":\"mock test\"}",
shenyuRequestLog.getResponseHeader());
+ Assertions.assertEquals(1, invocations.get());
+ }
+
+ @Test
+ public void testResolvedHeaderIsNotSerializedTwice() {
+ AtomicInteger invocations = new AtomicInteger();
+ shenyuRequestLog.setRequestHeader(() -> {
+ invocations.incrementAndGet();
+ return "{}";
+ });
+ Assertions.assertEquals("{}", shenyuRequestLog.getRequestHeader());
+ shenyuRequestLog.setRequestHeader("{\"a\":\"b\"}");
+ Assertions.assertEquals("{\"a\":\"b\"}",
shenyuRequestLog.getRequestHeader());
+ Assertions.assertEquals(1, invocations.get());
+ }
+
+ @Test
+ public void testDeferredHeaderSourceIsNotSerialized() {
+ shenyuRequestLog.setRequestHeader(() -> "{\"X-source\":\"mock
test\"}");
+ shenyuRequestLog.setResponseHeader(() -> "{\"X-response\":\"mock
test\"}");
+ String json = GsonUtils.getInstance().toJson(shenyuRequestLog);
+ Assertions.assertFalse(json.contains("Supplier"));
+ Assertions.assertFalse(json.contains("mock test"));
+ }
+
@Test
public void testQueryParams() {
shenyuRequestLog.setQueryParams("{\"id\"=\"1\"}");