From 0156d53579b3b3665f578b0a082ca7924a7b34b6 Mon Sep 17 00:00:00 2001 From: Gavin Bunney Date: Tue, 30 Jan 2024 08:29:16 -0800 Subject: [PATCH] Only capture first line of stacktrace for unexpected RequestAttempt exceptions --- .../com/netflix/zuul/niws/RequestAttempt.java | 9 ++++-- .../netflix/zuul/niws/RequestAttemptTest.java | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/zuul-core/src/main/java/com/netflix/zuul/niws/RequestAttempt.java b/zuul-core/src/main/java/com/netflix/zuul/niws/RequestAttempt.java index 6b1b2e9850..82e45dea65 100644 --- a/zuul-core/src/main/java/com/netflix/zuul/niws/RequestAttempt.java +++ b/zuul-core/src/main/java/com/netflix/zuul/niws/RequestAttempt.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.base.Throwables; import com.netflix.appinfo.AmazonInfo; import com.netflix.appinfo.InstanceInfo; import com.netflix.client.config.IClientConfig; @@ -340,7 +339,13 @@ public void setException(Throwable t) { } else { error = t.getMessage(); exceptionType = t.getClass().getSimpleName(); - cause = Throwables.getStackTraceAsString(t); + + // for unexpected exceptions, just capture the first line of the stacktrace + // otherwise we risk large stacktraces in memory and metrics systems + StackTraceElement[] stackTraceElements = t.getStackTrace(); + if (stackTraceElements.length > 0) { + cause = stackTraceElements[0].toString(); + } } } } diff --git a/zuul-core/src/test/java/com/netflix/zuul/niws/RequestAttemptTest.java b/zuul-core/src/test/java/com/netflix/zuul/niws/RequestAttemptTest.java index df9e305f04..0306206fc4 100644 --- a/zuul-core/src/test/java/com/netflix/zuul/niws/RequestAttemptTest.java +++ b/zuul-core/src/test/java/com/netflix/zuul/niws/RequestAttemptTest.java @@ -18,6 +18,9 @@ import com.netflix.zuul.exception.OutboundErrorType; import com.netflix.zuul.netty.connectionpool.OriginConnectException; +import io.netty.handler.codec.http2.DefaultHttp2Connection; +import io.netty.handler.codec.http2.Http2Error; +import io.netty.handler.codec.http2.Http2Exception; import org.junit.jupiter.api.Test; import javax.net.ssl.SSLHandshakeException; @@ -26,6 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; public class RequestAttemptTest { @@ -77,4 +81,30 @@ void originConnectExceptionWithCauseNotUnwrapped() { assertEquals("ORIGIN_CONNECT_ERROR", attempt.getError()); assertEquals("java.lang.RuntimeException: socket failure", attempt.getCause()); } + + @Test + void h2ExceptionCauseHandled() { + // mock out a real-ish h2 stream exception + Exception h2Exception = spy(Http2Exception.streamError( + 100, + Http2Error.REFUSED_STREAM, + "Cannot create stream 100 greater than Last-Stream-ID 99 from GOAWAY.", + new Object[] {100, 99})); + + // mock a stacktrace to ensure we don't actually capture it completely + when(h2Exception.getStackTrace()).thenReturn(new StackTraceElement[] { + new StackTraceElement( + DefaultHttp2Connection.class.getCanonicalName(), "createStream", "DefaultHttp2Connection.java", 772) + }); + + RequestAttempt attempt = new RequestAttempt(1, null, null, "target", "chosen", 200, null, null, 0, 0, 0); + attempt.setException(h2Exception); + + assertEquals("Cannot create stream 100 greater than Last-Stream-ID 99 from GOAWAY.", attempt.getError()); + assertEquals("StreamException", attempt.getExceptionType()); + + assertEquals( + "io.netty.handler.codec.http2.DefaultHttp2Connection.createStream(DefaultHttp2Connection.java:772)", + attempt.getCause()); + } } \ No newline at end of file