diff --git a/instrumentation/akka-http-2.11_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java b/instrumentation/akka-http-2.11_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java index f88a9c51bd..43d13411e9 100644 --- a/instrumentation/akka-http-2.11_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java +++ b/instrumentation/akka-http-2.11_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java @@ -1650,9 +1650,11 @@ private void assertResponseCodeOnTxEvents(Collection transacti Assert.assertNotNull(transactionEvents); Assert.assertEquals(expectedSize, transactionEvents.size()); for (TransactionEvent transactionEvent : transactionEvents) { - String httpResponseCode = String.valueOf(transactionEvent.getAttributes().get("http.statusCode")); + String httpResponseCode = (String) transactionEvent.getAttributes().get("httpResponseCode"); Assert.assertNotNull(httpResponseCode); Assert.assertEquals(expectedResponseCode, httpResponseCode); + int statusCode = (Integer) transactionEvent.getAttributes().get("http.statusCode"); + Assert.assertEquals(Integer.parseInt(expectedResponseCode), statusCode); } } diff --git a/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java b/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java index c4089f8fa8..17aebd7a74 100644 --- a/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java +++ b/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java @@ -1646,9 +1646,11 @@ private void assertResponseCodeOnTxEvents(Collection transacti Assert.assertNotNull(transactionEvents); Assert.assertEquals(expectedSize, transactionEvents.size()); for (TransactionEvent transactionEvent : transactionEvents) { - String httpResponseCode = String.valueOf(transactionEvent.getAttributes().get("http.statusCode")); + String httpResponseCode = String.valueOf(transactionEvent.getAttributes().get("httpResponseCode")); Assert.assertNotNull(httpResponseCode); Assert.assertEquals(expectedResponseCode, httpResponseCode); + int statusCode = (Integer) transactionEvent.getAttributes().get("http.statusCode"); + Assert.assertEquals(Integer.parseInt(expectedResponseCode), statusCode); } } diff --git a/instrumentation/grpc-1.22.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java b/instrumentation/grpc-1.22.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java index 9ff40de150..77b92868f1 100644 --- a/instrumentation/grpc-1.22.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java +++ b/instrumentation/grpc-1.22.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java @@ -15,6 +15,16 @@ public class GrpcConfig { public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("grpc.errors.enabled", true); + public static final boolean HTTP_ATTR_LEGACY; + public static final boolean HTTP_ATTR_STANDARD; + + static { + String attrMode = NewRelic.getAgent().getConfig().getValue("attributes.http_attribute_mode", "both"); + // legacy is only disabled when standard is selected. + HTTP_ATTR_LEGACY = !"standard".equalsIgnoreCase(attrMode); + // standard is only disabled when legacy is selected. + HTTP_ATTR_STANDARD = !"legacy".equalsIgnoreCase(attrMode); + } private GrpcConfig() { } diff --git a/instrumentation/grpc-1.22.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java b/instrumentation/grpc-1.22.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java index 9833b4c404..f6f5808352 100644 --- a/instrumentation/grpc-1.22.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java +++ b/instrumentation/grpc-1.22.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java @@ -39,6 +39,7 @@ public void close(Status status, Metadata trailers) { if (status != null) { int statusCode = status.getCode().value(); + NewRelic.addCustomParameter("response.status", statusCode); NewRelic.addCustomParameter("http.statusCode", statusCode); NewRelic.addCustomParameter("http.statusText", status.getDescription()); @@ -69,8 +70,15 @@ public void cancel(Status status) { if (status != null) { int statusCode = status.getCode().value(); - NewRelic.addCustomParameter("http.statusCode", statusCode); - NewRelic.addCustomParameter("http.statusText", status.getDescription()); + String statusMessage = status.getDescription(); + if (GrpcConfig.HTTP_ATTR_LEGACY) { + NewRelic.addCustomParameter("response.status", statusCode); + NewRelic.addCustomParameter("response.statusMessage", statusMessage); + } + if (GrpcConfig.HTTP_ATTR_STANDARD) { + NewRelic.addCustomParameter("http.statusCode", statusCode); + NewRelic.addCustomParameter("http.statusText", statusMessage); + } if (GrpcConfig.errorsEnabled && status.getCause() != null) { // If an error occurred during the close of this server call we should record it NewRelic.noticeError(status.getCause()); diff --git a/instrumentation/grpc-1.22.0/src/test/java/ValidationHelper.java b/instrumentation/grpc-1.22.0/src/test/java/ValidationHelper.java index a6beb20bce..974bed325e 100644 --- a/instrumentation/grpc-1.22.0/src/test/java/ValidationHelper.java +++ b/instrumentation/grpc-1.22.0/src/test/java/ValidationHelper.java @@ -68,6 +68,7 @@ static void validateGrpcInteraction(TestServer server, String clientTxName, Stri assertTrue(rootSegment.getName().endsWith(fullMethod)); assertEquals(1, rootSegment.getCallCount()); assertEquals(fullMethod, rootSegment.getTracerAttributes().get("request.method")); + assertEquals(0, rootSegment.getTracerAttributes().get("response.status")); assertEquals(0, rootSegment.getTracerAttributes().get("http.statusCode")); assertNull(rootSegment.getTracerAttributes().get("http.statusText")); assertEquals(grpcType, rootSegment.getTracerAttributes().get("grpc.type")); @@ -85,6 +86,7 @@ static void validateGrpcInteraction(TestServer server, String clientTxName, Stri assertEquals(name, serverTxEvent.getAttributes().get("sayHelloAfter")); } + assertEquals(0, serverTxEvent.getAttributes().get("response.status")); assertEquals(0, serverTxEvent.getAttributes().get("http.statusCode")); assertEquals("grpc://localhost:" + server.getPort() + "/" + fullMethod, serverTxEvent.getAttributes().get("request.uri")); } @@ -125,6 +127,7 @@ static void validateExceptionGrpcInteraction(TestServer server, String clientTxN assertTrue(rootSegment.getName().endsWith(fullMethod)); assertEquals(1, rootSegment.getCallCount()); assertEquals(fullMethod, rootSegment.getTracerAttributes().get("request.method")); + assertEquals(status, rootSegment.getTracerAttributes().get("response.status")); assertEquals(status, rootSegment.getTracerAttributes().get("http.statusCode")); assertEquals(grpcType, rootSegment.getTracerAttributes().get("grpc.type")); @@ -133,6 +136,7 @@ static void validateExceptionGrpcInteraction(TestServer server, String clientTxN assertEquals(1, serverTxEvents.size()); TransactionEvent serverTxEvent = serverTxEvents.iterator().next(); assertNotNull(serverTxEvent); + assertEquals(status, serverTxEvent.getAttributes().get("response.status")); assertEquals(status, serverTxEvent.getAttributes().get("http.statusCode")); assertEquals("grpc://localhost:" + server.getPort() + "/" + fullMethod, serverTxEvent.getAttributes().get("request.uri")); } diff --git a/instrumentation/grpc-1.30.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java b/instrumentation/grpc-1.30.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java index 9ff40de150..77b92868f1 100644 --- a/instrumentation/grpc-1.30.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java +++ b/instrumentation/grpc-1.30.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java @@ -15,6 +15,16 @@ public class GrpcConfig { public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("grpc.errors.enabled", true); + public static final boolean HTTP_ATTR_LEGACY; + public static final boolean HTTP_ATTR_STANDARD; + + static { + String attrMode = NewRelic.getAgent().getConfig().getValue("attributes.http_attribute_mode", "both"); + // legacy is only disabled when standard is selected. + HTTP_ATTR_LEGACY = !"standard".equalsIgnoreCase(attrMode); + // standard is only disabled when legacy is selected. + HTTP_ATTR_STANDARD = !"legacy".equalsIgnoreCase(attrMode); + } private GrpcConfig() { } diff --git a/instrumentation/grpc-1.30.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java b/instrumentation/grpc-1.30.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java index 9833b4c404..06d74add7a 100644 --- a/instrumentation/grpc-1.30.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java +++ b/instrumentation/grpc-1.30.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java @@ -39,8 +39,15 @@ public void close(Status status, Metadata trailers) { if (status != null) { int statusCode = status.getCode().value(); - NewRelic.addCustomParameter("http.statusCode", statusCode); - NewRelic.addCustomParameter("http.statusText", status.getDescription()); + String statusMessage = status.getDescription(); + if (GrpcConfig.HTTP_ATTR_LEGACY) { + NewRelic.addCustomParameter("response.status", statusCode); + NewRelic.addCustomParameter("response.statusMessage", statusMessage); + } + if (GrpcConfig.HTTP_ATTR_STANDARD) { + NewRelic.addCustomParameter("http.statusCode", statusCode); + NewRelic.addCustomParameter("http.statusText", statusMessage); + } if (GrpcConfig.errorsEnabled && status.getCause() != null) { // If an error occurred during the close of this server call we should record it @@ -69,6 +76,7 @@ public void cancel(Status status) { if (status != null) { int statusCode = status.getCode().value(); + NewRelic.addCustomParameter("response.status", statusCode); NewRelic.addCustomParameter("http.statusCode", statusCode); NewRelic.addCustomParameter("http.statusText", status.getDescription()); if (GrpcConfig.errorsEnabled && status.getCause() != null) { diff --git a/instrumentation/grpc-1.30.0/src/test/java/ValidationHelper.java b/instrumentation/grpc-1.30.0/src/test/java/ValidationHelper.java index a6beb20bce..974bed325e 100644 --- a/instrumentation/grpc-1.30.0/src/test/java/ValidationHelper.java +++ b/instrumentation/grpc-1.30.0/src/test/java/ValidationHelper.java @@ -68,6 +68,7 @@ static void validateGrpcInteraction(TestServer server, String clientTxName, Stri assertTrue(rootSegment.getName().endsWith(fullMethod)); assertEquals(1, rootSegment.getCallCount()); assertEquals(fullMethod, rootSegment.getTracerAttributes().get("request.method")); + assertEquals(0, rootSegment.getTracerAttributes().get("response.status")); assertEquals(0, rootSegment.getTracerAttributes().get("http.statusCode")); assertNull(rootSegment.getTracerAttributes().get("http.statusText")); assertEquals(grpcType, rootSegment.getTracerAttributes().get("grpc.type")); @@ -85,6 +86,7 @@ static void validateGrpcInteraction(TestServer server, String clientTxName, Stri assertEquals(name, serverTxEvent.getAttributes().get("sayHelloAfter")); } + assertEquals(0, serverTxEvent.getAttributes().get("response.status")); assertEquals(0, serverTxEvent.getAttributes().get("http.statusCode")); assertEquals("grpc://localhost:" + server.getPort() + "/" + fullMethod, serverTxEvent.getAttributes().get("request.uri")); } @@ -125,6 +127,7 @@ static void validateExceptionGrpcInteraction(TestServer server, String clientTxN assertTrue(rootSegment.getName().endsWith(fullMethod)); assertEquals(1, rootSegment.getCallCount()); assertEquals(fullMethod, rootSegment.getTracerAttributes().get("request.method")); + assertEquals(status, rootSegment.getTracerAttributes().get("response.status")); assertEquals(status, rootSegment.getTracerAttributes().get("http.statusCode")); assertEquals(grpcType, rootSegment.getTracerAttributes().get("grpc.type")); @@ -133,6 +136,7 @@ static void validateExceptionGrpcInteraction(TestServer server, String clientTxN assertEquals(1, serverTxEvents.size()); TransactionEvent serverTxEvent = serverTxEvents.iterator().next(); assertNotNull(serverTxEvent); + assertEquals(status, serverTxEvent.getAttributes().get("response.status")); assertEquals(status, serverTxEvent.getAttributes().get("http.statusCode")); assertEquals("grpc://localhost:" + server.getPort() + "/" + fullMethod, serverTxEvent.getAttributes().get("request.uri")); } diff --git a/instrumentation/grpc-1.4.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java b/instrumentation/grpc-1.4.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java index 9ff40de150..77b92868f1 100644 --- a/instrumentation/grpc-1.4.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java +++ b/instrumentation/grpc-1.4.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java @@ -15,6 +15,16 @@ public class GrpcConfig { public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("grpc.errors.enabled", true); + public static final boolean HTTP_ATTR_LEGACY; + public static final boolean HTTP_ATTR_STANDARD; + + static { + String attrMode = NewRelic.getAgent().getConfig().getValue("attributes.http_attribute_mode", "both"); + // legacy is only disabled when standard is selected. + HTTP_ATTR_LEGACY = !"standard".equalsIgnoreCase(attrMode); + // standard is only disabled when legacy is selected. + HTTP_ATTR_STANDARD = !"legacy".equalsIgnoreCase(attrMode); + } private GrpcConfig() { } diff --git a/instrumentation/grpc-1.4.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java b/instrumentation/grpc-1.4.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java index 1b91851d81..18b777f44d 100644 --- a/instrumentation/grpc-1.4.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java +++ b/instrumentation/grpc-1.4.0/src/main/java/io/grpc/internal/ServerStream_Instrumentation.java @@ -39,8 +39,15 @@ public void close(Status status, Metadata trailers) { if (status != null) { int statusCode = status.getCode().value(); - NewRelic.addCustomParameter("http.statusCode", statusCode); - NewRelic.addCustomParameter("http.statusText", status.getDescription()); + String statusMessage = status.getDescription(); + if (GrpcConfig.HTTP_ATTR_LEGACY) { + NewRelic.addCustomParameter("response.status", statusCode); + NewRelic.addCustomParameter("response.statusMessage", statusMessage); + } + if (GrpcConfig.HTTP_ATTR_STANDARD) { + NewRelic.addCustomParameter("http.statusCode", statusCode); + NewRelic.addCustomParameter("http.statusText", statusMessage); + } if (GrpcConfig.errorsEnabled && status.getCause() != null) { // If an error occurred during the close of this server call we should record it NewRelic.noticeError(status.getCause()); @@ -68,6 +75,7 @@ public void cancel(Status status) { if (status != null) { int statusCode = status.getCode().value(); + NewRelic.addCustomParameter("response.status", statusCode); NewRelic.addCustomParameter("http.statusCode", statusCode); NewRelic.addCustomParameter("http.statusText", status.getDescription()); if (GrpcConfig.errorsEnabled && status.getCause() != null) { diff --git a/instrumentation/grpc-1.4.0/src/test/java/ValidationHelper.java b/instrumentation/grpc-1.4.0/src/test/java/ValidationHelper.java index fecbc25596..112a362bc7 100644 --- a/instrumentation/grpc-1.4.0/src/test/java/ValidationHelper.java +++ b/instrumentation/grpc-1.4.0/src/test/java/ValidationHelper.java @@ -69,6 +69,7 @@ static void validateGrpcInteraction(TestServer server, String clientTxName, Stri assertTrue(rootSegment.getName().endsWith(fullMethod)); assertEquals(1, rootSegment.getCallCount()); assertEquals(fullMethod, rootSegment.getTracerAttributes().get("request.method")); + assertEquals(0, rootSegment.getTracerAttributes().get("response.status")); assertEquals(0, rootSegment.getTracerAttributes().get("http.statusCode")); assertNull(rootSegment.getTracerAttributes().get("http.statusText")); assertEquals(grpcType, rootSegment.getTracerAttributes().get("grpc.type")); @@ -86,6 +87,7 @@ static void validateGrpcInteraction(TestServer server, String clientTxName, Stri assertEquals(name, serverTxEvent.getAttributes().get("sayHelloAfter")); } + assertEquals(0, serverTxEvent.getAttributes().get("response.status")); assertEquals(0, serverTxEvent.getAttributes().get(AttributeNames.HTTP_STATUS_CODE)); assertEquals("grpc://localhost:" + server.getPort() + "/" + fullMethod, serverTxEvent.getAttributes().get("request.uri")); } @@ -126,6 +128,7 @@ static void validateExceptionGrpcInteraction(TestServer server, String clientTxN assertTrue(rootSegment.getName().endsWith(fullMethod)); assertEquals(1, rootSegment.getCallCount()); assertEquals(fullMethod, rootSegment.getTracerAttributes().get("request.method")); + assertEquals(status, rootSegment.getTracerAttributes().get("response.status")); assertEquals(status, rootSegment.getTracerAttributes().get(AttributeNames.HTTP_STATUS_CODE)); assertEquals(grpcType, rootSegment.getTracerAttributes().get("grpc.type")); @@ -134,6 +137,7 @@ static void validateExceptionGrpcInteraction(TestServer server, String clientTxN assertEquals(1, serverTxEvents.size()); TransactionEvent serverTxEvent = serverTxEvents.iterator().next(); assertNotNull(serverTxEvent); + assertEquals(status, serverTxEvent.getAttributes().get("response.status")); assertEquals(status, serverTxEvent.getAttributes().get(AttributeNames.HTTP_STATUS_CODE)); assertEquals("grpc://localhost:" + server.getPort() + "/" + fullMethod, serverTxEvent.getAttributes().get("request.uri")); } diff --git a/instrumentation/grpc-1.40.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java b/instrumentation/grpc-1.40.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java index 56600551fb..7237b5508d 100644 --- a/instrumentation/grpc-1.40.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java +++ b/instrumentation/grpc-1.40.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcConfig.java @@ -15,6 +15,16 @@ public class GrpcConfig { public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("grpc.errors.enabled", true); + public static final boolean HTTP_ATTR_LEGACY; + public static final boolean HTTP_ATTR_STANDARD; + + static { + String attrMode = NewRelic.getAgent().getConfig().getValue("attributes.http_attribute_mode", "both"); + // legacy is only disabled when standard is selected. + HTTP_ATTR_LEGACY = !"standard".equalsIgnoreCase(attrMode); + // standard is only disabled when legacy is selected. + HTTP_ATTR_STANDARD = !"legacy".equalsIgnoreCase(attrMode); + } private GrpcConfig() { } diff --git a/instrumentation/grpc-1.40.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcUtil.java b/instrumentation/grpc-1.40.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcUtil.java index c55a2b94a7..4239bb7365 100644 --- a/instrumentation/grpc-1.40.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcUtil.java +++ b/instrumentation/grpc-1.40.0/src/main/java/com/nr/agent/instrumentation/grpc/GrpcUtil.java @@ -34,14 +34,24 @@ public static void finalizeTransaction(Token token, Status status, Metadata meta } /** - * Set the http.statusCode attribute and error cause (if applicable) on the transaction + * Set the http status code attribute and error cause (if applicable) on the transaction * when the ServerStream is closed or cancelled. * * @param status The {@link Status} of the completed/cancelled operation */ public static void setServerStreamResponseStatus(Status status) { + if (status != null) { - NewRelic.addCustomParameter("http.statusCode", status.getCode().value()); + String statusMessage = status.getDescription(); + int value = status.getCode().value(); // code should not be null + if (GrpcConfig.HTTP_ATTR_LEGACY) { + NewRelic.addCustomParameter("response.status", value); + NewRelic.addCustomParameter("response.statusMessage", statusMessage); + } + if (GrpcConfig.HTTP_ATTR_STANDARD) { + NewRelic.addCustomParameter("http.statusCode", value); + NewRelic.addCustomParameter("http.statusText", statusMessage); + } if (GrpcConfig.errorsEnabled && status.getCause() != null) { // If an error occurred during the close of this server call we should record it NewRelic.noticeError(status.getCause()); diff --git a/instrumentation/grpc-1.40.0/src/test/java/ValidationHelper.java b/instrumentation/grpc-1.40.0/src/test/java/ValidationHelper.java index 024396f030..79aeae6084 100644 --- a/instrumentation/grpc-1.40.0/src/test/java/ValidationHelper.java +++ b/instrumentation/grpc-1.40.0/src/test/java/ValidationHelper.java @@ -60,6 +60,8 @@ static void validateGrpcInteraction(TestServer server, String clientTxName, Stri assertTrue(rootSegment.getName().endsWith(fullMethod)); assertEquals(1, rootSegment.getCallCount()); assertEquals(fullMethod, rootSegment.getTracerAttributes().get("request.method")); + assertEquals(0, rootSegment.getTracerAttributes().get("response.status")); + assertEquals(0, rootSegment.getTracerAttributes().get("http.statusCode")); assertEquals(grpcType, rootSegment.getTracerAttributes().get("grpc.type")); // Custom attributes (to test tracing into customer code) @@ -75,6 +77,7 @@ static void validateGrpcInteraction(TestServer server, String clientTxName, Stri assertEquals(name, serverTxEvent.getAttributes().get("sayHelloAfter")); } + assertEquals(0, serverTxEvent.getAttributes().get("response.status")); assertEquals("grpc://localhost:" + server.getPort() + "/" + fullMethod, serverTxEvent.getAttributes().get("request.uri")); } @@ -114,6 +117,8 @@ static void validateExceptionGrpcInteraction(TestServer server, String clientTxN assertTrue(rootSegment.getName().endsWith(fullMethod)); assertEquals(1, rootSegment.getCallCount()); assertEquals(fullMethod, rootSegment.getTracerAttributes().get("request.method")); + assertEquals(status, rootSegment.getTracerAttributes().get("response.status")); + assertEquals(status, rootSegment.getTracerAttributes().get("http.statusCode")); assertEquals(grpcType, rootSegment.getTracerAttributes().get("grpc.type")); // Custom attributes (to test tracing into customer code) @@ -121,6 +126,8 @@ static void validateExceptionGrpcInteraction(TestServer server, String clientTxN assertEquals(1, serverTxEvents.size()); TransactionEvent serverTxEvent = serverTxEvents.iterator().next(); assertNotNull(serverTxEvent); + assertEquals(status, serverTxEvent.getAttributes().get("response.status")); + assertEquals(status, serverTxEvent.getAttributes().get("http.statusCode")); assertEquals("grpc://localhost:" + server.getPort() + "/" + fullMethod, serverTxEvent.getAttributes().get("request.uri")); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/attributes/AttributeNames.java b/newrelic-agent/src/main/java/com/newrelic/agent/attributes/AttributeNames.java index 30984d2e0d..a72876b408 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/attributes/AttributeNames.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/attributes/AttributeNames.java @@ -36,6 +36,8 @@ public final class AttributeNames { public static final String HTTP_METHOD = "http.method"; public static final String HTTP_STATUS_CODE = "http.statusCode"; public static final String HTTP_STATUS_TEXT = "http.statusText"; + public static final String HTTP_STATUS = "httpResponseCode"; + public static final String HTTP_STATUS_MESSAGE = "httpResponseMessage"; public static final String LOCK_THREAD_NAME = "jvm.lock_thread_name"; public static final String THREAD_NAME = "jvm.thread_name"; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/AttributesConfig.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/AttributesConfig.java index 3da234bb2e..a8b1fd15ee 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/AttributesConfig.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/AttributesConfig.java @@ -19,4 +19,15 @@ public interface AttributesConfig { List attributesRootExclude(); + /** + * Whether the old http attributes (httpResponseCode, httpResponseMessage) should be sent. + * @since 8.8.1 + */ + boolean isLegacyHttpAttr(); + + /** + * Whether the new http attributes (http.statusCode, http.statusText) should be sent. + * @since 8.8.1 + */ + boolean isStandardHttpAttr(); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/AttributesConfigImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/AttributesConfigImpl.java index 019055d3dc..ff1b3dac27 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/AttributesConfigImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/AttributesConfigImpl.java @@ -8,7 +8,7 @@ package com.newrelic.agent.config; import com.newrelic.agent.Agent; -import com.newrelic.agent.attributes.AttributeNames; +import com.newrelic.agent.bridge.AgentBridge; import java.text.MessageFormat; import java.util.ArrayList; @@ -29,15 +29,38 @@ public class AttributesConfigImpl extends BaseConfig implements AttributesConfig public static final String ATTS_EXCLUDE = "attributes.exclude"; public static final String ATTS_INCLUDE = "attributes.include"; + private static final String HTTP_ATTR_MODE = "http_attribute_mode"; + private static final String HTTP_ATTR_MODE_LEGACY = "legacy"; + private static final String HTTP_ATTR_MODE_STANDARD = "standard"; + private static final String HTTP_ATTR_MODE_BOTH = "both"; + private final boolean enabledRoot; private final List attributesInclude; private final List attributeExclude; + private final boolean legacyHttpAttr; + private final boolean standardHttpAttr; public AttributesConfigImpl(Map pProps) { super(pProps, SYSTEM_PROPERTY_ROOT); enabledRoot = initEnabled(); attributesInclude = initAttributesInclude(); attributeExclude = initAttributesExclude(); + String httpAttributeMode = getProperty(HTTP_ATTR_MODE); + + boolean standardMode = HTTP_ATTR_MODE_STANDARD.equalsIgnoreCase(httpAttributeMode); + boolean legacyMode = HTTP_ATTR_MODE_LEGACY.equalsIgnoreCase(httpAttributeMode); + + // legacy is only disabled when mode is standard + legacyHttpAttr = !standardMode; + // standard is only disabled when mode is legacy + standardHttpAttr = !legacyMode; + + // logging invalid http attr mode + if (httpAttributeMode != null && !legacyMode && !standardMode && + !HTTP_ATTR_MODE_BOTH.equalsIgnoreCase(httpAttributeMode)) { + AgentBridge.getAgent().getLogger().log(Level.WARNING, "Invalid " + HTTP_ATTR_MODE + " config" + + " encountered: " + httpAttributeMode + ". Using default :" + HTTP_ATTR_MODE_BOTH + "."); + } } private boolean initEnabled() { @@ -90,6 +113,16 @@ public boolean isAttsEnabled(AgentConfig config, boolean defaultProp, String... return (toEnable || defaultProp); } + @Override + public boolean isLegacyHttpAttr() { + return legacyHttpAttr; + } + + @Override + public boolean isStandardHttpAttr() { + return standardHttpAttr; + } + private static Boolean getBooleanValue(AgentConfig config, String value) { try { Object inputObj = config.getValue(value); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/dispatchers/WebRequestDispatcher.java b/newrelic-agent/src/main/java/com/newrelic/agent/dispatchers/WebRequestDispatcher.java index 93d2b2a707..f47cd36b42 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/dispatchers/WebRequestDispatcher.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/dispatchers/WebRequestDispatcher.java @@ -16,8 +16,10 @@ import com.newrelic.agent.bridge.WebResponse; import com.newrelic.agent.config.AgentConfig; import com.newrelic.agent.config.AgentConfigImpl; +import com.newrelic.agent.config.AttributesConfig; import com.newrelic.agent.config.CustomRequestHeaderConfig; import com.newrelic.agent.config.HiddenProperties; +import com.newrelic.agent.config.TransactionEventsConfig; import com.newrelic.agent.config.TransactionTracerConfig; import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.servlet.ServletUtils; @@ -27,7 +29,6 @@ import com.newrelic.agent.tracers.servlet.ExternalTimeTracker; import com.newrelic.agent.transaction.TransactionNamer; import com.newrelic.agent.transaction.WebTransactionNamer; -import com.newrelic.agent.util.Strings; import com.newrelic.api.agent.ExtendedRequest; import com.newrelic.api.agent.Request; import com.newrelic.api.agent.Response; @@ -104,13 +105,25 @@ public void transactionActivityWithResponseFinished() { storeMethod(); storeResponseContentType(); + AttributesConfig attributesConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getAttributesConfig(); if (getStatus() > 0) { - // http.statusCode is supposed to be an int - getTransaction().getAgentAttributes().put(AttributeNames.HTTP_STATUS_CODE, getStatus()); + if (attributesConfig.isLegacyHttpAttr()) { + // http status is now being recorded as a string + getTransaction().getAgentAttributes().put(AttributeNames.HTTP_STATUS, String.valueOf(getStatus())); + } + if (attributesConfig.isStandardHttpAttr()) { + // http.statusCode is supposed to be an int + getTransaction().getAgentAttributes().put(AttributeNames.HTTP_STATUS_CODE, getStatus()); + } } if (getStatusMessage() != null) { - getTransaction().getAgentAttributes().put(AttributeNames.HTTP_STATUS_TEXT, getStatusMessage()); + if (attributesConfig.isLegacyHttpAttr()) { + getTransaction().getAgentAttributes().put(AttributeNames.HTTP_STATUS_MESSAGE, getStatusMessage()); + } + if (attributesConfig.isStandardHttpAttr()) { + getTransaction().getAgentAttributes().put(AttributeNames.HTTP_STATUS_TEXT, getStatusMessage()); + } } // adding request.uri here includes it in the Transaction event, which also propagates to any Transaction error events diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/SpanEventFactory.java b/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/SpanEventFactory.java index 7581304fd4..02464d849a 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/SpanEventFactory.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/SpanEventFactory.java @@ -11,6 +11,8 @@ import com.newrelic.agent.attributes.AttributeNames; import com.newrelic.agent.attributes.AttributeValidator; import com.newrelic.agent.config.AgentConfig; +import com.newrelic.agent.config.AttributesConfig; +import com.newrelic.agent.config.TransactionEventsConfig; import com.newrelic.agent.database.SqlObfuscator; import com.newrelic.agent.model.AttributeFilter; import com.newrelic.agent.model.SpanCategory; @@ -228,16 +230,28 @@ public SpanEventFactory setHttpComponent(String component) { } public SpanEventFactory setHttpStatusCode(Integer statusCode) { - if (filter.shouldIncludeAgentAttribute(appName, AttributeNames.HTTP_STATUS_CODE)) { + AttributesConfig attributesConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getAttributesConfig(); + if (attributesConfig.isStandardHttpAttr() && + filter.shouldIncludeAgentAttribute(appName, AttributeNames.HTTP_STATUS_CODE)) { builder.putAgentAttribute(AttributeNames.HTTP_STATUS_CODE, statusCode); } + if (attributesConfig.isLegacyHttpAttr() && + filter.shouldIncludeAgentAttribute(appName, AttributeNames.HTTP_STATUS)) { + builder.putAgentAttribute(AttributeNames.HTTP_STATUS, statusCode); + } return this; } public SpanEventFactory setHttpStatusText(String statusText) { - if (filter.shouldIncludeAgentAttribute(appName, AttributeNames.HTTP_STATUS_TEXT)) { + AttributesConfig attributesConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getAttributesConfig(); + if (attributesConfig.isStandardHttpAttr() && + filter.shouldIncludeAgentAttribute(appName, AttributeNames.HTTP_STATUS_TEXT)) { builder.putAgentAttribute(AttributeNames.HTTP_STATUS_TEXT, statusText); } + if (attributesConfig.isLegacyHttpAttr() && + filter.shouldIncludeAgentAttribute(appName, AttributeNames.HTTP_STATUS_MESSAGE)) { + builder.putAgentAttribute(AttributeNames.HTTP_STATUS_MESSAGE, statusText); + } return this; } diff --git a/newrelic-agent/src/main/resources/newrelic.yml b/newrelic-agent/src/main/resources/newrelic.yml index 5b4c271dcf..14ed8a4c9a 100644 --- a/newrelic-agent/src/main/resources/newrelic.yml +++ b/newrelic-agent/src/main/resources/newrelic.yml @@ -172,6 +172,12 @@ common: &default_settings # not be sent to New Relic. #exclude: + + # Defines which sets of http attributes the agent will send: standard, legacy or both (default). + # Having the agent send both sets will increase ingestion. + # Having the agent send only legacy may impact current or future functionality. + http_attribute_mode: both + # Transaction tracer captures deep information about slow # transactions and sends this to the New Relic service once a # minute. Included in the transaction is the exact call sequence of diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/service/analytics/SpanEventFactoryTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/service/analytics/SpanEventFactoryTest.java index 0ffaf80cf9..a8e01f8c68 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/service/analytics/SpanEventFactoryTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/service/analytics/SpanEventFactoryTest.java @@ -32,6 +32,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -113,6 +114,7 @@ public void shouldNotSetSpanErrorWhenFiltered() { @Test public void shouldSetHttpParameters() { + mockAttributeConfig(HttpAttrMode.BOTH); HttpParameters mockParameters = mock(HttpParameters.class); when(mockParameters.getLibrary()).thenReturn("library"); when(mockParameters.getProcedure()).thenReturn("procedure"); @@ -138,13 +140,34 @@ public void doesNotSetHttpAgentAttributesWhenFiltering() { @Test public void shouldSetStatusCode() { + mockAttributeConfig(HttpAttrMode.BOTH); SpanEvent spanEvent = spanEventFactory.setHttpStatusCode(418).build(); assertEquals(418, spanEvent.getAgentAttributes().get("http.statusCode")); + assertEquals(418, spanEvent.getAgentAttributes().get("httpResponseCode")); + } + + @Test + public void shouldSetStandardStatusCode() { + mockAttributeConfig(HttpAttrMode.STANDARD); + SpanEvent spanEvent = spanEventFactory.setHttpStatusCode(418).build(); + + assertEquals(418, spanEvent.getAgentAttributes().get("http.statusCode")); + assertNull(spanEvent.getAgentAttributes().get("httpResponseCode")); + } + + @Test + public void shouldSetLegacyStatusCode() { + mockAttributeConfig(HttpAttrMode.LEGACY); + SpanEvent spanEvent = spanEventFactory.setHttpStatusCode(418).build(); + + assertNull(spanEvent.getAgentAttributes().get("http.statusCode")); + assertEquals(418, spanEvent.getAgentAttributes().get("httpResponseCode")); } @Test public void shouldNotSetStatusCodeWhenFiltering() { + mockAttributeConfig(HttpAttrMode.BOTH); SpanEventFactory factory = new SpanEventFactory("blerb", new PassNothingAttributeFilter(), DEFAULT_SYSTEM_TIMESTAMP_SUPPLIER); SpanEvent spanEvent = factory.setHttpStatusCode(418).build(); @@ -160,17 +183,39 @@ public void shouldNotSetNullStatusCode() { @Test public void shouldSetStatusText() { + mockAttributeConfig(HttpAttrMode.BOTH); SpanEvent spanEvent = spanEventFactory.setHttpStatusText("I'm a teapot.").build(); assertEquals("I'm a teapot.", spanEvent.getAgentAttributes().get("http.statusText")); + assertEquals("I'm a teapot.", spanEvent.getAgentAttributes().get("httpResponseMessage")); + } + + @Test + public void shouldSetStandardStatusText() { + mockAttributeConfig(HttpAttrMode.STANDARD); + SpanEvent spanEvent = spanEventFactory.setHttpStatusText("I'm a teapot.").build(); + + assertEquals("I'm a teapot.", spanEvent.getAgentAttributes().get("http.statusText")); + assertNull(spanEvent.getAgentAttributes().get("httpResponseMessage")); + } + + @Test + public void shouldSetLegacyStatusText() { + mockAttributeConfig(HttpAttrMode.LEGACY); + SpanEvent spanEvent = spanEventFactory.setHttpStatusText("I'm a teapot.").build(); + + assertNull(spanEvent.getAgentAttributes().get("http.statusText")); + assertEquals("I'm a teapot.", spanEvent.getAgentAttributes().get("httpResponseMessage")); } @Test public void shouldNotSetStatusTextWhenFiltering() { + mockAttributeConfig(HttpAttrMode.BOTH); SpanEventFactory factory = new SpanEventFactory("blerb", new PassNothingAttributeFilter(), DEFAULT_SYSTEM_TIMESTAMP_SUPPLIER); SpanEvent spanEvent = factory.setHttpStatusText("I'm a teapot.").build(); assertNull(spanEvent.getAgentAttributes().get("http.statusText")); + assertNull(spanEvent.getAgentAttributes().get("httpResponseMessage")); } @Test @@ -178,6 +223,7 @@ public void shouldNotSetNullStatusText() { SpanEvent spanEvent = spanEventFactory.setHttpStatusText(null).build(); assertFalse(spanEvent.getAgentAttributes().containsKey("http.statusText")); + assertFalse(spanEvent.getAgentAttributes().containsKey("httpResponseMessage")); } @Test @@ -278,4 +324,25 @@ public boolean shouldIncludeAgentAttribute(String appName, String attributeName) return Collections.emptyMap(); } } + + /** + * These should never be both false. + */ + private void mockAttributeConfig(HttpAttrMode httpAttrMode) { + MockServiceManager serviceManager = new MockServiceManager(); + AgentConfig agentConfig = mock(AgentConfig.class, RETURNS_DEEP_STUBS); + serviceManager.setConfigService(new MockConfigService(agentConfig)); + + when(agentConfig.getAttributesConfig().isStandardHttpAttr()) + .thenReturn(httpAttrMode != HttpAttrMode.LEGACY); + + when(agentConfig.getAttributesConfig().isLegacyHttpAttr()) + .thenReturn(httpAttrMode != HttpAttrMode.STANDARD); + } + + private enum HttpAttrMode { + BOTH, + STANDARD, + LEGACY, + } } diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/service/analytics/TracerToSpanEventTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/service/analytics/TracerToSpanEventTest.java index 97dd485597..4870a274b3 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/service/analytics/TracerToSpanEventTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/service/analytics/TracerToSpanEventTest.java @@ -36,6 +36,8 @@ import static com.newrelic.agent.MetricNames.QUEUE_TIME; import static com.newrelic.agent.attributes.AttributeNames.HTTP_REQUEST_PREFIX; +import static com.newrelic.agent.attributes.AttributeNames.HTTP_STATUS; +import static com.newrelic.agent.attributes.AttributeNames.HTTP_STATUS_MESSAGE; import static com.newrelic.agent.attributes.AttributeNames.MESSAGE_REQUEST_PREFIX; import static com.newrelic.agent.attributes.AttributeNames.PORT; import static com.newrelic.agent.attributes.AttributeNames.QUEUE_DURATION; @@ -335,10 +337,14 @@ public void testResponseAttributesAddedToRoot() { int httpResponseCode = 404; String httpResponseMessage = "I cannot find that page, silly"; String contentType = "application/vnd.ms-powerpoint "; + expectedAgentAttributes.put("httpResponseCode", httpResponseCode); + expectedAgentAttributes.put("httpResponseMessage", httpResponseMessage); expectedAgentAttributes.put(RESPONSE_CONTENT_TYPE_PARAMETER_NAME, contentType); SpanEvent expectedSpanEvent = buildExpectedSpanEvent(); + transactionAgentAttributes.put(HTTP_STATUS, httpResponseCode); + transactionAgentAttributes.put(HTTP_STATUS_MESSAGE, httpResponseMessage); transactionAgentAttributes.put(RESPONSE_CONTENT_TYPE_PARAMETER_NAME, contentType); when(txnData.getAgentAttributes()).thenReturn(transactionAgentAttributes);