From 2f660abe95b5311f18e21ee35f5eea20d58d3f39 Mon Sep 17 00:00:00 2001 From: Andre Onuki Date: Thu, 18 Apr 2024 18:42:42 -0400 Subject: [PATCH 01/42] Adding cloud.resource_id attribute support to spans/transaction traces Setting cloud.resource_id in DynamoDB instrumentation --- .../agent/bridge/CollectionFactory.java | 11 +++ .../bridge/DefaultCollectionFactory.java | 20 ++++++ .../external/MessageConsumeParameters.java | 2 +- .../external/MessageProduceParameters.java | 4 +- .../newrelic/agent/util/AwsAccountUtil.java | 68 +++++++++++++++++++ .../agent/util/AwsAccountUtilTest.java | 25 +++++++ .../AmazonDynamoDBClient_Instrumentation.java | 40 +++++------ .../dynamodb_1_11_106/DynamoDBMetricUtil.java | 65 +++++++++++++++++- .../build.gradle | 3 +- .../dynamodb_v2/DynamoDBMetricUtil.java | 62 +++++++++++++++-- ...ltDynamoDbAsyncClient_Instrumentation.java | 44 ++++++------ ...DefaultDynamoDbClient_Instrumentation.java | 43 ++++++------ ...ultDynamoDbClient_InstrumentationTest.java | 2 + .../LocalTestDynamoDb.java | 18 +++-- instrumentation/build.gradle | 1 - .../agent/attributes/AttributeNames.java | 3 + .../service/analytics/SpanEventFactory.java | 7 +- .../newrelic/agent/tracers/DefaultTracer.java | 30 +++++--- .../agent/util/AgentCollectionFactory.java | 14 ++++ .../api/agent/DatastoreParameters.java | 32 ++++++++- .../api/agent/MessageConsumeParameters.java | 28 +++++++- .../api/agent/MessageProduceParameters.java | 28 +++++++- 22 files changed, 449 insertions(+), 101 deletions(-) create mode 100644 agent-bridge/src/main/java/com/newrelic/agent/util/AwsAccountUtil.java create mode 100644 agent-bridge/src/test/java/com/newrelic/agent/util/AwsAccountUtilTest.java diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/CollectionFactory.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/CollectionFactory.java index 20f11376c7..ed832986f0 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/CollectionFactory.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/CollectionFactory.java @@ -8,6 +8,7 @@ package com.newrelic.agent.bridge; import java.util.Map; +import java.util.function.Function; /** * Allows instrumentation and bridge API implementations to use collections from third partly libraries without @@ -27,4 +28,14 @@ public interface CollectionFactory { * @return concurrent-safe, weak-keyed map */ Map createConcurrentWeakKeyedMap(); + + /** + * Wraps the provided function into one that will cache the results for future calls. + * @param loader the function that calculates the value. + * @param maxSize the max number of items to be cached. + * @return the cached item, or the result of the loader call. + * @param the type of key + * @param the type of value stored/returned + */ + Function memoize(Function loader, int maxSize); } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/DefaultCollectionFactory.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/DefaultCollectionFactory.java index b8ba4e8785..9f04265e39 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/DefaultCollectionFactory.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/DefaultCollectionFactory.java @@ -10,11 +10,31 @@ import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +/** + * This implementation of {@link CollectionFactory} will only be used if the agent-bridge + * is being used by an application and the agent is NOT being loaded. Thus, it is unlikely + * that the objects created by this implementation are going to receive much use. + * So methods in this implementation do not need to implement all functional requirements + * of the methods in the interface, but they should not break under low use. + */ public class DefaultCollectionFactory implements CollectionFactory { @Override public Map createConcurrentWeakKeyedMap() { return Collections.synchronizedMap(new WeakHashMap()); } + + @Override + public Function memoize(Function loader, int maxSize) { + Map map = new ConcurrentHashMap<>(); + return k -> map.computeIfAbsent(k, k1 -> { + if (map.size() >= maxSize) { + map.remove(map.keySet().iterator().next()); + } + return loader.apply(k1); + }); + } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java index a580da6d98..98dfad581b 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java @@ -26,7 +26,7 @@ public class MessageConsumeParameters extends com.newrelic.api.agent.MessageCons @Deprecated protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, InboundHeaders inboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders); + super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java index ecdf60bc90..f4ed341478 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java @@ -16,7 +16,7 @@ public class MessageProduceParameters extends com.newrelic.api.agent.MessageProduceParameters implements ExternalParameters { /** - * @Deprecated Do not use. Use {@link com.newrelic.api.agent.MessageProduceParameters#MessageProduceParameters} instead. + * @Deprecated Do not use. Use the fluent builder {@link com.newrelic.api.agent.MessageProduceParameters#library(String)} instead. * * @param library * @param destinationType @@ -26,7 +26,7 @@ public class MessageProduceParameters extends com.newrelic.api.agent.MessageProd @Deprecated protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, OutboundHeaders outboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders); + super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/util/AwsAccountUtil.java b/agent-bridge/src/main/java/com/newrelic/agent/util/AwsAccountUtil.java new file mode 100644 index 0000000000..a987679d4b --- /dev/null +++ b/agent-bridge/src/main/java/com/newrelic/agent/util/AwsAccountUtil.java @@ -0,0 +1,68 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.newrelic.agent.util; + +import com.newrelic.agent.bridge.AgentBridge; + +import java.util.function.Function; + + +public class AwsAccountUtil { + private static AwsAccountUtil INSTANCE = AwsAccountUtil.create(); + + private final Function CACHE = AgentBridge.collectionFactory.memoize(this::doDecodeAccount, 32); + + public static AwsAccountUtil get() { + return INSTANCE; + } + + public Long decodeAccount(String accessKey) { + return CACHE.apply(accessKey); + } + + private Long doDecodeAccount(String awsAccessKeyId) { + String accessKeyWithoutPrefix = awsAccessKeyId.substring(4).toLowerCase(); + long encodedAccount = base32Decode(accessKeyWithoutPrefix); + // magic number + long mask = 140737488355200L; + // magic incantation to find out the account + return (encodedAccount & mask) >> 7; + } + + /** + * Character range is A-Z, 2-7. 'A' being 0 and '7', 31. + * Characters outside of this range will be considered 0. + * @param src the string to be decoded. Must be at least 10 characters. + * @return a long containing first 6 bytes of the base 32 decoded data. + * @throws ArrayIndexOutOfBoundsException if src has less than 10 characters + */ + private long base32Decode(String src) { + long base = 0; + char[] chars = src.toCharArray(); + // each char is 5 bits, we need 48 bits + for (int i = 0; i < 10; i++) { + char c = chars[i]; + base <<= 5; + if (c >= 'a' && c <= 'z') { + base += c - 'a'; + } else if (c >= '2' && c <= '7') { + base += c - '2' + 26; + } + } + // 50 bits were read, dropping the lowest 2 + return base >> 2; + } + + private AwsAccountUtil() { + // prevent instantiation of utility class + } + + private static AwsAccountUtil create() { + return new AwsAccountUtil(); + } +} diff --git a/agent-bridge/src/test/java/com/newrelic/agent/util/AwsAccountUtilTest.java b/agent-bridge/src/test/java/com/newrelic/agent/util/AwsAccountUtilTest.java new file mode 100644 index 0000000000..f368fb57d6 --- /dev/null +++ b/agent-bridge/src/test/java/com/newrelic/agent/util/AwsAccountUtilTest.java @@ -0,0 +1,25 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.newrelic.agent.util; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class AwsAccountUtilTest { + + @Test + public void decodeAccount() { + Long accountId = AwsAccountUtil.get().decodeAccount("FKKY6RVFFB77ZZZZZZZZ"); + assertEquals(999999999999L, accountId.longValue()); + + accountId = AwsAccountUtil.get().decodeAccount("FKKYQAAAAAAAZZZZZZZZ"); + assertEquals(1L, accountId.longValue()); + } + +} \ No newline at end of file diff --git a/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/main/java/com/amazonaws/services/dynamodbv2/AmazonDynamoDBClient_Instrumentation.java b/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/main/java/com/amazonaws/services/dynamodbv2/AmazonDynamoDBClient_Instrumentation.java index eb3fcd780f..ddaa75243f 100644 --- a/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/main/java/com/amazonaws/services/dynamodbv2/AmazonDynamoDBClient_Instrumentation.java +++ b/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/main/java/com/amazonaws/services/dynamodbv2/AmazonDynamoDBClient_Instrumentation.java @@ -10,6 +10,7 @@ import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.services.dynamodbv2.model.BatchGetItemRequest; import com.amazonaws.services.dynamodbv2.model.BatchGetItemResult; import com.amazonaws.services.dynamodbv2.model.BatchWriteItemRequest; @@ -64,26 +65,27 @@ public abstract class AmazonDynamoDBClient_Instrumentation extends AmazonWebServ public AmazonDynamoDBClient_Instrumentation(ClientConfiguration clientConfiguration) { super(clientConfiguration); } + private final AWSCredentialsProvider awsCredentialsProvider = Weaver.callOriginal(); @Trace(async = true, leaf = true) final CreateTableResult executeCreateTable(CreateTableRequest createTableRequest) { linkAndExpire(createTableRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "createTable", - createTableRequest.getTableName(), endpoint); + createTableRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @Trace(async = true, leaf = true) final BatchGetItemResult executeBatchGetItem(BatchGetItemRequest batchGetItemRequest) { linkAndExpire(batchGetItemRequest); - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchGetItem", "batch", endpoint); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchGetItem", "batch", endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @Trace(async = true, leaf = true) final BatchWriteItemResult executeBatchWriteItem(BatchWriteItemRequest batchWriteItemRequest) { linkAndExpire(batchWriteItemRequest); - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchWriteItem", "batch", endpoint); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchWriteItem", "batch", endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -91,7 +93,7 @@ final BatchWriteItemResult executeBatchWriteItem(BatchWriteItemRequest batchWrit final DeleteItemResult executeDeleteItem(DeleteItemRequest deleteItemRequest) { linkAndExpire(deleteItemRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteItem", - deleteItemRequest.getTableName(), endpoint); + deleteItemRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -99,14 +101,14 @@ final DeleteItemResult executeDeleteItem(DeleteItemRequest deleteItemRequest) { final DeleteTableResult executeDeleteTable(DeleteTableRequest deleteTableRequest) { linkAndExpire(deleteTableRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteTable", - deleteTableRequest.getTableName(), endpoint); + deleteTableRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @Trace(async = true, leaf = true) final DescribeLimitsResult executeDescribeLimits(DescribeLimitsRequest describeLimitsRequest) { linkAndExpire(describeLimitsRequest); - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeLimits", null, endpoint); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeLimits", null, endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -114,7 +116,7 @@ final DescribeLimitsResult executeDescribeLimits(DescribeLimitsRequest describeL final DescribeTableResult executeDescribeTable(DescribeTableRequest describeTableRequest) { linkAndExpire(describeTableRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTable", - describeTableRequest.getTableName(), endpoint); + describeTableRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -122,7 +124,7 @@ final DescribeTableResult executeDescribeTable(DescribeTableRequest describeTabl final DescribeTimeToLiveResult executeDescribeTimeToLive(DescribeTimeToLiveRequest describeTimeToLiveRequest) { linkAndExpire(describeTimeToLiveRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTimeToLive", - describeTimeToLiveRequest.getTableName(), endpoint); + describeTimeToLiveRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -130,7 +132,7 @@ final DescribeTimeToLiveResult executeDescribeTimeToLive(DescribeTimeToLiveReque final GetItemResult executeGetItem(GetItemRequest getItemRequest) { linkAndExpire(getItemRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "getItem", getItemRequest.getTableName(), - endpoint); + endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -138,14 +140,14 @@ final GetItemResult executeGetItem(GetItemRequest getItemRequest) { final ListTablesResult executeListTables(ListTablesRequest listTablesRequest) { linkAndExpire(listTablesRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTables", - listTablesRequest.getExclusiveStartTableName(), endpoint); + listTablesRequest.getExclusiveStartTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @Trace(async = true, leaf = true) final ListTagsOfResourceResult executeListTagsOfResource(ListTagsOfResourceRequest listTagsOfResourceRequest) { linkAndExpire(listTagsOfResourceRequest); - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTagsOfResource", null, endpoint); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTagsOfResource", null, endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -153,7 +155,7 @@ final ListTagsOfResourceResult executeListTagsOfResource(ListTagsOfResourceReque final PutItemResult executePutItem(PutItemRequest putItemRequest) { linkAndExpire(putItemRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "putItem", putItemRequest.getTableName(), - endpoint); + endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -161,7 +163,7 @@ final PutItemResult executePutItem(PutItemRequest putItemRequest) { final QueryResult executeQuery(QueryRequest queryRequest) { linkAndExpire(queryRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "query", queryRequest.getTableName(), - endpoint); + endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -169,21 +171,21 @@ final QueryResult executeQuery(QueryRequest queryRequest) { @Trace(async = true, leaf = true) final ScanResult executeScan(ScanRequest scanRequest) { linkAndExpire(scanRequest); - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "scan", scanRequest.getTableName(), endpoint); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "scan", scanRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @Trace(async = true, leaf = true) final TagResourceResult executeTagResource(TagResourceRequest tagResourceRequest) { linkAndExpire(tagResourceRequest); - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "tagResource", null, endpoint); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "tagResource", null, endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @Trace(async = true, leaf = true) final UntagResourceResult executeUntagResource(UntagResourceRequest untagResourceRequest) { linkAndExpire(untagResourceRequest); - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "untagResource", null, endpoint); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "untagResource", null, endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -191,7 +193,7 @@ final UntagResourceResult executeUntagResource(UntagResourceRequest untagResourc final UpdateItemResult executeUpdateItem(UpdateItemRequest updateItemRequest) { linkAndExpire(updateItemRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateItem", - updateItemRequest.getTableName(), endpoint); + updateItemRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -199,7 +201,7 @@ final UpdateItemResult executeUpdateItem(UpdateItemRequest updateItemRequest) { final UpdateTableResult executeUpdateTable(UpdateTableRequest updateTableRequest) { linkAndExpire(updateTableRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTable", - updateTableRequest.getTableName(), endpoint); + updateTableRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } @@ -207,7 +209,7 @@ final UpdateTableResult executeUpdateTable(UpdateTableRequest updateTableRequest final UpdateTimeToLiveResult executeUpdateTimeToLive(UpdateTimeToLiveRequest updateTimeToLiveRequest) { linkAndExpire(updateTimeToLiveRequest); DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTimeToLive", - updateTimeToLiveRequest.getTableName(), endpoint); + updateTimeToLiveRequest.getTableName(), endpoint, awsCredentialsProvider); return Weaver.callOriginal(); } diff --git a/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/main/java/com/nr/instrumentation/dynamodb_1_11_106/DynamoDBMetricUtil.java b/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/main/java/com/nr/instrumentation/dynamodb_1_11_106/DynamoDBMetricUtil.java index 61ce357f81..71908ca26b 100644 --- a/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/main/java/com/nr/instrumentation/dynamodb_1_11_106/DynamoDBMetricUtil.java +++ b/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/main/java/com/nr/instrumentation/dynamodb_1_11_106/DynamoDBMetricUtil.java @@ -7,11 +7,18 @@ package com.nr.instrumentation.dynamodb_1_11_106; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; import com.newrelic.agent.bridge.datastore.DatastoreVendor; +import com.newrelic.agent.util.AwsAccountUtil; import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.TracedMethod; import java.net.URI; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * This uses {@link DatastoreParameters} to create external metrics for all DynamoDB calls in @@ -22,22 +29,74 @@ public abstract class DynamoDBMetricUtil { private static final String PRODUCT = DatastoreVendor.DynamoDB.name(); private static final String INSTANCE_HOST = "amazon"; private static final String INSTANCE_ID = "dynamodb"; + private static final Pattern REGION_PATTERN = Pattern.compile("dynamodb\\.([^.]*)\\.amazonaws\\.com"); - public static void metrics(TracedMethod tracedMethod, String operation, String collection, URI endpoint) { - String host = endpoint == null ? INSTANCE_HOST : endpoint.getHost(); - String port = endpoint == null ? INSTANCE_ID : String.valueOf(getPort(endpoint)); + public static void metrics(TracedMethod tracedMethod, String operation, String collection, URI endpoint, AWSCredentialsProvider credentialsProvider) { + String host = INSTANCE_HOST; + String region = null; + if (endpoint != null) { + host = endpoint.getHost(); + region = getRegion(host); + } + String port = endpoint == null ? INSTANCE_ID : String.valueOf(getPort(endpoint)); + String arn = getArn(collection, region, credentialsProvider); DatastoreParameters params = DatastoreParameters .product(PRODUCT) .collection(collection) .operation(operation) .instance(host, port) .noDatabaseName() + .cloudResourceId(arn) .build(); tracedMethod.reportAsExternal(params); } + private static String getArn(String tableName, String region, AWSCredentialsProvider credentialsProvider) { + if (credentialsProvider == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Credentials provider is null."); + return null; + } + if (tableName == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Table name is null."); + return null; + } + if (region == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Region is null."); + return null; + } + AWSCredentials credentials = credentialsProvider.getCredentials(); + if (credentials == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Credentials is null."); + return null; + } + String accessKey = credentials.getAWSAccessKeyId(); + if (accessKey == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Access key is null."); + return null; + } + Long accountId = AwsAccountUtil.get().decodeAccount(accessKey); + if (accountId == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Unable to decode account."); + return null; + } + // arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName} + String arn = "arn:aws:dynamodb:" + region + ":" + accountId + ":table/" + tableName; + return arn; + } + + /** + * @throws NullPointerException if host is null + */ + private static String getRegion(String host) { + Matcher matcher = REGION_PATTERN.matcher(host); + if (matcher.matches()) { + return matcher.group(1); + } + return null; + } + private static int getPort(URI endpoint) { if (endpoint.getPort() > 0) { return endpoint.getPort(); diff --git a/instrumentation/aws-java-sdk-dynamodb-2.15.34/build.gradle b/instrumentation/aws-java-sdk-dynamodb-2.15.34/build.gradle index 1c7a879cef..76a451defa 100644 --- a/instrumentation/aws-java-sdk-dynamodb-2.15.34/build.gradle +++ b/instrumentation/aws-java-sdk-dynamodb-2.15.34/build.gradle @@ -4,7 +4,8 @@ dependencies { implementation platform('software.amazon.awssdk:bom:2.16.81') implementation("software.amazon.awssdk:dynamodb:2.16.81") - testImplementation("com.amazonaws:DynamoDBLocal:1.12.0") + testImplementation("com.amazonaws:DynamoDBLocal:1.25.0") + testImplementation("software.amazon.awssdk:url-connection-client") testImplementation("com.almworks.sqlite4java:sqlite4java:1.0.392") testImplementation("com.almworks.sqlite4java:libsqlite4java-osx:1.0.392") testImplementation("com.almworks.sqlite4java:libsqlite4java-linux-i386:1.0.392") diff --git a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/com/nr/instrumentation/dynamodb_v2/DynamoDBMetricUtil.java b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/com/nr/instrumentation/dynamodb_v2/DynamoDBMetricUtil.java index f0effb70d9..e8b4705c96 100644 --- a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/com/nr/instrumentation/dynamodb_v2/DynamoDBMetricUtil.java +++ b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/com/nr/instrumentation/dynamodb_v2/DynamoDBMetricUtil.java @@ -1,10 +1,19 @@ package com.nr.instrumentation.dynamodb_v2; import com.newrelic.agent.bridge.datastore.DatastoreVendor; +import com.newrelic.api.agent.Agent; import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.TracedMethod; +import com.newrelic.agent.util.AwsAccountUtil; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.awscore.client.config.AwsClientOption; +import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.regions.Region; import java.net.URI; +import java.util.logging.Level; public abstract class DynamoDBMetricUtil { @@ -12,24 +21,64 @@ public abstract class DynamoDBMetricUtil { private static final String INSTANCE_HOST = "amazon"; private static final String INSTANCE_ID = "dynamodb"; - public static void metrics(TracedMethod tracedMethod, String operation, String collection, URI endpoint) { - String host = endpoint == null ? INSTANCE_HOST : endpoint.getHost(); - String port = endpoint == null ? INSTANCE_ID : String.valueOf(getPort(endpoint)); + public static void metrics(TracedMethod tracedMethod, String operation, String tableName, SdkClientConfiguration clientConfiguration) { + String host = INSTANCE_HOST; + String port = INSTANCE_ID; + String arn = null; + if (clientConfiguration != null) { + URI endpoint = clientConfiguration.option(SdkClientOption.ENDPOINT); + if (endpoint != null) { + host = endpoint.getHost(); + port = String.valueOf(getPort(endpoint)); + } + arn = getArn(tableName, clientConfiguration); + } DatastoreParameters params = DatastoreParameters .product(PRODUCT) - .collection(collection) + .collection(tableName) .operation(operation) .instance(host, port) .noDatabaseName() + .cloudResourceId(arn) .build(); tracedMethod.reportAsExternal(params); } + private static String getArn(String tableName, SdkClientConfiguration clientConfiguration) { + if (tableName == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Table name is null."); + return null; + } + AwsCredentialsProvider credentialsProvider = clientConfiguration.option(AwsClientOption.CREDENTIALS_PROVIDER); + if (credentialsProvider == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Credentials provider is null."); + return null; + } + Region region = clientConfiguration.option(AwsClientOption.AWS_REGION); + if (region == null || region.id() == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Region is null."); + return null; + } + String accessKey = credentialsProvider.resolveCredentials().accessKeyId(); + if (accessKey == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Access key is null."); + return null; + } + Long accountId = AwsAccountUtil.get().decodeAccount(accessKey); + if (accountId == null) { + NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Unable to decode account."); + return null; + } + // arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName} + return "arn:aws:dynamodb:" + region.id() + ":" + accountId + ":table/" + tableName; + } + private static int getPort(URI endpoint) { - if (endpoint.getPort() > 0) { - return endpoint.getPort(); + int port = endpoint.getPort(); + if (port > 0) { + return port; } final String scheme = endpoint.getScheme(); @@ -40,6 +89,5 @@ private static int getPort(URI endpoint) { } return -1; } - } diff --git a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/software/amazon/awssdk/services/dynamodb/DefaultDynamoDbAsyncClient_Instrumentation.java b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/software/amazon/awssdk/services/dynamodb/DefaultDynamoDbAsyncClient_Instrumentation.java index f5e146f4e9..a84169df61 100644 --- a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/software/amazon/awssdk/services/dynamodb/DefaultDynamoDbAsyncClient_Instrumentation.java +++ b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/software/amazon/awssdk/services/dynamodb/DefaultDynamoDbAsyncClient_Instrumentation.java @@ -20,120 +20,116 @@ final class DefaultDynamoDbAsyncClient_Instrumentation { @Trace(leaf = true) public CompletableFuture scan(ScanRequest scanRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "scan", scanRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "scan", scanRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture putItem(PutItemRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "putItem", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "putItem", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture getItem(GetItemRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "getItem", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "getItem", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture deleteItem(DeleteItemRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteItem", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteItem", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture listTables(ListTablesRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTables", request.exclusiveStartTableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTables", request.exclusiveStartTableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture describeTable(DescribeTableRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTable", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTable", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture createTable(CreateTableRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "createTable", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "createTable", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture deleteTable(DeleteTableRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteTable", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteTable", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture batchGetItem(BatchGetItemRequest batchGetItemRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchGetItem", "batch", getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchGetItem", "batch", clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture batchWriteItem(BatchWriteItemRequest batchWriteItemRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchWriteItem", "batch", getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchWriteItem", "batch", clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture listTagsOfResource(ListTagsOfResourceRequest listTagsOfResourceRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTagsOfResource", listTagsOfResourceRequest.resourceArn(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTagsOfResource", listTagsOfResourceRequest.resourceArn(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture query(QueryRequest queryRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "query", queryRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "query", queryRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture updateItem(UpdateItemRequest updateItemRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateItem", updateItemRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateItem", updateItemRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture updateTable(UpdateTableRequest updateTableRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTable", updateTableRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTable", updateTableRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture updateTimeToLive(UpdateTimeToLiveRequest updateTimeToLiveRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTimeToLive", updateTimeToLiveRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTimeToLive", updateTimeToLiveRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture describeLimits(DescribeLimitsRequest describeLimitsRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeLimits", null, getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeLimits", null, clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture untagResource(UntagResourceRequest untagResourceRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "untagResource", untagResourceRequest.resourceArn(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "untagResource", untagResourceRequest.resourceArn(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture tagResource(TagResourceRequest tagResourceRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "tagResource", tagResourceRequest.resourceArn(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "tagResource", tagResourceRequest.resourceArn(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CompletableFuture describeTimeToLive(DescribeTimeToLiveRequest describeTimeToLiveRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTimeToLive", describeTimeToLiveRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTimeToLive", describeTimeToLiveRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } - - private URI getEndpoint() { - return clientConfiguration != null ? clientConfiguration.option(SdkClientOption.ENDPOINT) : null; - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/software/amazon/awssdk/services/dynamodb/DefaultDynamoDbClient_Instrumentation.java b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/software/amazon/awssdk/services/dynamodb/DefaultDynamoDbClient_Instrumentation.java index e3d32aea68..ae5568f7fe 100644 --- a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/software/amazon/awssdk/services/dynamodb/DefaultDynamoDbClient_Instrumentation.java +++ b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/main/java/software/amazon/awssdk/services/dynamodb/DefaultDynamoDbClient_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.dynamodb_v2.DynamoDBMetricUtil; +import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.services.dynamodb.model.*; @@ -18,119 +19,115 @@ final class DefaultDynamoDbClient_Instrumentation { @Trace(leaf = true) public GetItemResponse getItem(GetItemRequest getItemRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "getItem", getItemRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "getItem", getItemRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public ListTagsOfResourceResponse listTagsOfResource(ListTagsOfResourceRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTagsOfResource", null, getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTagsOfResource", null, clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public UntagResourceResponse untagResource(UntagResourceRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "untagResource", null, getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "untagResource", null, clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public BatchGetItemResponse batchGetItem(BatchGetItemRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchGetItem", "batch", getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchGetItem", "batch", clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public BatchWriteItemResponse batchWriteItem(BatchWriteItemRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchWriteItem", "batch", getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "batchWriteItem", "batch", clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public PutItemResponse putItem(PutItemRequest putItemRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "putItem", putItemRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "putItem", putItemRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public DeleteItemResponse deleteItem(DeleteItemRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteItem", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteItem", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public ListTablesResponse listTables(ListTablesRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTables", request.exclusiveStartTableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTables", request.exclusiveStartTableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public CreateTableResponse createTable(CreateTableRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "createTable", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "createTable", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public DeleteTableResponse deleteTable(DeleteTableRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteTable", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteTable", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public DescribeTableResponse describeTable(DescribeTableRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTable", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTable", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public ScanResponse scan(ScanRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "scan", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "scan", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public QueryResponse query(QueryRequest queryRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "query", queryRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "query", queryRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public UpdateItemResponse updateItem(UpdateItemRequest updateItemRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateItem", updateItemRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateItem", updateItemRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public UpdateTableResponse updateTable(UpdateTableRequest updateTableRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTable", updateTableRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTable", updateTableRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public UpdateTimeToLiveResponse updateTimeToLive(UpdateTimeToLiveRequest request) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTimeToLive", request.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTimeToLive", request.tableName(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public DescribeLimitsResponse describeLimits(DescribeLimitsRequest describeLimitsRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeLimits", null, getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeLimits", null, clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public TagResourceResponse tagResource(TagResourceRequest tagResourceRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "tagResource", tagResourceRequest.resourceArn(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "tagResource", tagResourceRequest.resourceArn(), clientConfiguration); return Weaver.callOriginal(); } @Trace(leaf = true) public DescribeTimeToLiveResponse describeTimeToLive(DescribeTimeToLiveRequest describeTimeToLiveRequest) { - DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTimeToLive", describeTimeToLiveRequest.tableName(), getEndpoint()); + DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTimeToLive", describeTimeToLiveRequest.tableName(), clientConfiguration); return Weaver.callOriginal(); } - - private URI getEndpoint() { - return clientConfiguration != null ? clientConfiguration.option(SdkClientOption.ENDPOINT) : null; - } } \ No newline at end of file diff --git a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/DefaultDynamoDbClient_InstrumentationTest.java b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/DefaultDynamoDbClient_InstrumentationTest.java index 4959dea77d..3784a2ff49 100644 --- a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/DefaultDynamoDbClient_InstrumentationTest.java +++ b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/DefaultDynamoDbClient_InstrumentationTest.java @@ -22,6 +22,8 @@ @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = {"software.amazon.awssdk.services.dynamodb", "com.nr.instrumentation"}) +@Ignore("This test ran into a problem where it uses the API from the CSEC agent instead of the agent's. " + + "Also, it is flaky on GHA.") public class DefaultDynamoDbClient_InstrumentationTest { private static final String DYNAMODB_PRODUCT = DatastoreVendor.DynamoDB.toString(); diff --git a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/LocalTestDynamoDb.java b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/LocalTestDynamoDb.java index 575f51ec22..e4d9a64acc 100644 --- a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/LocalTestDynamoDb.java +++ b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/LocalTestDynamoDb.java @@ -3,7 +3,14 @@ import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.transport.apache.ApacheHttpClientWrapper; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.internal.http.AmazonAsyncHttpClient; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -29,13 +36,16 @@ public class LocalTestDynamoDb { private LocalTestDynamoDb() throws Exception { port = String.valueOf(InstrumentationTestRunner.getIntrospector().getRandomPort()); hostName = InetAddress.getLocalHost().getHostName(); - server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", port}); + server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-disableTelemetry", "-inMemory", "-port", port}); + AwsCredentials awsCredentials = AwsBasicCredentials.create("1234QAAAAAAAZZZZZZZZ", "secret"); client = DynamoDbClient.builder() - .credentialsProvider(DefaultCredentialsProvider.builder().build()) + .credentialsProvider(StaticCredentialsProvider.create(awsCredentials)) .endpointOverride(new URI("http://localhost:" + port)) - .region(Region.US_WEST_1).build(); + .region(Region.US_WEST_1) + .httpClient(UrlConnectionHttpClient.create()) + .build(); asyncClient = DynamoDbAsyncClient.builder() - .credentialsProvider(DefaultCredentialsProvider.create()) + .credentialsProvider(StaticCredentialsProvider.create(awsCredentials)) .endpointOverride(new URI("http://localhost:" + port)) .region(Region.US_WEST_1).build(); } diff --git a/instrumentation/build.gradle b/instrumentation/build.gradle index 9754867a12..2c6da3f1cb 100644 --- a/instrumentation/build.gradle +++ b/instrumentation/build.gradle @@ -41,7 +41,6 @@ subprojects { exclude '**/BasicRequestsTest.class' exclude '**/BlazeClientTest' exclude '**/CassandraTest.class' - exclude '**/DefaultDynamoDbClient_InstrumentationTest.class' exclude '**/QuartzSystemTest.class' exclude '**/RabbitMQTest.class' } 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 a72876b408..d4af6c63c4 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 @@ -53,6 +53,9 @@ public final class AttributeNames { public static final String REQUEST_USER_AGENT_PARAMETER_NAME = "request.headers.userAgent"; public static final String REQUEST_METHOD_PARAMETER_NAME = "request.method"; + // cloud provider identifier for the resource being used + public static final String CLOUD_RESOURCE_ID = "cloud.resource_id"; + public static final String RESPONSE_CONTENT_TYPE_PARAMETER_NAME = "response.headers.contentType"; // high security matches 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 c7ae11defc..7667a1c133 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 @@ -12,7 +12,6 @@ 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; @@ -282,6 +281,11 @@ public SpanEventFactory setServerAddress(String host) { return this; } + public SpanEventFactory setCloudResourceId(String cloudResourceId) { + builder.putAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID,cloudResourceId); + return this; + } + public SpanEventFactory setServerPort(int port) { builder.putAgentAttribute("server.port", port); return this; @@ -386,6 +390,7 @@ public SpanEventFactory setExternalParameterAttributes(ExternalParameters parame setDatabaseCollection(datastoreParameters.getCollection()); setDatabaseOperation(datastoreParameters.getOperation()); setServerAddress(datastoreParameters.getHost()); + setCloudResourceId(datastoreParameters.getCloudResourceId()); setKindFromUserAttributes(); if (datastoreParameters.getPort() != null) { setServerPort(datastoreParameters.getPort()); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 19278de084..21db2749bd 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -661,9 +661,9 @@ private void recordExternalMetrics() { } else if (externalParameters instanceof HttpParameters) { recordExternalMetricsHttp((HttpParameters) externalParameters); } else if (externalParameters instanceof MessageProduceParameters) { - recordMessageBrokerMetrics(((MessageProduceParameters) this.externalParameters)); + recordMessageBrokerMetrics((MessageProduceParameters) this.externalParameters); } else if (externalParameters instanceof MessageConsumeParameters) { - recordMessageBrokerMetrics(((MessageConsumeParameters) this.externalParameters)); + recordMessageBrokerMetrics((MessageConsumeParameters) this.externalParameters); } else { Agent.LOG.log(Level.SEVERE, "Unknown externalParameters type. This should not happen. {0} -- {1}", externalParameters, externalParameters.getClass()); @@ -729,12 +729,17 @@ private void recordExternalMetricsDatastore(DatastoreParameters datastoreParamet datastoreParameters.getDatabaseName()); DatastoreConfig datastoreConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getDatastoreConfig(); - boolean allUnknown = datastoreParameters.getHost() == null && datastoreParameters.getPort() == null - && datastoreParameters.getPathOrId() == null; - if (datastoreConfig.isInstanceReportingEnabled() && !allUnknown) { - setAgentAttribute(DatastoreMetrics.DATASTORE_HOST, DatastoreMetrics.replaceLocalhost(datastoreParameters.getHost())); - setAgentAttribute(DatastoreMetrics.DATASTORE_PORT_PATH_OR_ID, DatastoreMetrics.getIdentifierOrPort( - datastoreParameters.getPort(), datastoreParameters.getPathOrId())); + if (datastoreConfig.isInstanceReportingEnabled()) { + boolean allUnknown = datastoreParameters.getHost() == null && datastoreParameters.getPort() == null + && datastoreParameters.getPathOrId() == null; + if (!allUnknown) { + setAgentAttribute(DatastoreMetrics.DATASTORE_HOST, DatastoreMetrics.replaceLocalhost(datastoreParameters.getHost())); + setAgentAttribute(DatastoreMetrics.DATASTORE_PORT_PATH_OR_ID, DatastoreMetrics.getIdentifierOrPort( + datastoreParameters.getPort(), datastoreParameters.getPathOrId())); + } + if (datastoreParameters.getCloudResourceId() != null) { + setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, datastoreParameters.getCloudResourceId()); + } } // Spec says this is a should, only send database name when we actually have one. @@ -815,6 +820,10 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP messageProduceParameters.getLibrary(), messageProduceParameters.getDestinationType().getTypeName())); } + + if (messageProduceParameters.getCloudResourceId() != null) { + setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageProduceParameters.getCloudResourceId()); + } } private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeParameters) { @@ -834,6 +843,10 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP messageConsumeParameters.getLibrary(), messageConsumeParameters.getDestinationType().getTypeName())); } + + if (messageConsumeParameters.getCloudResourceId() != null) { + setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageConsumeParameters.getCloudResourceId()); + } } private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { @@ -844,5 +857,4 @@ private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDa transaction.getSlowQueryListener(true).noticeTracer(this, slowQueryDatastoreParameters); } } - } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/util/AgentCollectionFactory.java b/newrelic-agent/src/main/java/com/newrelic/agent/util/AgentCollectionFactory.java index 3e2ccffa9d..265e4a631e 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/util/AgentCollectionFactory.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/util/AgentCollectionFactory.java @@ -8,11 +8,16 @@ package com.newrelic.agent.util; import java.util.Map; +import java.util.function.Function; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; import com.newrelic.agent.bridge.CollectionFactory; +/** + * This is the main instrumentation of CollectionFactory which is used when the agent is loaded. + */ public class AgentCollectionFactory implements CollectionFactory { @Override @@ -20,4 +25,13 @@ public Map createConcurrentWeakKeyedMap() { Cache cache = Caffeine.newBuilder().initialCapacity(32).weakKeys().executor(Runnable::run).build(); return cache.asMap(); } + + @Override + public Function memoize(Function loader, int maxSize) { + LoadingCache cache = Caffeine.newBuilder() + .maximumSize(maxSize) + .executor(Runnable::run) + .build(loader::apply); + return cache::get; + } } diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/DatastoreParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/DatastoreParameters.java index e1f4fede83..006e2ddde8 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/DatastoreParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/DatastoreParameters.java @@ -51,8 +51,13 @@ public class DatastoreParameters implements ExternalParameters { */ private final String databaseName; + /** + * The cloud provider's identifier for this resource. Eg. in AWS, this should be an ARN. + */ + private final String cloudResourceId; + private DatastoreParameters(String product, String collection, String operation, String host, Integer port, - String pathOrId, String databaseName) { + String pathOrId, String databaseName, String cloudResourceId) { this.product = product; this.collection = collection; this.operation = operation; @@ -60,6 +65,7 @@ private DatastoreParameters(String product, String collection, String operation, this.port = port; this.pathOrId = pathOrId; this.databaseName = databaseName; + this.cloudResourceId = cloudResourceId; } protected DatastoreParameters(DatastoreParameters datastoreParameters) { @@ -70,6 +76,7 @@ protected DatastoreParameters(DatastoreParameters datastoreParameters) { this.port = datastoreParameters.port; this.pathOrId = datastoreParameters.pathOrId; this.databaseName = datastoreParameters.databaseName; + this.cloudResourceId = datastoreParameters.cloudResourceId; } protected static class Builder implements CollectionParameter, OperationParameter, InstanceParameter, @@ -81,6 +88,7 @@ protected static class Builder implements CollectionParameter, OperationParamete private Integer port = null; private String pathOrId = null; private String databaseName = null; + private String cloudResourceId = null; /** * Used for {@link SlowQueryDatastoreParameters}. The builder method below gives us type safety here. @@ -136,6 +144,12 @@ public SlowQueryParameter databaseName(String databaseName) { return this; } + @Override + public Build cloudResourceId(String cloudResourceId) { + this.cloudResourceId = cloudResourceId; + return this; + } + @Override public DatastoreParameters build() { if (inputQueryLabel != null && rawInputQuery != null && rawInputQueryConverter != null) { @@ -167,7 +181,7 @@ public SlowQueryWithInputParameter noSlowQuery() { } private DatastoreParameters buildRegular() { - return new DatastoreParameters(product, collection, operation, host, port, pathOrId, databaseName); + return new DatastoreParameters(product, collection, operation, host, port, pathOrId, databaseName, cloudResourceId); } private SlowQueryDatastoreParameters buildWithSlowQuery() { @@ -253,6 +267,13 @@ public String getDatabaseName() { return databaseName; } + /** + * @return the cloud provider's identifier for the message queue. Eg. in AWS, this should be an ARN. + */ + public String getCloudResourceId() { + return cloudResourceId; + } + // Builder Interfaces /** @@ -377,6 +398,13 @@ public interface SlowQueryWithInputParameter extends Build { public interface Build { + /** + * Set the cloud provider's id for the database. + * This method is optional and can be bypassed by calling build directly. + * @return the build object so it can be built. + */ + Build cloudResourceId(String cloudResourceId); + /** * Build the final {@link DatastoreParameters} for the API call. * diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java index d65d872e9f..69b1236aab 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java @@ -20,20 +20,29 @@ public class MessageConsumeParameters implements ExternalParameters { private final DestinationType destinationType; private final String destinationName; private final InboundHeaders inboundHeaders; + private final String cloudResourceId; protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, - InboundHeaders inboundHeaders) { + InboundHeaders inboundHeaders, String cloudResourceId) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; this.inboundHeaders = inboundHeaders; + this.cloudResourceId = cloudResourceId; } + + /** + * This method creates a clone of a MessageConsumerParameters. + * This class is immutable, so the original object can be reused. + */ + @Deprecated protected MessageConsumeParameters(MessageConsumeParameters messageConsumeParameters) { this.library = messageConsumeParameters.library; this.destinationType = messageConsumeParameters.destinationType; this.destinationName = messageConsumeParameters.destinationName; this.inboundHeaders = messageConsumeParameters.inboundHeaders; + this.cloudResourceId = messageConsumeParameters.cloudResourceId; } public String getDestinationName() { @@ -48,6 +57,10 @@ public InboundHeaders getInboundHeaders() { return inboundHeaders; } + public String getCloudResourceId() { + return cloudResourceId; + } + public String getLibrary() { return library; } @@ -58,6 +71,7 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private DestinationType destinationType; private String destinationName; private InboundHeaders inboundHeaders; + private String cloudResourceId; public Builder(String library) { this.library = library; @@ -78,8 +92,13 @@ public Build inboundHeaders(InboundHeaders inboundHeaders) { return this; } + public Build cloudResourceId(String cloudResourceId) { + this.cloudResourceId = cloudResourceId; + return this; + } + public MessageConsumeParameters build() { - return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders); + return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId); } } @@ -128,6 +147,11 @@ public interface InboundHeadersParameter { public interface Build { + /** + * Set the cloud provider's id for the message queue. + * This method is optional and can be bypassed by calling build directly. + */ + Build cloudResourceId(String cloudResourceId); /** * Build the final {@link MessageConsumeParameters} for the API call. * diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java index a5fa2ff7be..487ddb8690 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java @@ -19,20 +19,28 @@ public class MessageProduceParameters implements ExternalParameters { private final DestinationType destinationType; private final String destinationName; private final OutboundHeaders outboundHeaders; + private final String cloudResourceId; protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, - OutboundHeaders outboundHeaders) { + OutboundHeaders outboundHeaders, String cloudResourceId) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; this.outboundHeaders = outboundHeaders; + this.cloudResourceId = cloudResourceId; } + /** + * This method creates a clone of a MessageProduceParameters. + * This class is immutable, so the original object can be reused. + */ + @Deprecated protected MessageProduceParameters(MessageProduceParameters messageProduceParameters) { this.library = messageProduceParameters.library; this.destinationType = messageProduceParameters.destinationType; this.destinationName = messageProduceParameters.destinationName; this.outboundHeaders = messageProduceParameters.outboundHeaders; + this.cloudResourceId = messageProduceParameters.cloudResourceId; } public String getDestinationName() { @@ -47,6 +55,10 @@ public OutboundHeaders getOutboundHeaders() { return outboundHeaders; } + public String getCloudResourceId() { + return cloudResourceId; + } + public String getLibrary() { return library; } @@ -57,6 +69,7 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private DestinationType destinationType; private String destinationName; private OutboundHeaders outboundHeaders; + private String cloudResourceId; public Builder(String library) { this.library = library; @@ -77,8 +90,13 @@ public Build outboundHeaders(OutboundHeaders outboundHeaders) { return this; } + public Build cloudResourceId(String cloudResourceId) { + this.cloudResourceId = cloudResourceId; + return this; + } + public MessageProduceParameters build() { - return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders); + return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId); } } @@ -128,6 +146,12 @@ public interface OutboundHeadersParameter { public interface Build { + /** + * Set the cloud provider's id for the message queue. + * This method is optional and can be bypassed by calling build directly. + */ + Build cloudResourceId(String cloudResourceId); + /** * Build the final {@link MessageProduceParameters} for the API call. * From d2f836469c25df98d0e666e96ea0b94f56f19fd5 Mon Sep 17 00:00:00 2001 From: Andre Onuki Date: Fri, 19 Apr 2024 15:53:02 -0400 Subject: [PATCH 02/42] Adding SpanFactory unit test --- .../analytics/SpanEventFactoryTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) 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 b5a96019a8..cdfd93b46e 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 @@ -248,6 +248,28 @@ public void shouldSetDataStoreParameters() { assertEquals("dbserver:3306", target.getAgentAttributes().get("peer.address")); } + @Test + public void shouldSetCloudResourceIdParameters() { + DatastoreParameters mockParameters = mock(DatastoreParameters.class); + when(mockParameters.getOperation()).thenReturn("putItem"); + when(mockParameters.getCollection()).thenReturn("tableName"); + when(mockParameters.getProduct()).thenReturn("DynamoDB"); + when(mockParameters.getHost()).thenReturn("dbserver"); + when(mockParameters.getPort()).thenReturn(1234); + when(mockParameters.getCloudResourceId()).thenReturn("arn:aws:dynamodb:us-west-1:123456789012:tableName"); + SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); + + Map agentAttrs = target.getAgentAttributes(); + assertEquals("putItem", agentAttrs.get("db.operation")); + assertEquals("tableName", agentAttrs.get("db.collection")); + assertEquals("DynamoDB", agentAttrs.get("db.system")); + assertEquals("dbserver", agentAttrs.get("peer.hostname")); + assertEquals("dbserver", agentAttrs.get("server.address")); + assertEquals(1234, agentAttrs.get("server.port")); + assertEquals("dbserver:1234", agentAttrs.get("peer.address")); + assertEquals("arn:aws:dynamodb:us-west-1:123456789012:tableName", agentAttrs.get("cloud.resource_id")); + } + @Test public void shouldStoreStackTrace() { SpanEventFactory spanEventFactory = new SpanEventFactory("MyApp", new AttributeFilter.PassEverythingAttributeFilter(), From 96f7f1684cb53b730c7f9a90e493b9832566e3e9 Mon Sep 17 00:00:00 2001 From: Andre Onuki Date: Fri, 19 Apr 2024 16:33:58 -0400 Subject: [PATCH 03/42] Reverting where DynamoDB instrumentation test is ignored --- .../DefaultDynamoDbClient_InstrumentationTest.java | 3 +-- instrumentation/build.gradle | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/DefaultDynamoDbClient_InstrumentationTest.java b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/DefaultDynamoDbClient_InstrumentationTest.java index 3784a2ff49..8599bd3980 100644 --- a/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/DefaultDynamoDbClient_InstrumentationTest.java +++ b/instrumentation/aws-java-sdk-dynamodb-2.15.34/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb_v2/DefaultDynamoDbClient_InstrumentationTest.java @@ -20,10 +20,9 @@ import static junit.framework.TestCase.assertEquals; +// this test is ignored in instrumentation/build.gradle @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = {"software.amazon.awssdk.services.dynamodb", "com.nr.instrumentation"}) -@Ignore("This test ran into a problem where it uses the API from the CSEC agent instead of the agent's. " + - "Also, it is flaky on GHA.") public class DefaultDynamoDbClient_InstrumentationTest { private static final String DYNAMODB_PRODUCT = DatastoreVendor.DynamoDB.toString(); diff --git a/instrumentation/build.gradle b/instrumentation/build.gradle index 2c6da3f1cb..745c185e00 100644 --- a/instrumentation/build.gradle +++ b/instrumentation/build.gradle @@ -40,6 +40,7 @@ subprojects { // It is likely they can be fixed with test containers exclude '**/BasicRequestsTest.class' exclude '**/BlazeClientTest' + exclude '**/DefaultDynamoDbClient_InstrumentationTest.class' exclude '**/CassandraTest.class' exclude '**/QuartzSystemTest.class' exclude '**/RabbitMQTest.class' From 557bdd351f6cae058c175d85cdd3145f0e3980aa Mon Sep 17 00:00:00 2001 From: Andre Onuki Date: Mon, 22 Apr 2024 12:01:31 -0400 Subject: [PATCH 04/42] Disabling DynamoDB 1 tests --- instrumentation/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/instrumentation/build.gradle b/instrumentation/build.gradle index 745c185e00..d7ca9d0314 100644 --- a/instrumentation/build.gradle +++ b/instrumentation/build.gradle @@ -40,8 +40,9 @@ subprojects { // It is likely they can be fixed with test containers exclude '**/BasicRequestsTest.class' exclude '**/BlazeClientTest' - exclude '**/DefaultDynamoDbClient_InstrumentationTest.class' exclude '**/CassandraTest.class' + exclude '**/DefaultDynamoDbClient_InstrumentationTest.class' + exclude '**/DynamoApiTest' exclude '**/QuartzSystemTest.class' exclude '**/RabbitMQTest.class' } From 21035556e2c8ff75f416d9bcd7d3efd271f2c6d6 Mon Sep 17 00:00:00 2001 From: Andre Onuki Date: Tue, 23 Apr 2024 09:50:31 -0400 Subject: [PATCH 05/42] Ignoring test in DynamoDB v1 --- .../awsjavasdkdynamodb1_11_106/DynamoApiTest.java | 2 ++ instrumentation/build.gradle | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb1_11_106/DynamoApiTest.java b/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb1_11_106/DynamoApiTest.java index 13d10bc01c..265e731e2d 100644 --- a/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb1_11_106/DynamoApiTest.java +++ b/instrumentation/aws-java-sdk-dynamodb-1.11.106/src/test/java/com/agent/instrumentation/awsjavasdkdynamodb1_11_106/DynamoApiTest.java @@ -56,6 +56,7 @@ import com.newrelic.api.agent.Trace; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -77,6 +78,7 @@ @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "com.amazonaws", "com.nr.instrumentation" }) +@Ignore("This test is running into some incompatibilities with a dependency.") public class DynamoApiTest { private static String hostName; diff --git a/instrumentation/build.gradle b/instrumentation/build.gradle index d7ca9d0314..9754867a12 100644 --- a/instrumentation/build.gradle +++ b/instrumentation/build.gradle @@ -42,7 +42,6 @@ subprojects { exclude '**/BlazeClientTest' exclude '**/CassandraTest.class' exclude '**/DefaultDynamoDbClient_InstrumentationTest.class' - exclude '**/DynamoApiTest' exclude '**/QuartzSystemTest.class' exclude '**/RabbitMQTest.class' } From 99977def7552818ab99a6860b9d320b9c1b813ab Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Wed, 1 May 2024 17:40:34 -0700 Subject: [PATCH 06/42] Add cloud.resource_id attribute to message produce/consume spans. Add related tests. --- .../service/analytics/SpanEventFactory.java | 10 +++++ .../analytics/SpanEventFactoryTest.java | 45 ++++++++++++++----- 2 files changed, 45 insertions(+), 10 deletions(-) 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 7667a1c133..4fd60056b5 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 @@ -24,6 +24,8 @@ import com.newrelic.api.agent.DatastoreParameters; import com.newrelic.api.agent.ExternalParameters; import com.newrelic.api.agent.HttpParameters; +import com.newrelic.api.agent.MessageConsumeParameters; +import com.newrelic.api.agent.MessageProduceParameters; import com.newrelic.api.agent.SlowQueryDatastoreParameters; import java.net.URI; @@ -404,6 +406,14 @@ public SpanEventFactory setExternalParameterAttributes(ExternalParameters parame } else { setAddress(datastoreParameters.getHost(), datastoreParameters.getPathOrId()); } + } else if (parameters instanceof MessageProduceParameters) { + MessageProduceParameters messageProduceParameters = (MessageProduceParameters) parameters; + setCategory(SpanCategory.generic); + setCloudResourceId(messageProduceParameters.getCloudResourceId()); + } else if (parameters instanceof MessageConsumeParameters) { + MessageConsumeParameters messageConsumeParameters = (MessageConsumeParameters) parameters; + setCategory(SpanCategory.generic); + setCloudResourceId(messageConsumeParameters.getCloudResourceId()); } else { setCategory(SpanCategory.generic); } 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 cdfd93b46e..a59d7a711a 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 @@ -19,7 +19,10 @@ import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.tracers.DefaultTracer; import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.HttpParameters; +import com.newrelic.api.agent.MessageConsumeParameters; +import com.newrelic.api.agent.MessageProduceParameters; import org.junit.Test; import java.net.URI; @@ -249,25 +252,47 @@ public void shouldSetDataStoreParameters() { } @Test - public void shouldSetCloudResourceIdParameters() { + public void shouldSetCloudResourceIdOnSpanFromDatastoreParameters() { + String expectedArn = "arn:aws:dynamodb:us-west-1:123456789012:tableName"; DatastoreParameters mockParameters = mock(DatastoreParameters.class); when(mockParameters.getOperation()).thenReturn("putItem"); when(mockParameters.getCollection()).thenReturn("tableName"); when(mockParameters.getProduct()).thenReturn("DynamoDB"); when(mockParameters.getHost()).thenReturn("dbserver"); when(mockParameters.getPort()).thenReturn(1234); - when(mockParameters.getCloudResourceId()).thenReturn("arn:aws:dynamodb:us-west-1:123456789012:tableName"); + when(mockParameters.getCloudResourceId()).thenReturn(expectedArn); SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); Map agentAttrs = target.getAgentAttributes(); - assertEquals("putItem", agentAttrs.get("db.operation")); - assertEquals("tableName", agentAttrs.get("db.collection")); - assertEquals("DynamoDB", agentAttrs.get("db.system")); - assertEquals("dbserver", agentAttrs.get("peer.hostname")); - assertEquals("dbserver", agentAttrs.get("server.address")); - assertEquals(1234, agentAttrs.get("server.port")); - assertEquals("dbserver:1234", agentAttrs.get("peer.address")); - assertEquals("arn:aws:dynamodb:us-west-1:123456789012:tableName", agentAttrs.get("cloud.resource_id")); + assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); + } + + @Test + public void shouldSetCloudResourceIdOnSpanFromMessageProduceParameters() { + String expectedArn = "arn:aws:sqs:us-east-1:123456789012:queueName"; + MessageProduceParameters mockParameters = mock(MessageProduceParameters.class); + when(mockParameters.getLibrary()).thenReturn("SQS"); + when(mockParameters.getDestinationName()).thenReturn("queueName"); + when(mockParameters.getDestinationType()).thenReturn(DestinationType.NAMED_QUEUE); + when(mockParameters.getCloudResourceId()).thenReturn(expectedArn); + SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); + + Map agentAttrs = target.getAgentAttributes(); + assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); + } + + @Test + public void shouldSetCloudResourceIdOnSpanFromMessageConsumeParameters() { + String expectedArn = "arn:aws:sqs:us-east-1:123456789012:queueName"; + MessageConsumeParameters mockParameters = mock(MessageConsumeParameters.class); + when(mockParameters.getLibrary()).thenReturn("SQS"); + when(mockParameters.getDestinationName()).thenReturn("queueName"); + when(mockParameters.getDestinationType()).thenReturn(DestinationType.NAMED_QUEUE); + when(mockParameters.getCloudResourceId()).thenReturn(expectedArn); + SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); + + Map agentAttrs = target.getAgentAttributes(); + assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); } @Test From e049b9526b759d538ce928f671869c6b181564d2 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Thu, 2 May 2024 17:15:21 -0400 Subject: [PATCH 07/42] Add host/port to external message api for amqp --- .../rabbitamqp27/RabbitAMQPMetricUtil.java | 18 +++++++- .../QueueingConsumer_Instrumentation.java | 11 ++++- .../client/impl/ChannelN_Instrumentation.java | 8 +++- .../rabbitamqp350/RabbitAMQPMetricUtil.java | 25 ++++++++++- .../QueueingConsumer_Instrumentation.java | 11 ++++- .../client/impl/ChannelN_Instrumentation.java | 8 +++- .../rabbitamqp500/RabbitAMQPMetricUtil.java | 26 ++++++++++- .../client/impl/ChannelN_Instrumentation.java | 9 +++- .../java/com/newrelic/agent/MetricNames.java | 2 + .../newrelic/agent/tracers/DefaultTracer.java | 15 +++++++ .../api/agent/MessageConsumeParameters.java | 43 ++++++++++++++++++- .../api/agent/MessageProduceParameters.java | 42 +++++++++++++++++- 12 files changed, 200 insertions(+), 18 deletions(-) diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java index 4844e01f74..8cc57c6079 100644 --- a/instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java @@ -14,7 +14,9 @@ import com.newrelic.api.agent.MessageConsumeParameters; import com.newrelic.api.agent.MessageProduceParameters; import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Connection; +import java.net.InetAddress; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; @@ -41,24 +43,28 @@ public static void nameTransaction(String exchangeName) { public static void processSendMessage(String exchangeName, String routingKey, HashMap headers, - AMQP.BasicProperties props, TracedMethod tracedMethod) { + AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) + .host(getHost(connection)) + .port(getPort(connection)) .build()); addAttributes(routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, - AMQP.BasicProperties properties, TracedMethod tracedMethod) { + AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .host(getHost(connection)) + .port(getPort(connection)) .build()); addConsumeAttributes(queueName, routingKey, properties); @@ -76,6 +82,14 @@ public static void queuePurge(String queue, TracedMethod tracedMethod) { RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); } + private static String getHost(Connection connection) { + return (connection != null) ? connection.getHost() : null; + } + + private static Integer getPort(Connection connection) { + return (connection != null) ? connection.getPort() : null; + } + private static void addAttributes(String routingKey, AMQP.BasicProperties properties) { if (!captureSegmentParameters) { return; diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java index 571b18df3f..aa4bec3414 100644 --- a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java @@ -18,6 +18,10 @@ @Weave(type = MatchType.BaseClass, originalName = "com.rabbitmq.client.QueueingConsumer") public abstract class QueueingConsumer_Instrumentation { + public Channel getChannel() { + return Weaver.callOriginal(); + } + @Weave(originalName = "com.rabbitmq.client.QueueingConsumer$Delivery") public static class Delivery_Instrumentation { public BasicProperties getProperties() { @@ -34,8 +38,13 @@ public QueueingConsumer.Delivery nextDelivery() { QueueingConsumer.Delivery delivery = Weaver.callOriginal(); Envelope envelope = delivery.getEnvelope(); BasicProperties props = delivery.getProperties(); + Connection connection = null; + Channel channel = getChannel(); + if (channel != null) { + connection = channel.getConnection(); + } RabbitAMQPMetricUtil.processGetMessage(null, envelope.getRoutingKey(), - envelope.getExchange(), props, AgentBridge.getAgent().getTracedMethod()); + envelope.getExchange(), props, AgentBridge.getAgent().getTracedMethod(), connection); RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); return delivery; } diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index efc32d7bab..917b7e080a 100644 --- a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -22,6 +22,10 @@ @Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") public class ChannelN_Instrumentation { + AMQConnection getConnection() { + return Weaver.callOriginal(); + } + @Trace public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) { @@ -37,7 +41,7 @@ public void basicPublish(String exchange, String routingKey, boolean mandatory, headers.putAll(props.getHeaders()); } RabbitAMQPMetricUtil.processSendMessage(exchange, routingKey, headers, props, - AgentBridge.getAgent().getTracedMethod()); + AgentBridge.getAgent().getTracedMethod(), getConnection()); props.setHeaders(headers); Weaver.callOriginal(); } @@ -51,7 +55,7 @@ public GetResponse basicGet(String queue, boolean autoAck) { if (response != null) { RabbitAMQPMetricUtil.processGetMessage(queue, response.getEnvelope().getRoutingKey(), response.getEnvelope().getExchange(), response.getProps(), - AgentBridge.getAgent().getTracedMethod()); + AgentBridge.getAgent().getTracedMethod(), getConnection()); } return response; } diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitAMQPMetricUtil.java index d10e03531f..108b2bab38 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitAMQPMetricUtil.java @@ -14,7 +14,9 @@ import com.newrelic.api.agent.MessageConsumeParameters; import com.newrelic.api.agent.MessageProduceParameters; import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Connection; +import java.net.InetAddress; import java.text.MessageFormat; import java.util.Map; @@ -39,24 +41,28 @@ public static void nameTransaction(String exchangeName) { } public static void processSendMessage(String exchangeName, String routingKey, Map headers, - AMQP.BasicProperties props, TracedMethod tracedMethod) { + AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) + .host(getHost(connection)) + .port(getPort(connection)) .build()); addAttributes(routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, - AMQP.BasicProperties properties, TracedMethod tracedMethod) { + AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .host(getHost(connection)) + .port(getPort(connection)) .build()); addConsumeAttributes(queueName, routingKey, properties); @@ -74,6 +80,21 @@ public static void queuePurge(String queue, TracedMethod tracedMethod) { RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); } + private static String getHost(Connection connection) { + String host = null; + if (connection != null) { + InetAddress address = connection.getAddress(); + if (address != null) { + host = address.getHostName(); + } + } + return host; + } + + private static Integer getPort(Connection connection) { + return (connection != null) ? connection.getPort() : null; + } + private static void addAttributes(String routingKey, AMQP.BasicProperties properties) { if (!captureSegmentParameters) { return; diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java index f4b89523f2..a119fe2fe0 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java @@ -18,6 +18,10 @@ @Weave(type = MatchType.BaseClass, originalName = "com.rabbitmq.client.QueueingConsumer") public abstract class QueueingConsumer_Instrumentation { + public Channel getChannel() { + return Weaver.callOriginal(); + } + @Weave(originalName = "com.rabbitmq.client.QueueingConsumer$Delivery") public static class Delivery_Instrumentation { public BasicProperties getProperties() { @@ -34,8 +38,13 @@ public QueueingConsumer.Delivery nextDelivery() { QueueingConsumer.Delivery delivery = Weaver.callOriginal(); Envelope envelope = delivery.getEnvelope(); BasicProperties props = delivery.getProperties(); + Connection connection = null; + Channel channel = getChannel(); + if (channel != null) { + connection = channel.getConnection(); + } RabbitAMQPMetricUtil.processGetMessage(null, envelope.getRoutingKey(), - envelope.getExchange(), props, AgentBridge.getAgent().getTracedMethod()); + envelope.getExchange(), props, AgentBridge.getAgent().getTracedMethod(), connection); RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); return delivery; } diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index e6c3e47679..584ba28295 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -22,6 +22,10 @@ @Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") public class ChannelN_Instrumentation { + AMQConnection getConnection() { + return Weaver.callOriginal(); + } + @Trace public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) { @@ -37,7 +41,7 @@ public void basicPublish(String exchange, String routingKey, boolean mandatory, headers.putAll(props.getHeaders()); } RabbitAMQPMetricUtil.processSendMessage(exchange, routingKey, headers, props, - AgentBridge.getAgent().getTracedMethod()); + AgentBridge.getAgent().getTracedMethod(), getConnection()); props = props.builder().headers(headers).build(); Weaver.callOriginal(); } @@ -51,7 +55,7 @@ public GetResponse basicGet(String queue, boolean autoAck) { if (response != null) { RabbitAMQPMetricUtil.processGetMessage(queue, response.getEnvelope().getRoutingKey(), response.getEnvelope().getExchange(), response.getProps(), - AgentBridge.getAgent().getTracedMethod()); + AgentBridge.getAgent().getTracedMethod(), getConnection()); } return response; } diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index 378525cc90..7430bafd8c 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -14,7 +14,10 @@ import com.newrelic.api.agent.MessageConsumeParameters; import com.newrelic.api.agent.MessageProduceParameters; import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.impl.AMQConnection; +import java.net.InetAddress; import java.text.MessageFormat; import java.util.Map; @@ -39,24 +42,28 @@ public static void nameTransaction(String exchangeName) { } public static void processSendMessage(String exchangeName, String routingKey, Map headers, - AMQP.BasicProperties props, TracedMethod tracedMethod) { + AMQP.BasicProperties props, TracedMethod tracedMethod, AMQConnection connection) { tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) + .host(getHost(connection)) + .port(getPort(connection)) .build()); addAttributes(routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, - AMQP.BasicProperties properties, TracedMethod tracedMethod) { + AMQP.BasicProperties properties, TracedMethod tracedMethod, AMQConnection connection) { tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .host(getHost(connection)) + .port(getPort(connection)) .build()); addConsumeAttributes(queueName, routingKey, properties); @@ -74,6 +81,21 @@ public static void queuePurge(String queue, TracedMethod tracedMethod) { RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); } + private static String getHost(AMQConnection connection) { + String host = null; + if (connection != null) { + InetAddress address = connection.getAddress(); + if (address != null) { + host = address.getHostName(); + } + } + return host; + } + + private static Integer getPort(Connection connection) { + return (connection != null) ? connection.getPort() : null; + } + private static void addAttributes(String routingKey, AMQP.BasicProperties properties) { if (!captureSegmentParameters) { return; diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index f444614bab..c25099f18d 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -17,11 +17,16 @@ import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.MessageProperties; +import java.net.InetAddress; import java.util.HashMap; @Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") public class ChannelN_Instrumentation { + AMQConnection getConnection() { + return Weaver.callOriginal(); + } + @Trace public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) { @@ -37,7 +42,7 @@ public void basicPublish(String exchange, String routingKey, boolean mandatory, headers.putAll(props.getHeaders()); } RabbitAMQPMetricUtil.processSendMessage(exchange, routingKey, headers, props, - AgentBridge.getAgent().getTracedMethod()); + AgentBridge.getAgent().getTracedMethod(), getConnection()); props = props.builder().headers(headers).build(); Weaver.callOriginal(); } @@ -51,7 +56,7 @@ public GetResponse basicGet(String queue, boolean autoAck) { if (response != null) { RabbitAMQPMetricUtil.processGetMessage(queue, response.getEnvelope().getRoutingKey(), response.getEnvelope().getExchange(), response.getProps(), - AgentBridge.getAgent().getTracedMethod()); + AgentBridge.getAgent().getTracedMethod(), getConnection()); } return response; } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/MetricNames.java b/newrelic-agent/src/main/java/com/newrelic/agent/MetricNames.java index 0a426c81b7..484ac49bd8 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/MetricNames.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/MetricNames.java @@ -320,6 +320,8 @@ public class MetricNames { public static final String MESSAGE_BROKER_CONSUME_NAMED = "MessageBroker/{0}/{1}/Consume/Named/{2}"; public static final String MESSAGE_BROKER_CONSUME_TEMP = "MessageBroker/{0}/{1}/Consume/Temp"; + public static final String MESSAGE_BROKER_INSTANCE = "MessageBroker/Instance/{0}/{1}"; + // API tracking supportability metrics that include API source. e.g. Supportability/API/Ignore/{source} // tokens public static final String SUPPORTABILITY_API_TOKEN = "Token"; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 21db2749bd..3c3569af4d 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -802,6 +802,13 @@ else if (destinationType == DestinationType.NAMED_QUEUE || destinationType == De Agent.LOG.log(Level.FINE, "Unexpected destination type when reporting external metrics for message consume."); } + + String host = consumeParameters.getHost(); + Integer port = consumeParameters.getPort(); + // Todo: send roll up metric for host and port + if (host != null && port != null) { + // Todo: add agent attribute for host + } } private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceParameters) { @@ -821,6 +828,14 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP messageProduceParameters.getDestinationType().getTypeName())); } + String host = messageProduceParameters.getHost(); + Integer port = messageProduceParameters.getPort(); + + // Todo: send roll up metric for host and port + if (host != null && port != null) { + // Todo: add agent attribute for host + } + if (messageProduceParameters.getCloudResourceId() != null) { setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageProduceParameters.getCloudResourceId()); } diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java index 69b1236aab..5a603d409a 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java @@ -21,14 +21,18 @@ public class MessageConsumeParameters implements ExternalParameters { private final String destinationName; private final InboundHeaders inboundHeaders; private final String cloudResourceId; + private final String host; + private final Integer port; protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, - InboundHeaders inboundHeaders, String cloudResourceId) { + InboundHeaders inboundHeaders, String cloudResourceId, String host, Integer port) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; this.inboundHeaders = inboundHeaders; this.cloudResourceId = cloudResourceId; + this.host = host; + this.port = port; } @@ -43,6 +47,8 @@ protected MessageConsumeParameters(MessageConsumeParameters messageConsumeParame this.destinationName = messageConsumeParameters.destinationName; this.inboundHeaders = messageConsumeParameters.inboundHeaders; this.cloudResourceId = messageConsumeParameters.cloudResourceId; + this.host = messageConsumeParameters.host; + this.port = messageConsumeParameters.port; } public String getDestinationName() { @@ -61,6 +67,14 @@ public String getCloudResourceId() { return cloudResourceId; } + public String getHost() { + return host; + } + + public Integer getPort() { + return port; + } + public String getLibrary() { return library; } @@ -72,6 +86,8 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String destinationName; private InboundHeaders inboundHeaders; private String cloudResourceId; + private String host; + private Integer port; public Builder(String library) { this.library = library; @@ -97,8 +113,18 @@ public Build cloudResourceId(String cloudResourceId) { return this; } + public Build host(String host) { + this.host = host; + return this; + } + + public Build port(Integer port) { + this.port = port; + return this; + } + public MessageConsumeParameters build() { - return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId); + return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId, host, port); } } @@ -152,6 +178,19 @@ public interface Build { * This method is optional and can be bypassed by calling build directly. */ Build cloudResourceId(String cloudResourceId); + + /** + * Set the host name for the message queue. + * This method is optional and can be bypassed by calling build directly. + */ + Build host(String host); + + /** + * Set the port for the message queue. + * This method is optional and can be bypassed by calling build directly. + */ + Build port(Integer port); + /** * Build the final {@link MessageConsumeParameters} for the API call. * diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java index 487ddb8690..6e0ec2f6da 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java @@ -20,14 +20,18 @@ public class MessageProduceParameters implements ExternalParameters { private final String destinationName; private final OutboundHeaders outboundHeaders; private final String cloudResourceId; + private final String host; + private final Integer port; protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, - OutboundHeaders outboundHeaders, String cloudResourceId) { + OutboundHeaders outboundHeaders, String cloudResourceId, String host, Integer port) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; this.outboundHeaders = outboundHeaders; this.cloudResourceId = cloudResourceId; + this.host = host; + this.port = port; } /** @@ -41,6 +45,8 @@ protected MessageProduceParameters(MessageProduceParameters messageProduceParame this.destinationName = messageProduceParameters.destinationName; this.outboundHeaders = messageProduceParameters.outboundHeaders; this.cloudResourceId = messageProduceParameters.cloudResourceId; + this.host = messageProduceParameters.host; + this.port = messageProduceParameters.port; } public String getDestinationName() { @@ -59,6 +65,14 @@ public String getCloudResourceId() { return cloudResourceId; } + public String getHost() { + return host; + } + + public Integer getPort() { + return port; + } + public String getLibrary() { return library; } @@ -70,6 +84,8 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String destinationName; private OutboundHeaders outboundHeaders; private String cloudResourceId; + private String host; + private Integer port; public Builder(String library) { this.library = library; @@ -95,8 +111,18 @@ public Build cloudResourceId(String cloudResourceId) { return this; } + public Build host(String host) { + this.host = host; + return this; + } + + public Build port(Integer port) { + this.port = port; + return this; + } + public MessageProduceParameters build() { - return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId); + return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId, host, port); } } @@ -152,6 +178,18 @@ public interface Build { */ Build cloudResourceId(String cloudResourceId); + /** + * Set the host name for the message queue. + * This method is optional and can be bypassed by calling build directly. + */ + Build host(String host); + + /** + * Set the port for the message queue. + * This method is optional and can be bypassed by calling build directly. + */ + Build port(Integer port); + /** * Build the final {@link MessageProduceParameters} for the API call. * From 7684192f1da259f6eeb45fc9eaada67566bfb961 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Fri, 3 May 2024 16:44:46 -0400 Subject: [PATCH 08/42] Report message broker host/port --- .../newrelic/agent/config/AgentConfig.java | 2 + .../agent/config/AgentConfigImpl.java | 13 +++ .../agent/config/MessageBrokerConfig.java | 8 ++ .../agent/config/MessageBrokerConfigImpl.java | 28 ++++++ .../agent/messaging/MessageMetrics.java | 86 +++++++++++++++++++ .../newrelic/agent/tracers/DefaultTracer.java | 27 ++++-- 6 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java create mode 100644 newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java create mode 100644 newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfig.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfig.java index d1e2d68fd9..e0d2eb3da7 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfig.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfig.java @@ -314,6 +314,8 @@ public interface AgentConfig extends com.newrelic.api.agent.Config, DataSenderCo DatastoreConfig getDatastoreConfig(); + MessageBrokerConfig getMessageBrokerConfig(); + ExternalTracerConfig getExternalTracerConfig(); boolean liteMode(); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java index 9120935538..cc6b9fa142 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java @@ -249,6 +249,8 @@ public class AgentConfigImpl extends BaseConfig implements AgentConfig { private final CircuitBreakerConfig circuitBreakerConfig; private final CrossProcessConfig crossProcessConfig; private final DatastoreConfig datastoreConfig; + + private final MessageBrokerConfig messageBrokerConfig; private final DistributedTracingConfig distributedTracingConfig; private final ErrorCollectorConfig errorCollectorConfig; private final ExtensionsConfig extensionsConfig; @@ -351,6 +353,7 @@ private AgentConfigImpl(Map props) { labelsConfig = new LabelsConfigImpl(getProperty(LABELS)); utilizationConfig = initUtilizationConfig(); datastoreConfig = initDatastoreConfig(); + messageBrokerConfig = initMessageBrokerConfig(); externalTracerConfig = initExternalTracerConfig(); jfrConfig = initJfrConfig(); jmxConfig = initJmxConfig(); @@ -827,6 +830,11 @@ private DatastoreConfig initDatastoreConfig() { return new DatastoreConfigImpl(props); } + private MessageBrokerConfig initMessageBrokerConfig() { + Map props = nestedProps(MessageBrokerConfigImpl.PROPERTY_NAME); + return new MessageBrokerConfigImpl(props); + } + private ExternalTracerConfig initExternalTracerConfig() { Map props = nestedProps(ExternalTracerConfigImpl.PROPERTY_NAME); return new ExternalTracerConfigImpl(props); @@ -1401,6 +1409,11 @@ public DatastoreConfig getDatastoreConfig() { return datastoreConfig; } + @Override + public MessageBrokerConfig getMessageBrokerConfig() { + return messageBrokerConfig; + } + @Override public ExternalTracerConfig getExternalTracerConfig() { return externalTracerConfig; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java new file mode 100644 index 0000000000..9ea0567a29 --- /dev/null +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java @@ -0,0 +1,8 @@ +package com.newrelic.agent.config; + +public interface MessageBrokerConfig { + /** + * @return true if message_broker_tracer.instance_reporting.enabled is enabled + */ + boolean isInstanceReportingEnabled(); +} diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java new file mode 100644 index 0000000000..0a89f67d71 --- /dev/null +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java @@ -0,0 +1,28 @@ +package com.newrelic.agent.config; + +import java.util.Map; + +public class MessageBrokerConfigImpl extends BaseConfig implements MessageBrokerConfig { + + public static final String ENABLED = "enabled"; + public static final String INSTANCE_REPORTING = "instance_reporting"; + public static final boolean INSTANCE_REPORTING_DEFAULT_ENABLED = true; + + public static final String PROPERTY_NAME = "message_broker_tracer"; + public static final String PROPERTY_ROOT = "newrelic.config." + PROPERTY_NAME + "."; + public static final String DOT = "."; + + private final boolean isInstanceEnabled; + + public MessageBrokerConfigImpl(Map props) { + super(props, PROPERTY_ROOT); + BaseConfig instanceReportConfig = new BaseConfig(nestedProps(INSTANCE_REPORTING), PROPERTY_ROOT + INSTANCE_REPORTING + DOT); + isInstanceEnabled = instanceReportConfig.getProperty(ENABLED, INSTANCE_REPORTING_DEFAULT_ENABLED); + + } + + @Override + public boolean isInstanceReportingEnabled() { + return isInstanceEnabled; + } +} diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java new file mode 100644 index 0000000000..88d1be6dac --- /dev/null +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -0,0 +1,86 @@ +package com.newrelic.agent.messaging; + +import com.newrelic.agent.config.Hostname; +import com.newrelic.agent.config.MessageBrokerConfig; +import com.newrelic.agent.service.ServiceFactory; +import com.newrelic.api.agent.TracedMethod; + +public class MessageMetrics { + public static final String METRIC_NAMESPACE = "MessageBroker"; + public static final String SLASH = "/"; + public static final String MESSAGE_BROKER_INSTANCE = METRIC_NAMESPACE + "/instance/"; + + public static final String UNKNOWN = "unknown"; + public static String HOSTNAME = Hostname.getHostname(ServiceFactory.getConfigService().getDefaultAgentConfig()); + + public static final String MESSAGE_BROKER_HOST = "message.broker.host"; + public static final String MESSAGE_BROKER_PORT = "message.broker.port"; + + public static boolean isAllParamsUnknown(String library, String host, Integer port) { + return isParamUnknown(library) && isParamUnknown(host) && isParamUnknown(port); + } + public static void collectMessageProducerMetrics(TracedMethod method, String library, String host, Integer port) { + reportInstanceIfEnabled(method, library, host, port); + } + + public static void collectMessageConsumerMetrics(TracedMethod method, String library, String host, Integer port) { + reportInstanceIfEnabled(method, library, host, port); + } + + public static void reportInstanceIfEnabled(TracedMethod method, String library, String host, Integer port) { + MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); + if (messageBrokerConfig.isInstanceReportingEnabled()) { + String metric = buildInstanceMetric(library, host, port); + method.addRollupMetricName(metric); + } + } + + public static String buildInstanceMetric(String library, String host, Integer port) { + String instance = buildInstanceIdentifier(library, host, port); + return MESSAGE_BROKER_INSTANCE + instance; + } + + public static String buildInstanceIdentifier(String library, String host, Integer port) { + String libraryName = replaceLibrary(library); + String hostname = replaceLocalhost(host); + String portName = replacePort(port); + + return libraryName + SLASH + hostname + SLASH + portName; + } + + public static String replaceLibrary(String library) { + if (isParamUnknown(library)) { + return UNKNOWN; + } + return library.toLowerCase(); + } + + public static String replaceLocalhost(String host) { + if (isParamUnknown(host)) { + return HOSTNAME; + } + + if ("localhost".equals(host) || "127.0.0.1".equals(host) || "0.0.0.0".equals(host) + || "0:0:0:0:0:0:0:1".equals(host) || "::1".equals(host) || "0:0:0:0:0:0:0:0".equals(host) + || "::".equals(host)) { + return HOSTNAME; + } + + return host; + } + + public static String replacePort(Integer port) { + if (isParamUnknown(port)) { + return UNKNOWN; + } + return String.valueOf(port); + } + + private static boolean isParamUnknown(String str) { + return str == null || str.isEmpty(); + } + + private static boolean isParamUnknown(Integer integer) { + return integer == null || integer == -1; + } +} diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 3c3569af4d..2ee014edc8 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -16,9 +16,11 @@ import com.newrelic.agent.bridge.external.ExternalMetrics; import com.newrelic.agent.config.AgentConfigImpl; import com.newrelic.agent.config.DatastoreConfig; +import com.newrelic.agent.config.MessageBrokerConfig; import com.newrelic.agent.config.TransactionTracerConfig; import com.newrelic.agent.database.DatastoreMetrics; import com.newrelic.agent.database.SqlObfuscator; +import com.newrelic.agent.messaging.MessageMetrics; import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.stats.ResponseTimeStats; import com.newrelic.agent.stats.TransactionStats; @@ -803,11 +805,17 @@ else if (destinationType == DestinationType.NAMED_QUEUE || destinationType == De "Unexpected destination type when reporting external metrics for message consume."); } + String library = consumeParameters.getLibrary(); String host = consumeParameters.getHost(); Integer port = consumeParameters.getPort(); - // Todo: send roll up metric for host and port - if (host != null && port != null) { - // Todo: add agent attribute for host + + MessageMetrics.collectMessageConsumerMetrics(this, library, host, port); + + MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); + + if (messageBrokerConfig.isInstanceReportingEnabled() && !MessageMetrics.isAllParamsUnknown(library, host, port)) { + setAgentAttribute(MessageMetrics.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); + setAgentAttribute(MessageMetrics.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); } } @@ -828,16 +836,17 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP messageProduceParameters.getDestinationType().getTypeName())); } + String library = messageProduceParameters.getLibrary(); String host = messageProduceParameters.getHost(); Integer port = messageProduceParameters.getPort(); - // Todo: send roll up metric for host and port - if (host != null && port != null) { - // Todo: add agent attribute for host - } + MessageMetrics.collectMessageProducerMetrics(this, library, host, port); + + MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - if (messageProduceParameters.getCloudResourceId() != null) { - setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageProduceParameters.getCloudResourceId()); + if (messageBrokerConfig.isInstanceReportingEnabled() && !MessageMetrics.isAllParamsUnknown(library, host, port)) { + setAgentAttribute(MessageMetrics.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); + setAgentAttribute(MessageMetrics.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); } } From 4e39b635e44494a20a52ba7c72c49316fbb248bb Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Fri, 3 May 2024 16:58:50 -0400 Subject: [PATCH 09/42] Fix compilation issue with new external message params --- .../agent/bridge/external/MessageConsumeParameters.java | 2 +- .../agent/bridge/external/MessageProduceParameters.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java index 98dfad581b..7024ae0dc6 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java @@ -26,7 +26,7 @@ public class MessageConsumeParameters extends com.newrelic.api.agent.MessageCons @Deprecated protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, InboundHeaders inboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null); + super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null, null, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java index f4ed341478..f5560508a1 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java @@ -26,7 +26,7 @@ public class MessageProduceParameters extends com.newrelic.api.agent.MessageProd @Deprecated protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, OutboundHeaders outboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null); + super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null, null, null); } } From 00e47d25e2a29da44a0c0c8ce47836379e96da53 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Fri, 3 May 2024 17:23:28 -0400 Subject: [PATCH 10/42] Improvements with verifier testing --- .../rabbitmq/client/QueueingConsumer_Instrumentation.java | 4 +--- .../com/rabbitmq/client/impl/ChannelN_Instrumentation.java | 7 +++---- .../rabbitmq/client/QueueingConsumer_Instrumentation.java | 4 +--- .../com/rabbitmq/client/impl/ChannelN_Instrumentation.java | 6 ++---- .../com/rabbitmq/client/impl/ChannelN_Instrumentation.java | 6 ++---- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java index aa4bec3414..084e225ba4 100644 --- a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java @@ -18,9 +18,7 @@ @Weave(type = MatchType.BaseClass, originalName = "com.rabbitmq.client.QueueingConsumer") public abstract class QueueingConsumer_Instrumentation { - public Channel getChannel() { - return Weaver.callOriginal(); - } + public abstract Channel getChannel(); @Weave(originalName = "com.rabbitmq.client.QueueingConsumer$Delivery") public static class Delivery_Instrumentation { diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index 917b7e080a..4a4449f19f 100644 --- a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -16,15 +16,14 @@ import com.rabbitmq.client.AMQP.BasicProperties; import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.MessageProperties; +import com.rabbitmq.client.Connection; import java.util.HashMap; @Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") -public class ChannelN_Instrumentation { +public abstract class ChannelN_Instrumentation { - AMQConnection getConnection() { - return Weaver.callOriginal(); - } + public abstract Connection getConnection(); @Trace public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java index a119fe2fe0..0eb51f6ce3 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java @@ -18,9 +18,7 @@ @Weave(type = MatchType.BaseClass, originalName = "com.rabbitmq.client.QueueingConsumer") public abstract class QueueingConsumer_Instrumentation { - public Channel getChannel() { - return Weaver.callOriginal(); - } + public abstract Channel getChannel(); @Weave(originalName = "com.rabbitmq.client.QueueingConsumer$Delivery") public static class Delivery_Instrumentation { diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index 584ba28295..8cfaf1f737 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -20,11 +20,9 @@ import java.util.HashMap; @Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") -public class ChannelN_Instrumentation { +public abstract class ChannelN_Instrumentation { - AMQConnection getConnection() { - return Weaver.callOriginal(); - } + public abstract AMQConnection getConnection(); @Trace public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index c25099f18d..abfb18b949 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -21,11 +21,9 @@ import java.util.HashMap; @Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") -public class ChannelN_Instrumentation { +public abstract class ChannelN_Instrumentation { - AMQConnection getConnection() { - return Weaver.callOriginal(); - } + public abstract AMQConnection getConnection(); @Trace public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, From 4c38cef996679da486d3d7226441ff2a7ca9af21 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 6 May 2024 13:06:28 -0400 Subject: [PATCH 11/42] Refactor rabbitmq modules so verifier & tests pass --- .../build.gradle | 4 +- .../java/RabbitMQTest_Integration.java | 0 .../rabbitamqp27/InboundWrapper.java | 0 .../rabbitamqp27/OutboundWrapper.java | 0 .../rabbitamqp27/RabbitAMQPMetricUtil.java | 0 .../client/Consumer_Instrumentation.java | 0 .../FileProperties_Instrumentation.java | 0 .../QueueingConsumer_Instrumentation.java | 0 .../client/impl/ChannelN_Instrumentation.java | 0 .../rabbitamqp27/RabbitMQTest.java | 0 .../rabbit-amqp-2.4.1/build.gradle | 19 + .../java/RabbitMQTest_Integration.java | 219 +++++++++++ .../rabbitamqp241}/InboundWrapper.java | 2 +- .../rabbitamqp241}/OutboundWrapper.java | 2 +- .../rabbitamqp241/RabbitAMQPMetricUtil.java | 123 +++++++ .../client/Consumer_Instrumentation.java | 6 +- .../FileProperties_Instrumentation.java | 17 + .../QueueingConsumer_Instrumentation.java | 2 +- .../client/impl/ChannelN_Instrumentation.java | 67 ++++ .../rabbitamqp241/RabbitMQTest.java | 341 +++++++++++++++++ .../rabbit-amqp-2.5.0/build.gradle | 20 + .../rabbitamqp250/InboundWrapper.java | 48 +++ .../rabbitamqp250/OutboundWrapper.java | 32 ++ .../rabbitamqp250}/RabbitAMQPMetricUtil.java | 2 +- .../client/Consumer_Instrumentation.java | 33 ++ .../QueueingConsumer_Instrumentation.java | 50 +++ .../client/impl/ChannelN_Instrumentation.java | 67 ++++ .../rabbitamqp250}/RabbitMQTest.java | 2 +- .../build.gradle | 4 +- .../rabbitamqp270/InboundWrapper.java | 48 +++ .../rabbitamqp270/OutboundWrapper.java | 32 ++ .../rabbitamqp270/RabbitAMQPMetricUtil.java | 121 +++++++ .../client/Consumer_Instrumentation.java | 33 ++ .../QueueingConsumer_Instrumentation.java | 50 +++ .../client/impl/ChannelN_Instrumentation.java | 2 +- .../rabbitamqp270/RabbitMQTest.java | 342 ++++++++++++++++++ settings.gradle | 6 +- 37 files changed, 1679 insertions(+), 15 deletions(-) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/build.gradle (80%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/integration-test/java/RabbitMQTest_Integration.java (100%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/InboundWrapper.java (100%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/OutboundWrapper.java (100%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java (100%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java (100%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java (100%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java (100%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java (100%) rename instrumentation/{rabbit-amqp-2.7 => rabbit-amqp-1.7.2}/src/test/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitMQTest.java (100%) create mode 100644 instrumentation/rabbit-amqp-2.4.1/build.gradle create mode 100644 instrumentation/rabbit-amqp-2.4.1/src/integration-test/java/RabbitMQTest_Integration.java rename instrumentation/{rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350 => rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241}/InboundWrapper.java (95%) rename instrumentation/{rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350 => rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241}/OutboundWrapper.java (92%) create mode 100644 instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java rename instrumentation/{rabbit-amqp-3.5.0 => rabbit-amqp-2.4.1}/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java (90%) create mode 100644 instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java rename instrumentation/{rabbit-amqp-3.5.0 => rabbit-amqp-2.4.1}/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java (96%) create mode 100644 instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java create mode 100644 instrumentation/rabbit-amqp-2.4.1/src/test/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitMQTest.java create mode 100644 instrumentation/rabbit-amqp-2.5.0/build.gradle create mode 100644 instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/InboundWrapper.java create mode 100644 instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/OutboundWrapper.java rename instrumentation/{rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350 => rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250}/RabbitAMQPMetricUtil.java (98%) create mode 100644 instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java create mode 100644 instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java create mode 100644 instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java rename instrumentation/{rabbit-amqp-3.5.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp350 => rabbit-amqp-2.5.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp250}/RabbitMQTest.java (99%) rename instrumentation/{rabbit-amqp-3.5.0 => rabbit-amqp-2.7.0}/build.gradle (80%) create mode 100644 instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/InboundWrapper.java create mode 100644 instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/OutboundWrapper.java create mode 100644 instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java create mode 100644 instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java create mode 100644 instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java rename instrumentation/{rabbit-amqp-3.5.0 => rabbit-amqp-2.7.0}/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java (97%) create mode 100644 instrumentation/rabbit-amqp-2.7.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitMQTest.java diff --git a/instrumentation/rabbit-amqp-2.7/build.gradle b/instrumentation/rabbit-amqp-1.7.2/build.gradle similarity index 80% rename from instrumentation/rabbit-amqp-2.7/build.gradle rename to instrumentation/rabbit-amqp-1.7.2/build.gradle index 8e557eb4bd..45d82aee7d 100644 --- a/instrumentation/rabbit-amqp-2.7/build.gradle +++ b/instrumentation/rabbit-amqp-1.7.2/build.gradle @@ -6,11 +6,11 @@ dependencies { } jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.rabbit-amqp-2.7' } + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.rabbit-amqp-1.7.2' } } verifyInstrumentation { - passesOnly 'com.rabbitmq:amqp-client:[1.7.2,2.5.0)' + passesOnly 'com.rabbitmq:amqp-client:[1.7.2,2.4.1)' } site { diff --git a/instrumentation/rabbit-amqp-2.7/src/integration-test/java/RabbitMQTest_Integration.java b/instrumentation/rabbit-amqp-1.7.2/src/integration-test/java/RabbitMQTest_Integration.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/integration-test/java/RabbitMQTest_Integration.java rename to instrumentation/rabbit-amqp-1.7.2/src/integration-test/java/RabbitMQTest_Integration.java diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/InboundWrapper.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/InboundWrapper.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/InboundWrapper.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/InboundWrapper.java diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/OutboundWrapper.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/OutboundWrapper.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/OutboundWrapper.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/OutboundWrapper.java diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java diff --git a/instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java diff --git a/instrumentation/rabbit-amqp-2.7/src/test/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitMQTest.java b/instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitMQTest.java similarity index 100% rename from instrumentation/rabbit-amqp-2.7/src/test/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitMQTest.java rename to instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitMQTest.java diff --git a/instrumentation/rabbit-amqp-2.4.1/build.gradle b/instrumentation/rabbit-amqp-2.4.1/build.gradle new file mode 100644 index 0000000000..0320c9b086 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.4.1/build.gradle @@ -0,0 +1,19 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("com.rabbitmq:amqp-client:2.4.1") + testImplementation("io.arivera.oss:embedded-rabbitmq:1.4.0") + testImplementation("org.slf4j:slf4j-simple:1.7.30") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.rabbit-amqp-2.4.1' } +} + +verifyInstrumentation { + passesOnly 'com.rabbitmq:amqp-client:[2.4.1,2.5.0)' +} + +site { + title 'RabbitAMQP' + type 'Messaging' +} \ No newline at end of file diff --git a/instrumentation/rabbit-amqp-2.4.1/src/integration-test/java/RabbitMQTest_Integration.java b/instrumentation/rabbit-amqp-2.4.1/src/integration-test/java/RabbitMQTest_Integration.java new file mode 100644 index 0000000000..388048856b --- /dev/null +++ b/instrumentation/rabbit-amqp-2.4.1/src/integration-test/java/RabbitMQTest_Integration.java @@ -0,0 +1,219 @@ +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TraceSegment; +import com.newrelic.agent.introspec.TracedMetricData; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.TransactionTrace; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; +import com.rabbitmq.client.GetResponse; +import com.rabbitmq.client.QueueingConsumer; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.UUID; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@Ignore +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "com.rabbitmq.client", "com.rabbitmq.client.impl"}) +public class RabbitMQTest_Integration { + private Channel channel; + private String DEFAULT_EXCHANGE = ""; + + @Before + public void setUp() throws IOException { + ConnectionFactory factory = new ConnectionFactory(); + Connection connection = factory.newConnection("localhost"); + channel = connection.createChannel(); + } + + @After + public void tearDown() throws IOException { + channel.getConnection().close(); + } + + @Test + public void testProduceConsume() throws IOException { + final String queueName = UUID.randomUUID().toString(); + putAndGetInTransaction(queueName); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + String expectedTransactionName = String.format("OtherTransaction/Message/RabbitMQ/Queue/Named/%s", queueName); + final Collection transactionNames = introspector.getTransactionNames(); + assertTrue(transactionNames.contains(expectedTransactionName)); + + Map metrics = introspector.getMetricsForTransaction(expectedTransactionName); + assertTrue(metrics.containsKey(String.format("MessageBroker/RabbitMQ/Queue/Consume/Named/%s", queueName))); + assertTrue(metrics.containsKey(String.format("MessageBroker/RabbitMQ/Queue/Produce/Named/%s", queueName))); + } + + @Test + public void testMessageListener() throws IOException, InterruptedException { + final String queueName = UUID.randomUUID().toString(); + final String messageForListener = "Hello message listener!"; + + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + channel.basicPublish(DEFAULT_EXCHANGE, queueName, new AMQP.BasicProperties(), messageForListener.getBytes()); + channel.basicConsume(queueName, new DefaultConsumer(channel) { + @Override + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, + byte[] body) throws IOException { + assertEquals(messageForListener, new String(body)); + } + }); + + // Let handleDelivery Transaction to finish. + Thread.sleep(1000); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + String expectedTransactionName = String.format("OtherTransaction/Message/RabbitMQ/Queue/Named/%s", queueName); + final Collection transactionNames = introspector.getTransactionNames(); + assertTrue(transactionNames.contains(expectedTransactionName)); + + Map metrics = introspector.getMetricsForTransaction(expectedTransactionName); + assertTrue(metrics.containsKey(String.format("MessageBroker/RabbitMQ/Queue/Consume/Named/%s", queueName))); + } + + @Test + public void testCat() throws IOException, InterruptedException { + final String queueName = UUID.randomUUID().toString(); + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + final String replyMessage = "reply"; + + channel.basicConsume(queueName, new DefaultConsumer(channel) { + @Override + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, + byte[] body) throws IOException { + channel.basicPublish(DEFAULT_EXCHANGE, properties.getReplyTo(), new AMQP.BasicProperties(), + replyMessage.getBytes()); + } + }); + + Thread thread = new Thread(new Runnable() { + @Override + @Trace(dispatcher = true) + public void run() { + NewRelic.setTransactionName("Category", "Sender"); + + try { + String tempQueue = channel.queueDeclare().getQueue(); + AMQP.BasicProperties properties = new AMQP.BasicProperties(); + properties.setReplyTo(tempQueue); + channel.basicPublish(DEFAULT_EXCHANGE, queueName, properties, "message".getBytes()); + + QueueingConsumer queueingConsumer = new QueueingConsumer(channel); + channel.basicConsume(tempQueue, true, queueingConsumer); + + // block + QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery(); + assertEquals(replyMessage, new String(delivery.getBody())); + + } catch (IOException e) { + } catch (InterruptedException e) { + } + } + }); + + thread.start(); + thread.join(2000); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + String senderTransactioName = "OtherTransaction/Category/Sender"; + String messageListenerTransactionName = String.format("OtherTransaction/Message/RabbitMQ/Queue/Named/%s", + queueName); + + final Collection transactionNames = introspector.getTransactionNames(); + assertTrue(transactionNames.contains(senderTransactioName)); + assertTrue(transactionNames.contains(messageListenerTransactionName)); + + Map senderMetrics = introspector.getMetricsForTransaction(senderTransactioName); + Map messageListenerMetrics = introspector.getMetricsForTransaction( + messageListenerTransactionName); + + assertTrue(senderMetrics.containsKey(String.format("MessageBroker/RabbitMQ/Queue/Produce/Named/%s", + queueName))); + assertTrue(senderMetrics.containsKey("MessageBroker/RabbitMQ/Queue/Consume/Temp")); + + assertTrue(messageListenerMetrics.containsKey(String.format("MessageBroker/RabbitMQ/Queue/Consume/Named/%s", + queueName))); + assertTrue(messageListenerMetrics.containsKey("MessageBroker/RabbitMQ/Queue/Produce/Temp")); + + // Ideally, the block below could be replaced with the following line: + // CatHelper.verifyOneSuccessfulCat(introspector, senderTransactioName, messageListenerTransactionName); + { + TransactionTrace senderTT = introspector.getTransactionTracesForTransaction( + senderTransactioName).iterator().next(); + TransactionTrace messageListenerTT = introspector.getTransactionTracesForTransaction( + messageListenerTransactionName).iterator().next(); + + Map senderTTIntrinsics = senderTT.getIntrinsicAttributes(); + Map messageListenerTTIntrinsics = messageListenerTT.getIntrinsicAttributes(); + + assertNotNull(senderTTIntrinsics.get("trip_id")); + assertNotNull(senderTTIntrinsics.get("path_hash")); + assertNotNull(getAttribute(senderTT, "transaction_guid")); + assertNotNull(messageListenerTTIntrinsics.get("referring_transaction_guid")); + assertNotNull(messageListenerTTIntrinsics.get("client_cross_process_id")); + + TransactionEvent senderEvent = introspector.getTransactionEvents(senderTransactioName).iterator().next(); + TransactionEvent messageListenerEvent = introspector.getTransactionEvents(messageListenerTransactionName).iterator().next(); + + assertEquals(senderEvent.getMyGuid(), messageListenerEvent.getReferrerGuid()); + } + + } + + private String getAttribute(TransactionTrace senderTT, String attributeName) { + Queue queue = new LinkedList(); + queue.offer(senderTT.getInitialTraceSegment()); + + while (!queue.isEmpty()) { + TraceSegment segment = queue.poll(); + if (segment.getTracerAttributes().containsKey(attributeName)) { + return (String) segment.getTracerAttributes().get(attributeName); + } + + for (TraceSegment childSegment : segment.getChildren()) { + queue.offer(childSegment); + } + } + + return null; + } + + + @Trace(dispatcher = true) + public void putAndGetInTransaction(String queueName) throws IOException { + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + + AMQP.BasicProperties properties = new AMQP.BasicProperties(); + channel.basicPublish(DEFAULT_EXCHANGE, queueName, properties, "message".getBytes()); + + GetResponse response = channel.basicGet(queueName, true); + assertEquals("message", new String(response.getBody())); + } + +} \ No newline at end of file diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/InboundWrapper.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/InboundWrapper.java similarity index 95% rename from instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/InboundWrapper.java rename to instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/InboundWrapper.java index 78ee3a2797..fea4da8cb1 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/InboundWrapper.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/InboundWrapper.java @@ -5,7 +5,7 @@ * */ -package com.nr.agent.instrumentation.rabbitamqp350; +package com.nr.agent.instrumentation.rabbitamqp241; import java.util.Collections; import java.util.List; diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/OutboundWrapper.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/OutboundWrapper.java similarity index 92% rename from instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/OutboundWrapper.java rename to instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/OutboundWrapper.java index e684cbc4c7..6e23a04f42 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/OutboundWrapper.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/OutboundWrapper.java @@ -5,7 +5,7 @@ * */ -package com.nr.agent.instrumentation.rabbitamqp350; +package com.nr.agent.instrumentation.rabbitamqp241; import java.util.Map; diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java new file mode 100644 index 0000000000..4c3168cee3 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -0,0 +1,123 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.rabbitamqp241; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.TracedMethod; +import com.newrelic.agent.bridge.TransactionNamePriority; +import com.newrelic.api.agent.DestinationType; +import com.newrelic.api.agent.MessageConsumeParameters; +import com.newrelic.api.agent.MessageProduceParameters; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Connection; + +import java.net.InetAddress; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +public abstract class RabbitAMQPMetricUtil { + private static final String RABBITMQ = "RabbitMQ"; + + private static final String MESSAGE_BROKER_TRANSACTION_EXCHANGE_NAMED = "RabbitMQ/Exchange/Named/{0}"; + + private static final String MESSAGE = "Message"; + private static final String DEFAULT = "Default"; + + private static final boolean captureSegmentParameters = AgentBridge.getAgent() + .getConfig() + .getValue("message_tracer.segment_parameters.enabled", Boolean.TRUE); + + public static void nameTransaction(String exchangeName) { + String transactionName = MessageFormat.format(MESSAGE_BROKER_TRANSACTION_EXCHANGE_NAMED, + exchangeName.isEmpty() ? DEFAULT : exchangeName); + AgentBridge.getAgent() + .getTransaction() + .setTransactionName(TransactionNamePriority.FRAMEWORK, false, MESSAGE, transactionName); + } + + public static void processSendMessage(String exchangeName, String routingKey, + HashMap headers, + AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { + tracedMethod.reportAsExternal(MessageProduceParameters + .library(RABBITMQ) + .destinationType(DestinationType.EXCHANGE) + .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .outboundHeaders(new OutboundWrapper(headers)) + .host(getHost(connection)) + .port(getPort(connection)) + .build()); + + addAttributes(routingKey, props); + } + + public static void processGetMessage(String queueName, String routingKey, String exchangeName, + AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { + tracedMethod.reportAsExternal(MessageConsumeParameters + .library(RABBITMQ) + .destinationType(DestinationType.EXCHANGE) + .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .host(getHost(connection)) + .port(getPort(connection)) + .build()); + + addConsumeAttributes(queueName, routingKey, properties); + } + + public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { + if (queueName != null && captureSegmentParameters) { + AgentBridge.privateApi.addTracerParameter("message.queueName", queueName); + } + addAttributes(routingKey, properties); + } + + public static void queuePurge(String queue, TracedMethod tracedMethod) { + tracedMethod.setMetricName(MessageFormat.format("MessageBroker/{0}/Queue/Purge/Named/{1}", + RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); + } + + private static String getHost(Connection connection) { + String host = null; + if (connection != null) { + InetAddress address = connection.getAddress(); + if (address != null) { + host = address.getHostName(); + } + } + return host; + } + + private static Integer getPort(Connection connection) { + return (connection != null) ? connection.getPort() : null; + } + + private static void addAttributes(String routingKey, AMQP.BasicProperties properties) { + if (!captureSegmentParameters) { + return; + } + + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + if (properties.getReplyTo() != null) { + AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); + } + if (properties.getCorrelationId() != null) { + AgentBridge.privateApi.addTracerParameter("message.correlationId", properties.getCorrelationId()); + } + if (properties.getHeaders() != null) { + for (Map.Entry entry : properties.getHeaders().entrySet()) { + if (entry.getKey().equals("NewRelicTransaction") || entry.getKey().equals("NewRelicID")) { + continue; + } + + AgentBridge.privateApi.addTracerParameter("message.headers." + entry.getKey(), entry.toString()); + } + } + } + +} diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java similarity index 90% rename from instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java rename to instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java index 44587dcb13..f994cea4a4 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -9,14 +9,14 @@ import java.io.IOException; -import com.newrelic.api.agent.TransportType; -import com.nr.agent.instrumentation.rabbitamqp350.InboundWrapper; -import com.nr.agent.instrumentation.rabbitamqp350.RabbitAMQPMetricUtil; import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.TransportType; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.rabbitamqp241.InboundWrapper; +import com.nr.agent.instrumentation.rabbitamqp241.RabbitAMQPMetricUtil; @Weave(type = MatchType.Interface, originalName = "com.rabbitmq.client.Consumer") public abstract class Consumer_Instrumentation { diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java new file mode 100644 index 0000000000..213cf2d111 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/FileProperties_Instrumentation.java @@ -0,0 +1,17 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.rabbitmq.client; + +import com.newrelic.api.agent.weaver.Weave; + +/** + * This class is weaved only to foce this module to fail in rabbitmq 2.5.0 and above. + */ +@Weave(originalName = "com.rabbitmq.client.FileProperties") +public class FileProperties_Instrumentation { +} diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java similarity index 96% rename from instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java rename to instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java index 0eb51f6ce3..fb7e38bb11 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java @@ -7,7 +7,7 @@ package com.rabbitmq.client; -import com.nr.agent.instrumentation.rabbitamqp350.RabbitAMQPMetricUtil; +import com.nr.agent.instrumentation.rabbitamqp241.RabbitAMQPMetricUtil; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java new file mode 100644 index 0000000000..2be940ab63 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -0,0 +1,67 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.rabbitmq.client.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.rabbitamqp241.RabbitAMQPMetricUtil; +import com.rabbitmq.client.AMQP.BasicProperties; +import com.rabbitmq.client.GetResponse; +import com.rabbitmq.client.MessageProperties; +import com.rabbitmq.client.Connection; + +import java.util.HashMap; + +@Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") +public abstract class ChannelN_Instrumentation { + + public abstract Connection getConnection(); + + @Trace + public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, + BasicProperties props, byte[] body) { + + if (props == null) { + props = MessageProperties.MINIMAL_BASIC; + } + + // Property headers is an Unmodifiable map. + // Create new map to hold new outbound and existing headers. + HashMap headers = new HashMap<>(); + if (props.getHeaders() != null) { + headers.putAll(props.getHeaders()); + } + RabbitAMQPMetricUtil.processSendMessage(exchange, routingKey, headers, props, + AgentBridge.getAgent().getTracedMethod(), getConnection()); + props.setHeaders(headers); + Weaver.callOriginal(); + } + + /* + * basicGet retrieves messages individually. + */ + @Trace + public GetResponse basicGet(String queue, boolean autoAck) { + GetResponse response = Weaver.callOriginal(); + if (response != null) { + RabbitAMQPMetricUtil.processGetMessage(queue, response.getEnvelope().getRoutingKey(), + response.getEnvelope().getExchange(), response.getProps(), + AgentBridge.getAgent().getTracedMethod(), getConnection()); + } + return response; + } + + @Trace + public AMQImpl.Queue.PurgeOk queuePurge(String queue) { + RabbitAMQPMetricUtil.queuePurge(queue, AgentBridge.getAgent().getTracedMethod()); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/rabbit-amqp-2.4.1/src/test/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitMQTest.java b/instrumentation/rabbit-amqp-2.4.1/src/test/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitMQTest.java new file mode 100644 index 0000000000..9ea884d4c4 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.4.1/src/test/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitMQTest.java @@ -0,0 +1,341 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.rabbitamqp241; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TraceSegment; +import com.newrelic.agent.introspec.TracedMetricData; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.TransactionTrace; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.TransactionNamePriority; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Address; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; +import com.rabbitmq.client.GetResponse; +import com.rabbitmq.client.QueueingConsumer; +import io.arivera.oss.embedded.rabbitmq.EmbeddedRabbitMq; +import io.arivera.oss.embedded.rabbitmq.EmbeddedRabbitMqConfig; +import io.arivera.oss.embedded.rabbitmq.PredefinedVersion; +import io.arivera.oss.embedded.rabbitmq.util.RandomPortSupplier; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "com.rabbitmq.client", "com.rabbitmq.client.impl" }) +public class RabbitMQTest { + private static final String DEFAULT_EXCHANGE = ""; + + @ClassRule + public static TemporaryFolder folder = new TemporaryFolder(); + + private Channel channel; + + private static int port; + + private static EmbeddedRabbitMq rabbitMq; + + @BeforeClass + public static void beforeClass() throws IOException { + port = new RandomPortSupplier().get(); + // Server + EmbeddedRabbitMqConfig config = new EmbeddedRabbitMqConfig.Builder() + .version(PredefinedVersion.V3_6_9) + .downloadFolder(folder.newFolder("download")) + .extractionFolder(folder.newFolder("extraction")) + .rabbitMqServerInitializationTimeoutInMillis(60 * 1000) + .defaultRabbitMqCtlTimeoutInMillis(60 * 1000) + .envVar("RABBITMQ_NODENAME", "RabbitMQ" + port) + .erlangCheckTimeoutInMillis(5000) + .port(port) + .build(); + + rabbitMq = new EmbeddedRabbitMq(config); + rabbitMq.start(); + } + + @AfterClass + public static void afterClass() { + rabbitMq.stop(); + } + + @Before + public void setUp() throws IOException { + ConnectionFactory factory = new ConnectionFactory(); + Connection connection = factory.newConnection(new Address[] { new Address("localhost", port) }); + channel = connection.createChannel(); + } + + @After + public void tearDown() throws IOException { + channel.getConnection().close(); + } + + @Test + public void testProduceConsumePurge() throws IOException { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + Map headers = new HashMap<>(); + headers.put("keyOne", 1); + headers.put("keyTwo", 2); + + String queueOne = UUID.randomUUID().toString(); + putGetAndPurge(DEFAULT_EXCHANGE, "direct", queueOne, null, null, null); + String queueOneTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueOne); + assertTrue(introspector.getTransactionNames().contains(queueOneTxn)); + assertProduceConsumePurgeMetrics("Default", queueOne, introspector.getMetricsForTransaction(queueOneTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueOneTxn).iterator().next(), + "Default", null, null, Collections.emptyMap()); + + String queueTwo = UUID.randomUUID().toString(); + putGetAndPurge("MyExchange", "direct", queueTwo, "replyTo", "correlation-id", headers); + String queueTwoTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueTwo); + assertTrue(introspector.getTransactionNames().contains(queueTwoTxn)); + assertProduceConsumePurgeMetrics("MyExchange", queueTwo, introspector.getMetricsForTransaction(queueTwoTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueTwoTxn).iterator().next(), + "MyExchange", "replyTo", "correlation-id", Collections.emptyMap()); + + String queueThree = UUID.randomUUID().toString(); + putGetAndPurge("direct", "direct", queueThree, null, null, null); + String queueThreeTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueThree); + assertTrue(introspector.getTransactionNames().contains(queueThreeTxn)); + assertProduceConsumePurgeMetrics("direct", queueThree, introspector.getMetricsForTransaction(queueThreeTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueThreeTxn).iterator().next(), + "direct", null, null, Collections.emptyMap()); + + String queueFour = UUID.randomUUID().toString(); + putGetAndPurge("TopicExchange", "topic", queueFour, "replyTo", null, headers); + String queueFourTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueFour); + assertTrue(introspector.getTransactionNames().contains(queueFourTxn)); + assertProduceConsumePurgeMetrics("TopicExchange", queueFour, + introspector.getMetricsForTransaction(queueFourTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueFourTxn).iterator().next(), + "TopicExchange", "replyTo", null, Collections.emptyMap()); + + String queueFive = UUID.randomUUID().toString(); + putGetAndPurge("headers", "headers", queueFive, null, "correlation-id", headers); + String queueFiveTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueFive); + assertTrue(introspector.getTransactionNames().contains(queueFiveTxn)); + assertProduceConsumePurgeMetrics("headers", queueFive, introspector.getMetricsForTransaction(queueFiveTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueFiveTxn).iterator().next(), + "headers", null, "correlation-id", Collections.emptyMap()); + } + + @Test + public void testMessageListener() throws IOException, InterruptedException { + final String queueName = UUID.randomUUID().toString(); + final String messageForListener = "Hello message listener!"; + + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + channel.basicPublish(DEFAULT_EXCHANGE, queueName, new AMQP.BasicProperties(), messageForListener.getBytes()); + channel.basicConsume(queueName, new DefaultConsumer(channel) { + @Override + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, + byte[] body) throws IOException { + assertEquals(messageForListener, new String(body)); + } + }); + + // Let handleDelivery Transaction to finish. + Thread.sleep(1000); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + String expectedTransactionName = "OtherTransaction/Message/RabbitMQ/Exchange/Named/Default"; + final Collection transactionNames = introspector.getTransactionNames(); + assertTrue(transactionNames.contains(expectedTransactionName)); + + //Do not record consume metric, message has already been delivered + Map metrics = introspector.getMetricsForTransaction(expectedTransactionName); + assertFalse(metrics.containsKey("MessageBroker/RabbitMQ/Exchange/Consume/Named/Default")); + } + + @Test + public void testCat() throws IOException, InterruptedException { + final Map deliveryHeaders = new HashMap<>(); + final Map consumerHeaders = new HashMap<>(); + + final String queueName = UUID.randomUUID().toString(); + final String replyMessage = "reply"; + final String exchangeName = "MyFavoriteExchange"; + + channel.exchangeDeclare(exchangeName, "topic"); + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + channel.queueBind(queueName, exchangeName, queueName); + System.out.println("Queue name is " + queueName); + + channel.basicConsume(queueName, new DefaultConsumer(channel) { + @Override + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, + byte[] body) throws IOException { + channel.basicPublish(DEFAULT_EXCHANGE, properties.getReplyTo(), new AMQP.BasicProperties(), + replyMessage.getBytes()); + consumerHeaders.putAll(properties.getHeaders()); + } + }); + + Thread thread = new Thread(new Runnable() { + @Override + @Trace(dispatcher = true) + public void run() { + NewRelic.setTransactionName("Category", "Sender"); + try { + String tempQueue = channel.queueDeclare().getQueue(); + AMQP.BasicProperties basicProperties = new AMQP.BasicProperties(); + basicProperties.setReplyTo(tempQueue); + channel.basicPublish(exchangeName, queueName, basicProperties, "message".getBytes()); + + QueueingConsumer queueingConsumer = new QueueingConsumer(channel); + channel.basicConsume(tempQueue, true, queueingConsumer); + + // block + QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery(); + deliveryHeaders.putAll(delivery.getProperties().getHeaders()); + assertEquals(replyMessage, new String(delivery.getBody())); + } catch (IOException | InterruptedException ignored) { + } + } + }); + + thread.start(); + thread.join(2000); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + String senderTransactioName = "OtherTransaction/Category/Sender"; + String messageListenerTransactionName = "OtherTransaction/Message/RabbitMQ/Exchange/Named/MyFavoriteExchange"; + + Collection transactionNames = introspector.getTransactionNames(); + assertTrue(transactionNames.contains(senderTransactioName)); + assertTrue(transactionNames.contains(messageListenerTransactionName)); + + Map senderMetrics = introspector.getMetricsForTransaction(senderTransactioName); + assertTrue(senderMetrics.containsKey("MessageBroker/RabbitMQ/Exchange/Produce/Named/MyFavoriteExchange")); + assertTrue(senderMetrics.containsKey("MessageBroker/RabbitMQ/Exchange/Consume/Named/Default")); + + Map messageListenerMetrics = introspector.getMetricsForTransaction( + messageListenerTransactionName); + //Do not record consume metric in listener + assertFalse(messageListenerMetrics.containsKey("MessageBroker/RabbitMQ/Exchange/Consume/Named/MyFavoriteExchange")); + assertTrue(messageListenerMetrics.containsKey("MessageBroker/RabbitMQ/Exchange/Produce/Named/Default")); + + // Test one-way CAT. Both transactions do a publish/consume + assertTrue(consumerHeaders.containsKey("NewRelicTransaction")); + assertTrue(consumerHeaders.containsKey("NewRelicID")); + assertTrue(deliveryHeaders.containsKey("NewRelicTransaction")); + assertTrue(deliveryHeaders.containsKey("NewRelicID")); + + TransactionEvent senderEvent = introspector.getTransactionEvents(senderTransactioName).iterator().next(); + TransactionEvent messageListenerEvent = introspector.getTransactionEvents( + messageListenerTransactionName).iterator().next(); + assertEquals(senderEvent.getMyGuid(), messageListenerEvent.getReferrerGuid()); + assertEquals(senderEvent.getMyPathHash(), messageListenerEvent.getReferringPathHash()); + } + + @Trace(dispatcher = true) + public void putGetAndPurge(String exchangeName, String exchangeType, String queueName, String replyTo, + String correlationId, Map headers) + throws IOException { + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + + if (!exchangeName.equals(DEFAULT_EXCHANGE)) { + channel.exchangeDeclare(exchangeName, exchangeType); + channel.queueBind(queueName, exchangeName, queueName); + } + + AMQP.BasicProperties properties = new AMQP.BasicProperties(); + properties.setReplyTo(replyTo); + properties.setCorrelationId(correlationId); + properties.setHeaders(headers); + channel.basicPublish(exchangeName, queueName, properties, "message".getBytes()); + + GetResponse response = channel.basicGet(queueName, true); + assertEquals("message", new String(response.getBody())); + + NewRelic.getAgent() + .getTransaction() + .setTransactionName(TransactionNamePriority.CUSTOM_HIGH, true, "Test", queueName); + + channel.queuePurge(queueName); + } + + private void assertProduceConsumePurgeMetrics(String exchangeName, String queueName, + Map metrics) { + String consumeMetric = "MessageBroker/RabbitMQ/Exchange/Consume/Named/" + exchangeName; + assertTrue(metrics.containsKey(consumeMetric)); + assertEquals(1, metrics.get(consumeMetric).getCallCount()); + + String produceMetric = "MessageBroker/RabbitMQ/Exchange/Produce/Named/" + exchangeName; + assertTrue(metrics.containsKey(produceMetric)); + assertEquals(1, metrics.get(produceMetric).getCallCount()); + + String purgeMetric = "MessageBroker/RabbitMQ/Queue/Purge/Named/" + queueName; + assertTrue(metrics.containsKey(purgeMetric)); + assertEquals(1, metrics.get(purgeMetric).getCallCount()); + } + + private void assertProduceConsumeTraceAttrs(TransactionTrace trace, String exchangeName, String replyTo, + String correlationId, Map headers) { + // Collect all segments + Map segments = new HashMap<>(); + Queue queue = new LinkedList<>(); + queue.offer(trace.getInitialTraceSegment()); + while (!queue.isEmpty()) { + TraceSegment segment = queue.poll(); + segments.put(segment.getName(), segment); + queue.addAll(segment.getChildren()); + } + + TraceSegment produceSegment = segments.get("MessageBroker/RabbitMQ/Exchange/Consume/Named/" + exchangeName); + assertTrue(produceSegment.getTracerAttributes().containsKey("message.routingKey")); + assertEquals(replyTo, produceSegment.getTracerAttributes().get("message.replyTo")); + assertEquals(correlationId, produceSegment.getTracerAttributes().get("message.correlationId")); + + for (String key : headers.keySet()) { + assertNotNull(produceSegment.getTracerAttributes().get("message." + key)); + } + + TraceSegment consumeSegment = segments.get("MessageBroker/RabbitMQ/Exchange/Consume/Named/" + exchangeName); + assertTrue(consumeSegment.getTracerAttributes().containsKey("message.routingKey")); + assertTrue(consumeSegment.getTracerAttributes().containsKey("message.queueName")); + assertEquals(replyTo, consumeSegment.getTracerAttributes().get("message.replyTo")); + + for (String key : headers.keySet()) { + assertNotNull(consumeSegment.getTracerAttributes().get("message." + key)); + } + } + +} diff --git a/instrumentation/rabbit-amqp-2.5.0/build.gradle b/instrumentation/rabbit-amqp-2.5.0/build.gradle new file mode 100644 index 0000000000..98276e56e6 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.5.0/build.gradle @@ -0,0 +1,20 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("com.rabbitmq:amqp-client:2.6.0") + + testImplementation("io.arivera.oss:embedded-rabbitmq:1.4.0") + testImplementation("org.slf4j:slf4j-simple:1.7.30") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.rabbit-amqp-2.5.0' } +} + +verifyInstrumentation { + passesOnly 'com.rabbitmq:amqp-client:[2.5.0,2.7.0)' +} + +site { + title 'RabbitAMQP' + type 'Messaging' +} \ No newline at end of file diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/InboundWrapper.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/InboundWrapper.java new file mode 100644 index 0000000000..f16801e77a --- /dev/null +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/InboundWrapper.java @@ -0,0 +1,48 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.rabbitamqp250; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.ExtendedInboundHeaders; + +public class InboundWrapper extends ExtendedInboundHeaders { + private final Map delegate; + + public InboundWrapper(Map arguments) { + super(); + this.delegate = arguments; + } + + @Override + public String getHeader(String name) { + Object property = delegate.get(name); + if (property == null) { + return null; + } + return property.toString(); + } + + @Override + public List getHeaders(String name) { + String result = getHeader(name); + if (result == null) { + return null; + } + return Collections.singletonList(result); + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.MESSAGE; + } + +} diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/OutboundWrapper.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/OutboundWrapper.java new file mode 100644 index 0000000000..97c5b8d885 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/OutboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.rabbitamqp250; + +import java.util.Map; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.OutboundHeaders; + +public class OutboundWrapper implements OutboundHeaders { + + private final Map delegate; + + public OutboundWrapper(Map headers) { + this.delegate = headers; + } + + @Override + public void setHeader(String name, String value) { + delegate.put(name, value); + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.MESSAGE; + } +} diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java similarity index 98% rename from instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitAMQPMetricUtil.java rename to instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index 108b2bab38..7130e096f5 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -5,7 +5,7 @@ * */ -package com.nr.agent.instrumentation.rabbitamqp350; +package com.nr.agent.instrumentation.rabbitamqp250; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TracedMethod; diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java new file mode 100644 index 0000000000..7d92591f52 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -0,0 +1,33 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.rabbitmq.client; + +import java.io.IOException; + +import com.newrelic.api.agent.TransportType; +import com.nr.agent.instrumentation.rabbitamqp250.InboundWrapper; +import com.nr.agent.instrumentation.rabbitamqp250.RabbitAMQPMetricUtil; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(type = MatchType.Interface, originalName = "com.rabbitmq.client.Consumer") +public abstract class Consumer_Instrumentation { + + @Trace(dispatcher = true) + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) + throws IOException { + RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); + AgentBridge.getAgent().getTransaction().provideHeaders(new InboundWrapper(properties.getHeaders())); + AgentBridge.getAgent().getTransaction(false).setTransportType(TransportType.AMQP); + RabbitAMQPMetricUtil.addConsumeAttributes(null, envelope.getRoutingKey(), properties); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java new file mode 100644 index 0000000000..c48c0c42f0 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java @@ -0,0 +1,50 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.rabbitmq.client; + +import com.nr.agent.instrumentation.rabbitamqp250.RabbitAMQPMetricUtil; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.rabbitmq.client.AMQP.BasicProperties; + +@Weave(type = MatchType.BaseClass, originalName = "com.rabbitmq.client.QueueingConsumer") +public abstract class QueueingConsumer_Instrumentation { + + public abstract Channel getChannel(); + + @Weave(originalName = "com.rabbitmq.client.QueueingConsumer$Delivery") + public static class Delivery_Instrumentation { + public BasicProperties getProperties() { + return Weaver.callOriginal(); + } + + public Envelope getEnvelope() { + return Weaver.callOriginal(); + } + } + + @Trace + public QueueingConsumer.Delivery nextDelivery() { + QueueingConsumer.Delivery delivery = Weaver.callOriginal(); + Envelope envelope = delivery.getEnvelope(); + BasicProperties props = delivery.getProperties(); + Connection connection = null; + Channel channel = getChannel(); + if (channel != null) { + connection = channel.getConnection(); + } + RabbitAMQPMetricUtil.processGetMessage(null, envelope.getRoutingKey(), + envelope.getExchange(), props, AgentBridge.getAgent().getTracedMethod(), connection); + RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); + return delivery; + } + +} diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java new file mode 100644 index 0000000000..f880755c96 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -0,0 +1,67 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.rabbitmq.client.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.rabbitamqp250.RabbitAMQPMetricUtil; +import com.rabbitmq.client.AMQP.BasicProperties; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.GetResponse; +import com.rabbitmq.client.MessageProperties; + +import java.util.HashMap; + +@Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") +public abstract class ChannelN_Instrumentation { + + public abstract Connection getConnection(); + + @Trace + public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, + BasicProperties props, byte[] body) { + + if (props == null) { + props = MessageProperties.MINIMAL_BASIC; + } + + // Property headers is an Unmodifiable map. + // Create new map to hold new outbound and existing headers. + HashMap headers = new HashMap<>(); + if (props.getHeaders() != null) { + headers.putAll(props.getHeaders()); + } + RabbitAMQPMetricUtil.processSendMessage(exchange, routingKey, headers, props, + AgentBridge.getAgent().getTracedMethod(), getConnection()); + props = props.builder().headers(headers).build(); + Weaver.callOriginal(); + } + + /* + * basicGet retrieves messages individually. + */ + @Trace + public GetResponse basicGet(String queue, boolean autoAck) { + GetResponse response = Weaver.callOriginal(); + if (response != null) { + RabbitAMQPMetricUtil.processGetMessage(queue, response.getEnvelope().getRoutingKey(), + response.getEnvelope().getExchange(), response.getProps(), + AgentBridge.getAgent().getTracedMethod(), getConnection()); + } + return response; + } + + @Trace + public AMQImpl.Queue.PurgeOk queuePurge(String queue) { + RabbitAMQPMetricUtil.queuePurge(queue, AgentBridge.getAgent().getTracedMethod()); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/rabbit-amqp-3.5.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitMQTest.java b/instrumentation/rabbit-amqp-2.5.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitMQTest.java similarity index 99% rename from instrumentation/rabbit-amqp-3.5.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitMQTest.java rename to instrumentation/rabbit-amqp-2.5.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitMQTest.java index b80f10fc76..52d6cb3034 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp350/RabbitMQTest.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitMQTest.java @@ -5,7 +5,7 @@ * */ -package com.nr.agent.instrumentation.rabbitamqp350; +package com.nr.agent.instrumentation.rabbitamqp250; import com.newrelic.agent.introspec.InstrumentationTestConfig; import com.newrelic.agent.introspec.InstrumentationTestRunner; diff --git a/instrumentation/rabbit-amqp-3.5.0/build.gradle b/instrumentation/rabbit-amqp-2.7.0/build.gradle similarity index 80% rename from instrumentation/rabbit-amqp-3.5.0/build.gradle rename to instrumentation/rabbit-amqp-2.7.0/build.gradle index 8a0accae4d..955778edba 100644 --- a/instrumentation/rabbit-amqp-3.5.0/build.gradle +++ b/instrumentation/rabbit-amqp-2.7.0/build.gradle @@ -7,11 +7,11 @@ dependencies { } jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.rabbit-amqp-3.5.0' } + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.rabbit-amqp-2.7.0' } } verifyInstrumentation { - passesOnly 'com.rabbitmq:amqp-client:[2.5.0,5.0.0.RC1)' + passesOnly 'com.rabbitmq:amqp-client:[2.7.0,5.0.0.RC1)' } site { diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/InboundWrapper.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/InboundWrapper.java new file mode 100644 index 0000000000..b59007fa5a --- /dev/null +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/InboundWrapper.java @@ -0,0 +1,48 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.rabbitamqp270; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.ExtendedInboundHeaders; + +public class InboundWrapper extends ExtendedInboundHeaders { + private final Map delegate; + + public InboundWrapper(Map arguments) { + super(); + this.delegate = arguments; + } + + @Override + public String getHeader(String name) { + Object property = delegate.get(name); + if (property == null) { + return null; + } + return property.toString(); + } + + @Override + public List getHeaders(String name) { + String result = getHeader(name); + if (result == null) { + return null; + } + return Collections.singletonList(result); + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.MESSAGE; + } + +} diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/OutboundWrapper.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/OutboundWrapper.java new file mode 100644 index 0000000000..ef7f073c97 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/OutboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.rabbitamqp270; + +import java.util.Map; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.OutboundHeaders; + +public class OutboundWrapper implements OutboundHeaders { + + private final Map delegate; + + public OutboundWrapper(Map headers) { + this.delegate = headers; + } + + @Override + public void setHeader(String name, String value) { + delegate.put(name, value); + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.MESSAGE; + } +} diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java new file mode 100644 index 0000000000..7aec547c5a --- /dev/null +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -0,0 +1,121 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.rabbitamqp270; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.TracedMethod; +import com.newrelic.agent.bridge.TransactionNamePriority; +import com.newrelic.api.agent.DestinationType; +import com.newrelic.api.agent.MessageConsumeParameters; +import com.newrelic.api.agent.MessageProduceParameters; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Connection; + +import java.net.InetAddress; +import java.text.MessageFormat; +import java.util.Map; + +public abstract class RabbitAMQPMetricUtil { + private static final String RABBITMQ = "RabbitMQ"; + + private static final String MESSAGE_BROKER_TRANSACTION_EXCHANGE_NAMED = "RabbitMQ/Exchange/Named/{0}"; + + private static final String MESSAGE = "Message"; + private static final String DEFAULT = "Default"; + + private static final boolean captureSegmentParameters = AgentBridge.getAgent() + .getConfig() + .getValue("message_tracer.segment_parameters.enabled", Boolean.TRUE); + + public static void nameTransaction(String exchangeName) { + String transactionName = MessageFormat.format(MESSAGE_BROKER_TRANSACTION_EXCHANGE_NAMED, + exchangeName.isEmpty() ? DEFAULT : exchangeName); + AgentBridge.getAgent() + .getTransaction() + .setTransactionName(TransactionNamePriority.FRAMEWORK, false, MESSAGE, transactionName); + } + + public static void processSendMessage(String exchangeName, String routingKey, Map headers, + AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { + tracedMethod.reportAsExternal(MessageProduceParameters + .library(RABBITMQ) + .destinationType(DestinationType.EXCHANGE) + .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .outboundHeaders(new OutboundWrapper(headers)) + .host(getHost(connection)) + .port(getPort(connection)) + .build()); + + addAttributes(routingKey, props); + } + + public static void processGetMessage(String queueName, String routingKey, String exchangeName, + AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { + tracedMethod.reportAsExternal(MessageConsumeParameters + .library(RABBITMQ) + .destinationType(DestinationType.EXCHANGE) + .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .host(getHost(connection)) + .port(getPort(connection)) + .build()); + + addConsumeAttributes(queueName, routingKey, properties); + } + + public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { + if (queueName != null && captureSegmentParameters) { + AgentBridge.privateApi.addTracerParameter("message.queueName", queueName); + } + addAttributes(routingKey, properties); + } + + public static void queuePurge(String queue, TracedMethod tracedMethod) { + tracedMethod.setMetricName(MessageFormat.format("MessageBroker/{0}/Queue/Purge/Named/{1}", + RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); + } + + private static String getHost(Connection connection) { + String host = null; + if (connection != null) { + InetAddress address = connection.getAddress(); + if (address != null) { + host = address.getHostName(); + } + } + return host; + } + + private static Integer getPort(Connection connection) { + return (connection != null) ? connection.getPort() : null; + } + + private static void addAttributes(String routingKey, AMQP.BasicProperties properties) { + if (!captureSegmentParameters) { + return; + } + + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + if (properties.getReplyTo() != null) { + AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); + } + if (properties.getCorrelationId() != null) { + AgentBridge.privateApi.addTracerParameter("message.correlationId", properties.getCorrelationId()); + } + if (properties.getHeaders() != null) { + for (Map.Entry entry : properties.getHeaders().entrySet()) { + if (entry.getKey().equals("NewRelicTransaction") || entry.getKey().equals("NewRelicID")) { + continue; + } + + AgentBridge.privateApi.addTracerParameter("message.headers." + entry.getKey(), entry.toString()); + } + } + } + +} diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java new file mode 100644 index 0000000000..3f60e36d72 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -0,0 +1,33 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.rabbitmq.client; + +import java.io.IOException; + +import com.newrelic.api.agent.TransportType; +import com.nr.agent.instrumentation.rabbitamqp270.InboundWrapper; +import com.nr.agent.instrumentation.rabbitamqp270.RabbitAMQPMetricUtil; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(type = MatchType.Interface, originalName = "com.rabbitmq.client.Consumer") +public abstract class Consumer_Instrumentation { + + @Trace(dispatcher = true) + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) + throws IOException { + RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); + AgentBridge.getAgent().getTransaction().provideHeaders(new InboundWrapper(properties.getHeaders())); + AgentBridge.getAgent().getTransaction(false).setTransportType(TransportType.AMQP); + RabbitAMQPMetricUtil.addConsumeAttributes(null, envelope.getRoutingKey(), properties); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java new file mode 100644 index 0000000000..5875e7887f --- /dev/null +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java @@ -0,0 +1,50 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.rabbitmq.client; + +import com.nr.agent.instrumentation.rabbitamqp270.RabbitAMQPMetricUtil; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.rabbitmq.client.AMQP.BasicProperties; + +@Weave(type = MatchType.BaseClass, originalName = "com.rabbitmq.client.QueueingConsumer") +public abstract class QueueingConsumer_Instrumentation { + + public abstract Channel getChannel(); + + @Weave(originalName = "com.rabbitmq.client.QueueingConsumer$Delivery") + public static class Delivery_Instrumentation { + public BasicProperties getProperties() { + return Weaver.callOriginal(); + } + + public Envelope getEnvelope() { + return Weaver.callOriginal(); + } + } + + @Trace + public QueueingConsumer.Delivery nextDelivery() { + QueueingConsumer.Delivery delivery = Weaver.callOriginal(); + Envelope envelope = delivery.getEnvelope(); + BasicProperties props = delivery.getProperties(); + Connection connection = null; + Channel channel = getChannel(); + if (channel != null) { + connection = channel.getConnection(); + } + RabbitAMQPMetricUtil.processGetMessage(null, envelope.getRoutingKey(), + envelope.getExchange(), props, AgentBridge.getAgent().getTracedMethod(), connection); + RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); + return delivery; + } + +} diff --git a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java similarity index 97% rename from instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java rename to instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index 8cfaf1f737..0a7b7530c4 100644 --- a/instrumentation/rabbit-amqp-3.5.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -12,7 +12,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.agent.instrumentation.rabbitamqp350.RabbitAMQPMetricUtil; +import com.nr.agent.instrumentation.rabbitamqp270.RabbitAMQPMetricUtil; import com.rabbitmq.client.AMQP.BasicProperties; import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.MessageProperties; diff --git a/instrumentation/rabbit-amqp-2.7.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitMQTest.java b/instrumentation/rabbit-amqp-2.7.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitMQTest.java new file mode 100644 index 0000000000..7ff4022bb9 --- /dev/null +++ b/instrumentation/rabbit-amqp-2.7.0/src/test/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitMQTest.java @@ -0,0 +1,342 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.rabbitamqp270; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TraceSegment; +import com.newrelic.agent.introspec.TracedMetricData; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.TransactionTrace; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.TransactionNamePriority; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; +import com.rabbitmq.client.GetResponse; +import com.rabbitmq.client.QueueingConsumer; +import io.arivera.oss.embedded.rabbitmq.EmbeddedRabbitMq; +import io.arivera.oss.embedded.rabbitmq.EmbeddedRabbitMqConfig; +import io.arivera.oss.embedded.rabbitmq.PredefinedVersion; +import io.arivera.oss.embedded.rabbitmq.apache.commons.lang3.SystemUtils; +import io.arivera.oss.embedded.rabbitmq.util.RandomPortSupplier; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "com.rabbitmq.client", "com.rabbitmq.client.impl" }) +public class RabbitMQTest { + private static final String DEFAULT_EXCHANGE = ""; + + @ClassRule + public static TemporaryFolder folder = new TemporaryFolder(); + + private static EmbeddedRabbitMq rabbitMq; + + private Channel channel; + + private static int port; + + @BeforeClass + public static void beforeClass() throws IOException { + port = new RandomPortSupplier().get(); + // Server + EmbeddedRabbitMqConfig config = new EmbeddedRabbitMqConfig.Builder() + .version(PredefinedVersion.V3_6_9) + .downloadFolder(folder.newFolder("download")) + .extractionFolder(folder.newFolder("extraction")) + .rabbitMqServerInitializationTimeoutInMillis(60 * 1000) + .defaultRabbitMqCtlTimeoutInMillis(60 * 1000) + .envVar("RABBITMQ_NODENAME", "RabbitMQ" + port) + .erlangCheckTimeoutInMillis(5000) + .port(port) + .build(); + + rabbitMq = new EmbeddedRabbitMq(config); + rabbitMq.start(); + } + + @AfterClass + public static void afterClass() { + rabbitMq.stop(); + } + + @Before + public void setUp() throws IOException { + ConnectionFactory factory = new ConnectionFactory(); + factory.setPort(port); + factory.setHost("localhost"); + Connection connection = factory.newConnection(); + channel = connection.createChannel(); + } + + @After + public void tearDown() throws IOException { + channel.getConnection().close(); + } + + @Test + public void testProduceConsumePurge() throws IOException { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + Map headers = new HashMap<>(); + headers.put("keyOne", 1); + headers.put("keyTwo", 2); + + String queueOne = UUID.randomUUID().toString(); + putGetAndPurge(DEFAULT_EXCHANGE, "direct", queueOne, null, null, null); + String queueOneTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueOne); + assertTrue(introspector.getTransactionNames().contains(queueOneTxn)); + assertProduceConsumePurgeMetrics("Default", queueOne, introspector.getMetricsForTransaction(queueOneTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueOneTxn).iterator().next(), + "Default", null, null, Collections.emptyMap()); + + String queueTwo = UUID.randomUUID().toString(); + putGetAndPurge("MyExchange", "direct", queueTwo, "replyTo", "correlation-id", headers); + String queueTwoTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueTwo); + assertTrue(introspector.getTransactionNames().contains(queueTwoTxn)); + assertProduceConsumePurgeMetrics("MyExchange", queueTwo, introspector.getMetricsForTransaction(queueTwoTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueTwoTxn).iterator().next(), + "MyExchange", "replyTo", "correlation-id", Collections.emptyMap()); + + String queueThree = UUID.randomUUID().toString(); + putGetAndPurge("direct", "direct", queueThree, null, null, null); + String queueThreeTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueThree); + assertTrue(introspector.getTransactionNames().contains(queueThreeTxn)); + assertProduceConsumePurgeMetrics("direct", queueThree, introspector.getMetricsForTransaction(queueThreeTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueThreeTxn).iterator().next(), + "direct", null, null, Collections.emptyMap()); + + String queueFour = UUID.randomUUID().toString(); + putGetAndPurge("TopicExchange", "topic", queueFour, "replyTo", null, headers); + String queueFourTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueFour); + assertTrue(introspector.getTransactionNames().contains(queueFourTxn)); + assertProduceConsumePurgeMetrics("TopicExchange", queueFour, + introspector.getMetricsForTransaction(queueFourTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueFourTxn).iterator().next(), + "TopicExchange", "replyTo", null, Collections.emptyMap()); + + String queueFive = UUID.randomUUID().toString(); + putGetAndPurge("headers", "headers", queueFive, null, "correlation-id", headers); + String queueFiveTxn = MessageFormat.format("OtherTransaction/Test/{0}", queueFive); + assertTrue(introspector.getTransactionNames().contains(queueFiveTxn)); + assertProduceConsumePurgeMetrics("headers", queueFive, introspector.getMetricsForTransaction(queueFiveTxn)); + assertProduceConsumeTraceAttrs(introspector.getTransactionTracesForTransaction(queueFiveTxn).iterator().next(), + "headers", null, "correlation-id", Collections.emptyMap()); + } + + @Test + public void testMessageListener() throws IOException, InterruptedException { + final String queueName = UUID.randomUUID().toString(); + final String messageForListener = "Hello message listener!"; + + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + channel.basicPublish(DEFAULT_EXCHANGE, queueName, new AMQP.BasicProperties(), messageForListener.getBytes()); + channel.basicConsume(queueName, new DefaultConsumer(channel) { + @Override + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, + byte[] body) throws IOException { + assertEquals(messageForListener, new String(body)); + } + }); + + // Let handleDelivery Transaction to finish. + Thread.sleep(1000); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + String expectedTransactionName = "OtherTransaction/Message/RabbitMQ/Exchange/Named/Default"; + final Collection transactionNames = introspector.getTransactionNames(); + assertTrue(transactionNames.contains(expectedTransactionName)); + + Map metrics = introspector.getMetricsForTransaction(expectedTransactionName); + //Do not record consume metric, message has already been delivered + assertFalse(metrics.containsKey("MessageBroker/RabbitMQ/Exchange/Consume/Named/Default")); + } + + @Test + public void testCat() throws IOException, InterruptedException { + final Map deliveryHeaders = new HashMap<>(); + final Map consumerHeaders = new HashMap<>(); + + final String queueName = UUID.randomUUID().toString(); + final String replyMessage = "reply"; + final String exchangeName = "MyFavoriteExchange"; + + channel.exchangeDeclare(exchangeName, "topic"); + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + channel.queueBind(queueName, exchangeName, queueName); + + channel.basicConsume(queueName, new DefaultConsumer(channel) { + @Override + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, + byte[] body) throws IOException { + channel.basicPublish(DEFAULT_EXCHANGE, properties.getReplyTo(), new AMQP.BasicProperties(), + replyMessage.getBytes()); + consumerHeaders.putAll(properties.getHeaders()); + } + }); + + Thread thread = new Thread(new Runnable() { + @Override + @Trace(dispatcher = true) + public void run() { + NewRelic.setTransactionName("Category", "Sender"); + try { + String tempQueue = channel.queueDeclare().getQueue(); + AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties().builder(); + builder.replyTo(tempQueue); + channel.basicPublish(exchangeName, queueName, builder.build(), "message".getBytes()); + + QueueingConsumer queueingConsumer = new QueueingConsumer(channel); + channel.basicConsume(tempQueue, true, queueingConsumer); + + // block + QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery(); + deliveryHeaders.putAll(delivery.getProperties().getHeaders()); + assertEquals(replyMessage, new String(delivery.getBody())); + } catch (IOException | InterruptedException ignored) { + } + } + }); + + thread.start(); + thread.join(2000); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + String senderTransactioName = "OtherTransaction/Category/Sender"; + String messageListenerTransactionName = "OtherTransaction/Message/RabbitMQ/Exchange/Named/MyFavoriteExchange"; + + Collection transactionNames = introspector.getTransactionNames(); + assertTrue(transactionNames.contains(senderTransactioName)); + assertTrue(transactionNames.contains(messageListenerTransactionName)); + + Map senderMetrics = introspector.getMetricsForTransaction(senderTransactioName); + assertTrue(senderMetrics.containsKey("MessageBroker/RabbitMQ/Exchange/Produce/Named/MyFavoriteExchange")); + assertTrue(senderMetrics.containsKey("MessageBroker/RabbitMQ/Exchange/Consume/Named/Default")); + + Map messageListenerMetrics = introspector.getMetricsForTransaction(messageListenerTransactionName); + //Do not record consume metric in listener + assertFalse(messageListenerMetrics.containsKey("MessageBroker/RabbitMQ/Exchange/Consume/Named/MyFavoriteExchange")); + assertTrue(messageListenerMetrics.containsKey("MessageBroker/RabbitMQ/Exchange/Produce/Named/Default")); + + // Test one-way CAT. Both transactions do a publish/consume + assertTrue(consumerHeaders.containsKey("NewRelicTransaction")); + assertTrue(consumerHeaders.containsKey("NewRelicID")); + assertTrue(deliveryHeaders.containsKey("NewRelicTransaction")); + assertTrue(deliveryHeaders.containsKey("NewRelicID")); + + TransactionEvent senderEvent = introspector.getTransactionEvents(senderTransactioName).iterator().next(); + TransactionEvent messageListenerEvent = introspector.getTransactionEvents( + messageListenerTransactionName).iterator().next(); + assertEquals(senderEvent.getMyGuid(), messageListenerEvent.getReferrerGuid()); + assertEquals(senderEvent.getMyPathHash(), messageListenerEvent.getReferringPathHash()); + } + + @Trace(dispatcher = true) + public void putGetAndPurge(String exchangeName, String exchangeType, String queueName, String replyTo, + String correlationId, Map headers) + throws IOException { + channel.queueDeclare(queueName, false, false, true, Collections.emptyMap()); + + if (!exchangeName.equals(DEFAULT_EXCHANGE)) { + channel.exchangeDeclare(exchangeName, exchangeType); + channel.queueBind(queueName, exchangeName, queueName); + } + + AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder(); + builder.replyTo(replyTo); + builder.correlationId(correlationId); + builder.headers(headers); + channel.basicPublish(exchangeName, queueName, builder.build(), "message".getBytes()); + + GetResponse response = channel.basicGet(queueName, true); + assertEquals("message", new String(response.getBody())); + + NewRelic.getAgent() + .getTransaction() + .setTransactionName(TransactionNamePriority.CUSTOM_HIGH, true, "Test", queueName); + + channel.queuePurge(queueName); + } + + private void assertProduceConsumePurgeMetrics(String exchangeName, String queueName, + Map metrics) { + String consumeMetric = "MessageBroker/RabbitMQ/Exchange/Consume/Named/" + exchangeName; + assertTrue(metrics.containsKey(consumeMetric)); + assertEquals(1, metrics.get(consumeMetric).getCallCount()); + + String produceMetric = "MessageBroker/RabbitMQ/Exchange/Produce/Named/" + exchangeName; + assertTrue(metrics.containsKey(produceMetric)); + assertEquals(1, metrics.get(produceMetric).getCallCount()); + + String purgeMetric = "MessageBroker/RabbitMQ/Queue/Purge/Named/" + queueName; + assertTrue(metrics.containsKey(purgeMetric)); + assertEquals(1, metrics.get(purgeMetric).getCallCount()); + } + + private void assertProduceConsumeTraceAttrs(TransactionTrace trace, String exchangeName, String replyTo, + String correlationId, Map headers) { + // Collect all segments + Map segments = new HashMap<>(); + Queue queue = new LinkedList<>(); + queue.offer(trace.getInitialTraceSegment()); + while (!queue.isEmpty()) { + TraceSegment segment = queue.poll(); + segments.put(segment.getName(), segment); + queue.addAll(segment.getChildren()); + } + + TraceSegment produceSegment = segments.get("MessageBroker/RabbitMQ/Exchange/Consume/Named/" + exchangeName); + assertTrue(produceSegment.getTracerAttributes().containsKey("message.routingKey")); + assertEquals(replyTo, produceSegment.getTracerAttributes().get("message.replyTo")); + assertEquals(correlationId, produceSegment.getTracerAttributes().get("message.correlationId")); + + for (String key : headers.keySet()) { + assertNotNull(produceSegment.getTracerAttributes().get("message." + key)); + } + + TraceSegment consumeSegment = segments.get("MessageBroker/RabbitMQ/Exchange/Consume/Named/" + exchangeName); + assertTrue(consumeSegment.getTracerAttributes().containsKey("message.routingKey")); + assertTrue(consumeSegment.getTracerAttributes().containsKey("message.queueName")); + assertEquals(replyTo, consumeSegment.getTracerAttributes().get("message.replyTo")); + + for (String key : headers.keySet()) { + assertNotNull(consumeSegment.getTracerAttributes().get("message." + key)); + } + } + +} diff --git a/settings.gradle b/settings.gradle index f36dca65b7..c3aecb485e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -296,8 +296,10 @@ include 'instrumentation:r2dbc-mysql' include 'instrumentation:r2dbc-postgresql-0.9.0' include 'instrumentation:r2dbc-postgresql-0.9.2' include 'instrumentation:r2dbc-mssql' -include 'instrumentation:rabbit-amqp-2.7' -include 'instrumentation:rabbit-amqp-3.5.0' +include 'instrumentation:rabbit-amqp-1.7.2' +include 'instrumentation:rabbit-amqp-2.4.1' +include 'instrumentation:rabbit-amqp-2.5.0' +include 'instrumentation:rabbit-amqp-2.7.0' include 'instrumentation:rabbit-amqp-5.0.0' include 'instrumentation:reactor-3.3.0' include 'instrumentation:resin-3' From d362c7a62dd0a569fbd114f4e0bd684ac2ed416b Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 6 May 2024 13:12:41 -0400 Subject: [PATCH 12/42] Refactor internal amqp-1.7.2 nr package --- .../{rabbitamqp27 => rabbitamqp172}/InboundWrapper.java | 2 +- .../{rabbitamqp27 => rabbitamqp172}/OutboundWrapper.java | 2 +- .../{rabbitamqp27 => rabbitamqp172}/RabbitAMQPMetricUtil.java | 3 +-- .../java/com/rabbitmq/client/Consumer_Instrumentation.java | 4 ++-- .../com/rabbitmq/client/QueueingConsumer_Instrumentation.java | 2 +- .../com/rabbitmq/client/impl/ChannelN_Instrumentation.java | 2 +- .../{rabbitamqp27 => rabbitamqp172}/RabbitMQTest.java | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) rename instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/{rabbitamqp27 => rabbitamqp172}/InboundWrapper.java (95%) rename instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/{rabbitamqp27 => rabbitamqp172}/OutboundWrapper.java (92%) rename instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/{rabbitamqp27 => rabbitamqp172}/RabbitAMQPMetricUtil.java (98%) rename instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/{rabbitamqp27 => rabbitamqp172}/RabbitMQTest.java (99%) diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/InboundWrapper.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/InboundWrapper.java similarity index 95% rename from instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/InboundWrapper.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/InboundWrapper.java index 62dceb0c34..d5642712a2 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/InboundWrapper.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/InboundWrapper.java @@ -5,7 +5,7 @@ * */ -package com.nr.agent.instrumentation.rabbitamqp27; +package com.nr.agent.instrumentation.rabbitamqp172; import java.util.Collections; import java.util.List; diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/OutboundWrapper.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/OutboundWrapper.java similarity index 92% rename from instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/OutboundWrapper.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/OutboundWrapper.java index d245136178..e7343f7730 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/OutboundWrapper.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/OutboundWrapper.java @@ -5,7 +5,7 @@ * */ -package com.nr.agent.instrumentation.rabbitamqp27; +package com.nr.agent.instrumentation.rabbitamqp172; import java.util.Map; diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java similarity index 98% rename from instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java rename to instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index 8cc57c6079..9a3d4938cf 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -5,7 +5,7 @@ * */ -package com.nr.agent.instrumentation.rabbitamqp27; +package com.nr.agent.instrumentation.rabbitamqp172; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TracedMethod; @@ -16,7 +16,6 @@ import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Connection; -import java.net.InetAddress; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java index c4d764fc76..2af56138eb 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -15,8 +15,8 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.agent.instrumentation.rabbitamqp27.InboundWrapper; -import com.nr.agent.instrumentation.rabbitamqp27.RabbitAMQPMetricUtil; +import com.nr.agent.instrumentation.rabbitamqp172.InboundWrapper; +import com.nr.agent.instrumentation.rabbitamqp172.RabbitAMQPMetricUtil; @Weave(type = MatchType.Interface, originalName = "com.rabbitmq.client.Consumer") public abstract class Consumer_Instrumentation { diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java index 084e225ba4..782adb14a6 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/QueueingConsumer_Instrumentation.java @@ -7,7 +7,7 @@ package com.rabbitmq.client; -import com.nr.agent.instrumentation.rabbitamqp27.RabbitAMQPMetricUtil; +import com.nr.agent.instrumentation.rabbitamqp172.RabbitAMQPMetricUtil; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index 4a4449f19f..533ea0921a 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -12,7 +12,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.agent.instrumentation.rabbitamqp27.RabbitAMQPMetricUtil; +import com.nr.agent.instrumentation.rabbitamqp172.RabbitAMQPMetricUtil; import com.rabbitmq.client.AMQP.BasicProperties; import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.MessageProperties; diff --git a/instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitMQTest.java b/instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitMQTest.java similarity index 99% rename from instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitMQTest.java rename to instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitMQTest.java index 6b4f437b20..67a50fc0d9 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/rabbitamqp27/RabbitMQTest.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/test/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitMQTest.java @@ -5,7 +5,7 @@ * */ -package com.nr.agent.instrumentation.rabbitamqp27; +package com.nr.agent.instrumentation.rabbitamqp172; import com.newrelic.agent.introspec.InstrumentationTestConfig; import com.newrelic.agent.introspec.InstrumentationTestRunner; From fa8d5c672c02692ff77dfdcd2ab47f3413bf8c15 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 6 May 2024 17:57:53 -0400 Subject: [PATCH 13/42] Add activemq client instrumentation to get endpoint --- .../agent/bridge/messaging/HostAndPort.java | 19 +++++ .../agent/bridge/messaging/JmsUtil.java | 27 +++++++ .../activemq-client-5.8.0/build.gradle | 18 +++++ .../activemqclient580/ActiveMQUtil.java | 35 ++++++++++ .../ActiveMQMessage_Instrumentation.java | 25 +++++++ .../activemqclient580/ActiveMQUtilTest.java | 70 +++++++++++++++++++ settings.gradle | 1 + 7 files changed, 195 insertions(+) create mode 100644 agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java create mode 100644 agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java create mode 100644 instrumentation/activemq-client-5.8.0/build.gradle create mode 100644 instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java create mode 100644 instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java create mode 100644 instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java new file mode 100644 index 0000000000..92b9423c4f --- /dev/null +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java @@ -0,0 +1,19 @@ +package com.newrelic.agent.bridge.messaging; + +public class HostAndPort { + private final String hostName; + private final Integer port; + + public HostAndPort(String host, Integer port) { + this.hostName = host; + this.port = port; + } + + public String getHostName() { + return hostName; + } + + public Integer getPort() { + return port; + } +} diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java new file mode 100644 index 0000000000..0b3b7d76e5 --- /dev/null +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java @@ -0,0 +1,27 @@ +package com.newrelic.agent.bridge.messaging; + +import com.newrelic.agent.bridge.AgentBridge; + +import java.util.Map; + +public class JmsUtil { + private static final Map hostAndPortCache = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); + + public static final String JMS_HOST_AND_PORT_PROPERTY = "com.nr.agent.instrumentation.jms.HostAndPort"; + + public static HostAndPort getHostAndPortFromCache(String address) { + Object obj = hostAndPortCache.get(address); + if (obj instanceof HostAndPort) { + return (HostAndPort) obj; + } + if (obj != null) { + hostAndPortCache.remove(address); + } + return null; + } + + public static HostAndPort cacheAndReturnHostAndPort(String address, HostAndPort hostAndPort) { + hostAndPortCache.put(address, hostAndPort); + return hostAndPort; + } +} diff --git a/instrumentation/activemq-client-5.8.0/build.gradle b/instrumentation/activemq-client-5.8.0/build.gradle new file mode 100644 index 0000000000..32be0a29cf --- /dev/null +++ b/instrumentation/activemq-client-5.8.0/build.gradle @@ -0,0 +1,18 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("org.apache.activemq:activemq-client:5.16.7") + testImplementation("org.slf4j:slf4j-simple:1.7.30") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.activemq-client-5.8.0' } +} + +verifyInstrumentation { + passesOnly 'org.apache.activemq:activemq-client:[5.8.0,)' +} + +site { + title 'ActiveMQClient' + type 'Messaging' +} \ No newline at end of file diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java new file mode 100644 index 0000000000..9ba4d27418 --- /dev/null +++ b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java @@ -0,0 +1,35 @@ +package com.nr.agent.instrumentation.activemqclient580; + +import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.agent.bridge.messaging.JmsUtil; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ActiveMQUtil { + public static final Pattern addressPattern = Pattern.compile("^\\w+://(.+)/.+:(\\d+)"); + public static HostAndPort parseHostAndPort(String address) { + HostAndPort cached = JmsUtil.getHostAndPortFromCache(address); + if (cached != null) { + return cached; + } + + Matcher m = addressPattern.matcher(address); + if(!m.find()) { + return null; + } + + String hostName = m.group(1); + int port; + + try { + String portStr = m.group(2); + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + return null; + } + + HostAndPort hostAndPort = new HostAndPort(hostName, port); + return JmsUtil.cacheAndReturnHostAndPort(address, hostAndPort); + } +} diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java new file mode 100644 index 0000000000..11eccd48c4 --- /dev/null +++ b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java @@ -0,0 +1,25 @@ +package org.apache.activemq.command; + +import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.activemqclient580.ActiveMQUtil; +import org.apache.activemq.ActiveMQConnection; + +import javax.jms.JMSException; + +import static com.newrelic.agent.bridge.messaging.JmsUtil.JMS_HOST_AND_PORT_PROPERTY; + +@Weave(type = MatchType.BaseClass, originalName = "org.apache.activemq.command.ActiveMQMessage") +public abstract class ActiveMQMessage_Instrumentation { + public abstract ActiveMQConnection getConnection(); + + // This is so the JMS instrumentation can grab host and port of Active MQ + public HostAndPort getObjectProperty(String name) throws JMSException { + if (JMS_HOST_AND_PORT_PROPERTY.equals(name)) { + return ActiveMQUtil.parseHostAndPort(getConnection().getTransport().toString()); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java new file mode 100644 index 0000000000..74be5ec89a --- /dev/null +++ b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java @@ -0,0 +1,70 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.activemqclient580; + +import com.newrelic.agent.bridge.messaging.HostAndPort; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class ActiveMQUtilTest { + + @Test + public void testAwsUrlParsing() { + final String awsAddress = "ssl://b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com/174.65.25.235:61617"; + + final String expectedHost = "b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com"; + final Integer expectedPort = 61617; + + final HostAndPort hostAndPort = ActiveMQUtil.parseHostAndPort(awsAddress); + assertHostAndPort(expectedHost, expectedPort, hostAndPort); + + // Verify caching works + final HostAndPort hostAndPortRepeated = ActiveMQUtil.parseHostAndPort(awsAddress); + assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); + + } + + @Test + public void testAwsUrlParsingWithLocalPort() { + final String awsAddress = "ssl://b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com/174.65.25.235:61617@59925"; + + final String expectedHost = "b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com"; + final Integer expectedPort = 61617; + + final HostAndPort hostAndPort = ActiveMQUtil.parseHostAndPort(awsAddress); + assertHostAndPort(expectedHost, expectedPort, hostAndPort); + + // Verify caching works + final HostAndPort hostAndPortRepeated = ActiveMQUtil.parseHostAndPort(awsAddress); + assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); + + } + + @Test + public void testLocalhostParsing() { + final String localhostAddress = "tcp://localhost/127.0.0.1:61616@59925"; + + final String expectedHost = "localhost"; + final Integer expectedPort = 61616; + + final HostAndPort hostAndPort = ActiveMQUtil.parseHostAndPort(localhostAddress); + assertHostAndPort(expectedHost, expectedPort, hostAndPort); + + // Verify caching works + final HostAndPort hostAndPortRepeated = ActiveMQUtil.parseHostAndPort(localhostAddress); + assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); + } + + private void assertHostAndPort(final String expectedHostName, final Integer expectedPort, final HostAndPort hostAndPort) { + assertNotNull(hostAndPort); + assertEquals(expectedHostName, hostAndPort.getHostName()); + assertEquals(expectedPort, hostAndPort.getPort()); + } +} diff --git a/settings.gradle b/settings.gradle index c3aecb485e..baeecddd12 100644 --- a/settings.gradle +++ b/settings.gradle @@ -66,6 +66,7 @@ if (JavaVersion.current().isJava11Compatible()) { } // Weaver Instrumentation +include 'instrumentation:activemq-client-5.8.0' include 'instrumentation:anorm-2.3' include 'instrumentation:anorm-2.4' include 'instrumentation:aws-bedrock-runtime-2.20' From bc0eb51fedaee0c2b5c9b7a1d2d4f34f0c423284 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 6 May 2024 18:19:17 -0400 Subject: [PATCH 14/42] Set host and port in jms instrumentation --- .../agent/bridge/messaging/HostAndPort.java | 4 +++ .../agent/bridge/messaging/JmsUtil.java | 2 +- .../activemqclient580/ActiveMQUtil.java | 4 +-- .../ActiveMQMessage_Instrumentation.java | 6 ++-- .../instrumentation/jms11/JmsMetricUtil.java | 34 +++++++++++++++---- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java index 92b9423c4f..5c0a2282d5 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java @@ -4,6 +4,10 @@ public class HostAndPort { private final String hostName; private final Integer port; + public static HostAndPort empty() { + return new HostAndPort(null, null); + } + public HostAndPort(String host, Integer port) { this.hostName = host; this.port = port; diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java index 0b3b7d76e5..c4abe4e1df 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java @@ -7,7 +7,7 @@ public class JmsUtil { private static final Map hostAndPortCache = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); - public static final String JMS_HOST_AND_PORT_PROPERTY = "com.nr.agent.instrumentation.jms.HostAndPort"; + public static final String NR_JMS_HOST_AND_PORT_PROPERTY = "com.nr.agent.instrumentation.jms.HostAndPort"; public static HostAndPort getHostAndPortFromCache(String address) { Object obj = hostAndPortCache.get(address); diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java index 9ba4d27418..78abc6bc91 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java @@ -16,7 +16,7 @@ public static HostAndPort parseHostAndPort(String address) { Matcher m = addressPattern.matcher(address); if(!m.find()) { - return null; + return HostAndPort.empty(); } String hostName = m.group(1); @@ -26,7 +26,7 @@ public static HostAndPort parseHostAndPort(String address) { String portStr = m.group(2); port = Integer.parseInt(portStr); } catch (NumberFormatException e) { - return null; + return HostAndPort.empty(); } HostAndPort hostAndPort = new HostAndPort(hostName, port); diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java index 11eccd48c4..54b83a64d4 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java @@ -9,15 +9,15 @@ import javax.jms.JMSException; -import static com.newrelic.agent.bridge.messaging.JmsUtil.JMS_HOST_AND_PORT_PROPERTY; +import static com.newrelic.agent.bridge.messaging.JmsUtil.NR_JMS_HOST_AND_PORT_PROPERTY; @Weave(type = MatchType.BaseClass, originalName = "org.apache.activemq.command.ActiveMQMessage") public abstract class ActiveMQMessage_Instrumentation { public abstract ActiveMQConnection getConnection(); - // This is so the JMS instrumentation can grab host and port of Active MQ + // This is so the JMS instrumentation can grab host and port of the Active MQ instance public HostAndPort getObjectProperty(String name) throws JMSException { - if (JMS_HOST_AND_PORT_PROPERTY.equals(name)) { + if (NR_JMS_HOST_AND_PORT_PROPERTY.equals(name)) { return ActiveMQUtil.parseHostAndPort(getConnection().getTransport().toString()); } return Weaver.callOriginal(); diff --git a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java index e188af2b06..c1484bfe88 100644 --- a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java +++ b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java @@ -9,6 +9,8 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TracedMethod; +import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.agent.bridge.messaging.JmsUtil; import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.MessageConsumeParameters; import com.newrelic.api.agent.MessageProduceParameters; @@ -125,12 +127,18 @@ public static void processSendMessage(Message message, Destination dest, TracedM try { DestinationType destinationType = getDestinationType(dest); String destinationName = getDestinationName(dest == null ? message.getJMSDestination() : dest); - tracer.reportAsExternal(MessageProduceParameters + MessageProduceParameters.Build builder = MessageProduceParameters .library("JMS") .destinationType(destinationType) .destinationName(destinationName) - .outboundHeaders(new OutboundWrapper(message)) - .build()); + .outboundHeaders(new OutboundWrapper(message)); + HostAndPort hostAndPort = getHostAndPort(message); + if (hostAndPort != null) { + builder = builder + .host(hostAndPort.getHostName()) + .port(hostAndPort.getPort()); + } + tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message produce."); @@ -146,18 +154,32 @@ public static void processConsume(Message message, TracedMethod tracer) { try { DestinationType destinationType = getDestinationType(message.getJMSDestination()); String destinationName = getDestinationName(message.getJMSDestination()); - tracer.reportAsExternal(MessageConsumeParameters + MessageConsumeParameters.Build builder = MessageConsumeParameters .library("JMS") .destinationType(destinationType) .destinationName(destinationName) - .inboundHeaders(new InboundWrapper(message)) - .build()); + .inboundHeaders(new InboundWrapper(message)); + HostAndPort hostAndPort = getHostAndPort(message); + if (hostAndPort != null) { + builder = builder + .host(hostAndPort.getHostName()) + .port(hostAndPort.getPort()); + } + tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message consume."); } } + private static HostAndPort getHostAndPort(Message message) throws JMSException { + Object obj = message.getObjectProperty(JmsUtil.NR_JMS_HOST_AND_PORT_PROPERTY); + if (obj instanceof HostAndPort) { + return (HostAndPort) obj; + } + return null; + } + private static String getDestinationName(Destination destination) throws JMSException { if (destination instanceof TemporaryQueue || destination instanceof TemporaryTopic) { From fda38a93b3cd1f52b6936a4d0779a8395f6a94de Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 6 May 2024 18:22:50 -0400 Subject: [PATCH 15/42] Set host and port in jms-3 instrumentation --- .../instrumentation/jms3/JmsMetricUtil.java | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java index d66e0e43ee..0c4609ee76 100644 --- a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java +++ b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java @@ -9,6 +9,8 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TracedMethod; +import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.agent.bridge.messaging.JmsUtil; import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.MessageConsumeParameters; import com.newrelic.api.agent.MessageProduceParameters; @@ -125,12 +127,18 @@ public static void processSendMessage(Message message, Destination dest, TracedM try { DestinationType destinationType = getDestinationType(dest); String destinationName = getDestinationName(dest == null ? message.getJMSDestination() : dest); - tracer.reportAsExternal(MessageProduceParameters + MessageProduceParameters.Build builder = MessageProduceParameters .library("JMS") .destinationType(destinationType) .destinationName(destinationName) - .outboundHeaders(new OutboundWrapper(message)) - .build()); + .outboundHeaders(new OutboundWrapper(message)); + HostAndPort hostAndPort = getHostAndPort(message); + if (hostAndPort != null) { + builder = builder + .host(hostAndPort.getHostName()) + .port(hostAndPort.getPort()); + } + tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message produce."); @@ -146,18 +154,32 @@ public static void processConsume(Message message, TracedMethod tracer) { try { DestinationType destinationType = getDestinationType(message.getJMSDestination()); String destinationName = getDestinationName(message.getJMSDestination()); - tracer.reportAsExternal(MessageConsumeParameters + MessageConsumeParameters.Build builder = MessageConsumeParameters .library("JMS") .destinationType(destinationType) .destinationName(destinationName) - .inboundHeaders(new InboundWrapper(message)) - .build()); + .inboundHeaders(new InboundWrapper(message)); + HostAndPort hostAndPort = getHostAndPort(message); + if (hostAndPort != null) { + builder = builder + .host(hostAndPort.getHostName()) + .port(hostAndPort.getPort()); + } + tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message consume."); } } + private static HostAndPort getHostAndPort(Message message) throws JMSException { + Object obj = message.getObjectProperty(JmsUtil.NR_JMS_HOST_AND_PORT_PROPERTY); + if (obj instanceof HostAndPort) { + return (HostAndPort) obj; + } + return null; + } + private static String getDestinationName(Destination destination) throws JMSException { if (destination instanceof TemporaryQueue || destination instanceof TemporaryTopic) { From a0c6aeb0ea19d4bfd21dd9f888665a6458780aa5 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Tue, 7 May 2024 11:54:21 -0400 Subject: [PATCH 16/42] Fix verifier issues with activemq --- .../activemq/command/ActiveMQMessage_Instrumentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java index 54b83a64d4..e17fdb869f 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java @@ -16,7 +16,7 @@ public abstract class ActiveMQMessage_Instrumentation { public abstract ActiveMQConnection getConnection(); // This is so the JMS instrumentation can grab host and port of the Active MQ instance - public HostAndPort getObjectProperty(String name) throws JMSException { + public Object getObjectProperty(String name) { if (NR_JMS_HOST_AND_PORT_PROPERTY.equals(name)) { return ActiveMQUtil.parseHostAndPort(getConnection().getTransport().toString()); } From 1231875382f189f6bbeaf56616de508bf0c1d96e Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Tue, 7 May 2024 12:52:50 -0400 Subject: [PATCH 17/42] Fix where consume params are set --- .../instrumentation/jms11/ApacheMQTest.java | 2 +- .../agent/messaging/MessageMetrics.java | 4 +-- .../newrelic/agent/tracers/DefaultTracer.java | 28 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/instrumentation/jms-1.1/src/test/java/com/nr/agent/instrumentation/jms11/ApacheMQTest.java b/instrumentation/jms-1.1/src/test/java/com/nr/agent/instrumentation/jms11/ApacheMQTest.java index 0a4876eaad..8999d6f093 100644 --- a/instrumentation/jms-1.1/src/test/java/com/nr/agent/instrumentation/jms11/ApacheMQTest.java +++ b/instrumentation/jms-1.1/src/test/java/com/nr/agent/instrumentation/jms11/ApacheMQTest.java @@ -24,7 +24,7 @@ * Run the standard JMS instrumentation test suite against the ApacheMQ JMS provider. */ @RunWith(InstrumentationTestRunner.class) -@InstrumentationTestConfig(includePrefixes = { "com.nr.agent.instrumentation.jms11" }) +@InstrumentationTestConfig(includePrefixes = { "com.nr.agent.instrumentation.jms11", "com.nr.agent.instrumentation.activemqclient580", "org.apache.activemq" }) public class ApacheMQTest implements JmsProviderTest { private static final String MESSAGE_BROKER_URL = "vm://localhost?create=false"; private static final String QUEUE_NAME = "InstrumentationTestQueue"; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index 88d1be6dac..5386d47f07 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -19,11 +19,11 @@ public class MessageMetrics { public static boolean isAllParamsUnknown(String library, String host, Integer port) { return isParamUnknown(library) && isParamUnknown(host) && isParamUnknown(port); } - public static void collectMessageProducerMetrics(TracedMethod method, String library, String host, Integer port) { + public static void collectMessageProducerRollupMetrics(TracedMethod method, String library, String host, Integer port) { reportInstanceIfEnabled(method, library, host, port); } - public static void collectMessageConsumerMetrics(TracedMethod method, String library, String host, Integer port) { + public static void collectMessageConsumerRollupMetrics(TracedMethod method, String library, String host, Integer port) { reportInstanceIfEnabled(method, library, host, port); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 2ee014edc8..07628fe860 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -804,19 +804,6 @@ else if (destinationType == DestinationType.NAMED_QUEUE || destinationType == De Agent.LOG.log(Level.FINE, "Unexpected destination type when reporting external metrics for message consume."); } - - String library = consumeParameters.getLibrary(); - String host = consumeParameters.getHost(); - Integer port = consumeParameters.getPort(); - - MessageMetrics.collectMessageConsumerMetrics(this, library, host, port); - - MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - - if (messageBrokerConfig.isInstanceReportingEnabled() && !MessageMetrics.isAllParamsUnknown(library, host, port)) { - setAgentAttribute(MessageMetrics.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); - setAgentAttribute(MessageMetrics.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); - } } private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceParameters) { @@ -840,7 +827,7 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP String host = messageProduceParameters.getHost(); Integer port = messageProduceParameters.getPort(); - MessageMetrics.collectMessageProducerMetrics(this, library, host, port); + MessageMetrics.collectMessageProducerRollupMetrics(this, library, host, port); MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); @@ -871,6 +858,19 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP if (messageConsumeParameters.getCloudResourceId() != null) { setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageConsumeParameters.getCloudResourceId()); } + + String library = messageConsumeParameters.getLibrary(); + String host = messageConsumeParameters.getHost(); + Integer port = messageConsumeParameters.getPort(); + + MessageMetrics.collectMessageConsumerRollupMetrics(this, library, host, port); + + MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); + + if (messageBrokerConfig.isInstanceReportingEnabled() && !MessageMetrics.isAllParamsUnknown(library, host, port)) { + setAgentAttribute(MessageMetrics.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); + setAgentAttribute(MessageMetrics.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); + } } private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { From f3fac5a1782404944b5c637af82174a3076af78d Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Tue, 14 May 2024 13:19:12 -0400 Subject: [PATCH 18/42] Add message broker endpoint attributes to span factory --- .../newrelic/agent/attributes/AttributeNames.java | 4 ++++ .../newrelic/agent/messaging/MessageMetrics.java | 7 ++----- .../agent/service/analytics/SpanEventFactory.java | 14 ++++++++++++++ .../com/newrelic/agent/tracers/DefaultTracer.java | 12 ++++++------ 4 files changed, 26 insertions(+), 11 deletions(-) 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 d4af6c63c4..960d7a4236 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 @@ -56,6 +56,10 @@ public final class AttributeNames { // cloud provider identifier for the resource being used public static final String CLOUD_RESOURCE_ID = "cloud.resource_id"; + // Message broker endpoint information + public static final String MESSAGE_BROKER_HOST = "message.broker.host"; + public static final String MESSAGE_BROKER_PORT = "message.broker.port"; + public static final String RESPONSE_CONTENT_TYPE_PARAMETER_NAME = "response.headers.contentType"; // high security matches diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index 5386d47f07..4573a897fa 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -13,11 +13,8 @@ public class MessageMetrics { public static final String UNKNOWN = "unknown"; public static String HOSTNAME = Hostname.getHostname(ServiceFactory.getConfigService().getDefaultAgentConfig()); - public static final String MESSAGE_BROKER_HOST = "message.broker.host"; - public static final String MESSAGE_BROKER_PORT = "message.broker.port"; - - public static boolean isAllParamsUnknown(String library, String host, Integer port) { - return isParamUnknown(library) && isParamUnknown(host) && isParamUnknown(port); + public static boolean isEndpointParamsKnown(String host, Integer port) { + return !(isParamUnknown(host) && isParamUnknown(port)); } public static void collectMessageProducerRollupMetrics(TracedMethod method, String library, String host, Integer port) { reportInstanceIfEnabled(method, library, host, port); 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 4fd60056b5..0bc343a7b8 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 @@ -288,6 +288,16 @@ public SpanEventFactory setCloudResourceId(String cloudResourceId) { return this; } + public SpanEventFactory setMessageBrokerHost(String host) { + builder.putAgentAttribute(AttributeNames.MESSAGE_BROKER_HOST, host); + return this; + } + + public SpanEventFactory setMessageBrokerPort(Integer port) { + builder.putAgentAttribute(AttributeNames.MESSAGE_BROKER_PORT, port); + return this; + } + public SpanEventFactory setServerPort(int port) { builder.putAgentAttribute("server.port", port); return this; @@ -410,10 +420,14 @@ public SpanEventFactory setExternalParameterAttributes(ExternalParameters parame MessageProduceParameters messageProduceParameters = (MessageProduceParameters) parameters; setCategory(SpanCategory.generic); setCloudResourceId(messageProduceParameters.getCloudResourceId()); + setMessageBrokerHost(messageProduceParameters.getHost()); + setMessageBrokerPort(messageProduceParameters.getPort()); } else if (parameters instanceof MessageConsumeParameters) { MessageConsumeParameters messageConsumeParameters = (MessageConsumeParameters) parameters; setCategory(SpanCategory.generic); setCloudResourceId(messageConsumeParameters.getCloudResourceId()); + setMessageBrokerHost(messageConsumeParameters.getHost()); + setMessageBrokerPort(messageConsumeParameters.getPort()); } else { setCategory(SpanCategory.generic); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 07628fe860..380bff54de 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -831,9 +831,9 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - if (messageBrokerConfig.isInstanceReportingEnabled() && !MessageMetrics.isAllParamsUnknown(library, host, port)) { - setAgentAttribute(MessageMetrics.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); - setAgentAttribute(MessageMetrics.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); + if (messageBrokerConfig.isInstanceReportingEnabled() && MessageMetrics.isEndpointParamsKnown(host, port)) { + setAgentAttribute(AttributeNames.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); + setAgentAttribute(AttributeNames.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); } } @@ -867,9 +867,9 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - if (messageBrokerConfig.isInstanceReportingEnabled() && !MessageMetrics.isAllParamsUnknown(library, host, port)) { - setAgentAttribute(MessageMetrics.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); - setAgentAttribute(MessageMetrics.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); + if (messageBrokerConfig.isInstanceReportingEnabled() && MessageMetrics.isEndpointParamsKnown(host, port)) { + setAgentAttribute(AttributeNames.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); + setAgentAttribute(AttributeNames.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); } } From 275c6c46f7eab05442758d66e30ab1ce4592d52f Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Wed, 15 May 2024 12:13:09 -0400 Subject: [PATCH 19/42] Add operation + destination to message rollup metric for endpoint --- .../agent/messaging/MessageMetrics.java | 37 ++++++++++++++----- .../newrelic/agent/tracers/DefaultTracer.java | 4 +- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index 4573a897fa..399497f9f2 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -3,6 +3,7 @@ import com.newrelic.agent.config.Hostname; import com.newrelic.agent.config.MessageBrokerConfig; import com.newrelic.agent.service.ServiceFactory; +import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.TracedMethod; public class MessageMetrics { @@ -16,33 +17,49 @@ public class MessageMetrics { public static boolean isEndpointParamsKnown(String host, Integer port) { return !(isParamUnknown(host) && isParamUnknown(port)); } - public static void collectMessageProducerRollupMetrics(TracedMethod method, String library, String host, Integer port) { - reportInstanceIfEnabled(method, library, host, port); + public static void collectMessageProducerRollupMetrics(TracedMethod method, String library, String host, Integer port, + DestinationType destinationType, String destinationName) { + reportInstanceIfEnabled(method, library, "Produce", host, port, destinationType, destinationName); } - public static void collectMessageConsumerRollupMetrics(TracedMethod method, String library, String host, Integer port) { - reportInstanceIfEnabled(method, library, host, port); + public static void collectMessageConsumerRollupMetrics(TracedMethod method, String library, String host, Integer port, + DestinationType destinationType, String destinationName) { + reportInstanceIfEnabled(method, library, "Consume", host, port, destinationType, destinationName); } - public static void reportInstanceIfEnabled(TracedMethod method, String library, String host, Integer port) { + public static void reportInstanceIfEnabled(TracedMethod method, String library, String operation, String host, Integer port, + DestinationType destinationType, String destinationName) { MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); if (messageBrokerConfig.isInstanceReportingEnabled()) { - String metric = buildInstanceMetric(library, host, port); + String metric = buildInstanceMetric(library, host, port, operation, destinationType, destinationName); method.addRollupMetricName(metric); } } - public static String buildInstanceMetric(String library, String host, Integer port) { - String instance = buildInstanceIdentifier(library, host, port); + public static String buildInstanceMetric(String library, String host, Integer port, String operation, + DestinationType destinationType, String destinationName) { + String instance = buildInstanceIdentifier(library, host, port, operation, destinationType, destinationName); return MESSAGE_BROKER_INSTANCE + instance; } - public static String buildInstanceIdentifier(String library, String host, Integer port) { + public static String buildInstanceIdentifier(String library, String host, Integer port, String operation, + DestinationType destinationType, String destinationName) { String libraryName = replaceLibrary(library); String hostname = replaceLocalhost(host); String portName = replacePort(port); + String parsedDestinationName = replaceDestinationName(destinationType, destinationName); - return libraryName + SLASH + hostname + SLASH + portName; + return libraryName + SLASH + hostname + SLASH + portName + SLASH + operation + SLASH + destinationType.getTypeName() + SLASH + parsedDestinationName; + } + + public static String replaceDestinationName(DestinationType destinationType, String destinationName) { + if (destinationType == DestinationType.TEMP_QUEUE || destinationType == DestinationType.TEMP_TOPIC) { + return "Temp"; + } + if (isParamUnknown(destinationName)) { + return UNKNOWN; + } + return destinationName.toLowerCase(); } public static String replaceLibrary(String library) { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 380bff54de..4f41051f92 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -827,7 +827,7 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP String host = messageProduceParameters.getHost(); Integer port = messageProduceParameters.getPort(); - MessageMetrics.collectMessageProducerRollupMetrics(this, library, host, port); + MessageMetrics.collectMessageProducerRollupMetrics(this, library, host, port, destinationType, messageProduceParameters.getDestinationName()); MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); @@ -863,7 +863,7 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP String host = messageConsumeParameters.getHost(); Integer port = messageConsumeParameters.getPort(); - MessageMetrics.collectMessageConsumerRollupMetrics(this, library, host, port); + MessageMetrics.collectMessageConsumerRollupMetrics(this, library, host, port, destinationType, messageConsumeParameters.getDestinationName()); MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); From ff9d03573cdf2c1a26ea32874bbd5895c8380410 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Thu, 16 May 2024 15:57:05 -0400 Subject: [PATCH 20/42] Change message broker host attribute to be message.broker.endpoint --- .../main/java/com/newrelic/agent/attributes/AttributeNames.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 960d7a4236..2c4a8a8bba 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 @@ -57,7 +57,7 @@ public final class AttributeNames { public static final String CLOUD_RESOURCE_ID = "cloud.resource_id"; // Message broker endpoint information - public static final String MESSAGE_BROKER_HOST = "message.broker.host"; + public static final String MESSAGE_BROKER_HOST = "message.broker.endpoint"; public static final String MESSAGE_BROKER_PORT = "message.broker.port"; public static final String RESPONSE_CONTENT_TYPE_PARAMETER_NAME = "response.headers.contentType"; From 39812ae7ba71c970ae7c77c5c33fb49932ee2f94 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Fri, 17 May 2024 11:12:18 -0400 Subject: [PATCH 21/42] Switch to otel message attribute names --- .../java/com/newrelic/agent/attributes/AttributeNames.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 2c4a8a8bba..bc2b431ab5 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 @@ -57,8 +57,8 @@ public final class AttributeNames { public static final String CLOUD_RESOURCE_ID = "cloud.resource_id"; // Message broker endpoint information - public static final String MESSAGE_BROKER_HOST = "message.broker.endpoint"; - public static final String MESSAGE_BROKER_PORT = "message.broker.port"; + public static final String MESSAGE_BROKER_HOST = "server.address"; + public static final String MESSAGE_BROKER_PORT = "server.port"; public static final String RESPONSE_CONTENT_TYPE_PARAMETER_NAME = "response.headers.contentType"; From 3b2a439f900d8721211e3f871c3dd2507ffa2a67 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Fri, 17 May 2024 15:27:20 -0400 Subject: [PATCH 22/42] Alter mq endpoint for temp queues/topics --- .../test/newrelic/test/agent/api/ApiTest.java | 76 ++++++++- .../agent/attributes/AttributeNames.java | 2 +- .../agent/messaging/MessageMetrics.java | 15 +- .../newrelic/agent/tracers/DefaultTracer.java | 4 +- .../agent/messaging/MessageMetricsTest.java | 154 ++++++++++++++++++ .../analytics/SpanEventFactoryTest.java | 34 ++++ 6 files changed, 272 insertions(+), 13 deletions(-) create mode 100644 newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java diff --git a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java index bddce9c7a5..91a4754a0a 100644 --- a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java +++ b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java @@ -15,9 +15,9 @@ import com.newrelic.agent.TransactionListener; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TransactionNamePriority; -import com.newrelic.agent.browser.BrowserConfigTest; import com.newrelic.agent.config.AgentConfigImpl; import com.newrelic.agent.config.ConfigConstant; +import com.newrelic.agent.config.Hostname; import com.newrelic.agent.dispatchers.WebRequestDispatcher; import com.newrelic.agent.environment.AgentIdentity; import com.newrelic.agent.errors.ErrorService; @@ -38,7 +38,6 @@ import com.newrelic.agent.tracers.servlet.MockHttpResponse; import com.newrelic.agent.transaction.PriorityTransactionName; import com.newrelic.agent.transaction.TransactionThrowable; -import com.newrelic.agent.util.Obfuscator; import com.newrelic.api.agent.DatastoreParameters; import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.ExtendedRequest; @@ -77,13 +76,11 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.regex.Matcher; import java.util.regex.Pattern; /* (non-javadoc) @@ -95,6 +92,7 @@ public class ApiTest implements TransactionListener { ApiTestHelper apiTestHelper = new ApiTestHelper(); private static final String CAT_CONFIG_FILE = "configs/cross_app_tracing_test.yml"; private static final String HIGH_SECURITY_CONFIG_FILE = "configs/high_security_config.yml"; + public static String HOSTNAME = Hostname.getHostname(ServiceFactory.getConfigService().getDefaultAgentConfig()); private static final ClassLoader CLASS_LOADER = ApiTest.class.getClassLoader(); @Before @@ -1996,7 +1994,32 @@ public void testMessagingAPI() throws Exception { server.start(); runTestMessagingAPI(); String messageBrokerMetric = "MessageBroker/JMS/Queue/Consume/Temp"; + String endpointMetric = "MessageBroker/instance/JMS/unknown/unknown/Consume/Queue/Temp"; Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); + Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); + } catch (IOException e) { + e.printStackTrace(); + Assert.fail(); + } finally { + Transaction.clearTransaction(); + server.closeAllConnections(); + holder.close(); + } + } + + @Test + public void testMessagingAPIWithHostAndPort() throws Exception { + // override default agent config to disabled distributed tracing and use CAT instead + EnvironmentHolder holder = setupEnvironmentHolder(CAT_CONFIG_FILE, "cat_enabled_dt_disabled_test"); + MessagingTestServer server = new MessagingTestServer(8088); + + try { + server.start(); + runTestMessagingAPIWithHostAndPort(); + String messageBrokerMetric = "MessageBroker/JMS/Queue/Consume/Temp"; + String endpointMetric = String.format("MessageBroker/instance/JMS/%s/8088/Consume/Queue/Temp", HOSTNAME); + Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); + Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); } catch (IOException e) { e.printStackTrace(); Assert.fail(); @@ -2048,6 +2071,51 @@ private void runTestMessagingAPI() { } } + @Trace(dispatcher = true) + private void runTestMessagingAPIWithHostAndPort() { + URL myURL = null; + try { + Thread.sleep(600); + myURL = new URL("http://localhost:8088"); + HttpUriRequest request = RequestBuilder.get().setUri(myURL.toURI()).build(); + + ApiTestHelper.OutboundWrapper outboundRequestWrapper = new ApiTestHelper.OutboundWrapper(request, HeaderType.MESSAGE); + + // MessageProducer + ExternalParameters messageProduceParameters = MessageProduceParameters + .library("JMS") + .destinationType(DestinationType.NAMED_QUEUE) + .destinationName("Message Destination") + .outboundHeaders(outboundRequestWrapper) + .host(myURL.getHost()) + .port(myURL.getPort()) + .build(); + NewRelic.getAgent().getTracedMethod().reportAsExternal(messageProduceParameters); + + Assert.assertTrue(request.getHeaders("NewRelicID").length != 0); + Assert.assertTrue(request.getHeaders("NewRelicTransaction").length != 0); + + CloseableHttpClient connection = HttpClientBuilder.create().build(); + CloseableHttpResponse response = connection.execute(request); + + // MessageConsumer + ExternalParameters messageResponseParameters = MessageConsumeParameters + .library("JMS") + .destinationType(DestinationType.TEMP_QUEUE) + .destinationName("Message Destination") + .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) + .host(myURL.getHost()) + .port(myURL.getPort()) + .build(); + NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); + + Assert.assertTrue(response.getHeaders("NewRelicAppData").length != 0); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + @Test public void testNotNull() { Assert.assertNotNull(NewRelic.getAgent().getTransaction().getTracedMethod()); 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 bc2b431ab5..080282cb07 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 @@ -56,7 +56,7 @@ public final class AttributeNames { // cloud provider identifier for the resource being used public static final String CLOUD_RESOURCE_ID = "cloud.resource_id"; - // Message broker endpoint information + // Message broker endpoint information (open telemetry compatible) public static final String MESSAGE_BROKER_HOST = "server.address"; public static final String MESSAGE_BROKER_PORT = "server.port"; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index 399497f9f2..8953bfda5b 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -12,19 +12,22 @@ public class MessageMetrics { public static final String MESSAGE_BROKER_INSTANCE = METRIC_NAMESPACE + "/instance/"; public static final String UNKNOWN = "unknown"; + + public static final String PRODUCE = "Produce"; + public static final String CONSUME = "Consume"; public static String HOSTNAME = Hostname.getHostname(ServiceFactory.getConfigService().getDefaultAgentConfig()); - public static boolean isEndpointParamsKnown(String host, Integer port) { + public static boolean isAnyEndpointParamsKnown(String host, Integer port) { return !(isParamUnknown(host) && isParamUnknown(port)); } public static void collectMessageProducerRollupMetrics(TracedMethod method, String library, String host, Integer port, DestinationType destinationType, String destinationName) { - reportInstanceIfEnabled(method, library, "Produce", host, port, destinationType, destinationName); + reportInstanceIfEnabled(method, library, PRODUCE, host, port, destinationType, destinationName); } public static void collectMessageConsumerRollupMetrics(TracedMethod method, String library, String host, Integer port, DestinationType destinationType, String destinationName) { - reportInstanceIfEnabled(method, library, "Consume", host, port, destinationType, destinationName); + reportInstanceIfEnabled(method, library, CONSUME, host, port, destinationType, destinationName); } public static void reportInstanceIfEnabled(TracedMethod method, String library, String operation, String host, Integer port, @@ -59,19 +62,19 @@ public static String replaceDestinationName(DestinationType destinationType, Str if (isParamUnknown(destinationName)) { return UNKNOWN; } - return destinationName.toLowerCase(); + return "Named" + SLASH + destinationName; } public static String replaceLibrary(String library) { if (isParamUnknown(library)) { return UNKNOWN; } - return library.toLowerCase(); + return library; } public static String replaceLocalhost(String host) { if (isParamUnknown(host)) { - return HOSTNAME; + return UNKNOWN; } if ("localhost".equals(host) || "127.0.0.1".equals(host) || "0.0.0.0".equals(host) diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 4f41051f92..e4126640b4 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -831,7 +831,7 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - if (messageBrokerConfig.isInstanceReportingEnabled() && MessageMetrics.isEndpointParamsKnown(host, port)) { + if (messageBrokerConfig.isInstanceReportingEnabled() && MessageMetrics.isAnyEndpointParamsKnown(host, port)) { setAgentAttribute(AttributeNames.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); setAgentAttribute(AttributeNames.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); } @@ -867,7 +867,7 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - if (messageBrokerConfig.isInstanceReportingEnabled() && MessageMetrics.isEndpointParamsKnown(host, port)) { + if (messageBrokerConfig.isInstanceReportingEnabled() && MessageMetrics.isAnyEndpointParamsKnown(host, port)) { setAgentAttribute(AttributeNames.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); setAgentAttribute(AttributeNames.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); } diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java new file mode 100644 index 0000000000..a0806179ce --- /dev/null +++ b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java @@ -0,0 +1,154 @@ +package com.newrelic.agent.messaging; + +import com.newrelic.agent.AgentHelper; +import com.newrelic.api.agent.DestinationType; +import com.newrelic.api.agent.TracedMethod; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.text.MessageFormat; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MessageMetricsTest { + + private final String hostName; + + TracedMethod mockTracedMethod; + + public MessageMetricsTest() throws UnknownHostException { + this.hostName = InetAddress.getLocalHost().getHostName(); + } + + @Before + public void before() throws Exception { + AgentHelper.bootstrap(AgentHelper.createAgentConfig(true)); + mockTracedMethod = Mockito.mock(TracedMethod.class); + } + + @Test + public void testReplaceEmptyPort() { + assertEquals(MessageMetrics.UNKNOWN, MessageMetrics.replacePort(null)); + assertEquals(MessageMetrics.UNKNOWN, MessageMetrics.replacePort(-1)); + assertEquals("1234", MessageMetrics.replacePort(1234)); + } + + @Test + public void testReplaceLocalhost() throws Exception { + assertEquals(MessageMetrics.UNKNOWN, MessageMetrics.replaceLocalhost(null)); + + final String hostname = InetAddress.getLocalHost().getHostName(); + + assertEquals(hostname, MessageMetrics.replaceLocalhost("localhost")); + assertEquals(hostname, MessageMetrics.replaceLocalhost("127.0.0.1")); + assertEquals(hostname, MessageMetrics.replaceLocalhost("0.0.0.0")); + assertEquals(hostname, MessageMetrics.replaceLocalhost("0:0:0:0:0:0:0:1")); + assertEquals(hostname, MessageMetrics.replaceLocalhost("::1")); + assertEquals(hostname, MessageMetrics.replaceLocalhost("0:0:0:0:0:0:0:0")); + assertEquals(hostname, MessageMetrics.replaceLocalhost("::")); + + assertEquals("example.com", MessageMetrics.replaceLocalhost("example.com")); + } + + @Test + public void testIsAnyEndpointParamsKnown() { + assertTrue(MessageMetrics.isAnyEndpointParamsKnown("example.com", 1000)); + assertTrue(MessageMetrics.isAnyEndpointParamsKnown("example.com", null)); + assertTrue(MessageMetrics.isAnyEndpointParamsKnown(null, 1000)); + assertFalse(MessageMetrics.isAnyEndpointParamsKnown(null, null)); + assertTrue(MessageMetrics.isAnyEndpointParamsKnown("example.com", -1)); + assertTrue(MessageMetrics.isAnyEndpointParamsKnown("", 1000)); + assertFalse(MessageMetrics.isAnyEndpointParamsKnown("", -1)); + } + + @Test + public void testCollectMessageProducerRollupMetrics() { + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "localhost", + 8080, DestinationType.NAMED_TOPIC, "topic"); + Mockito.verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Produce/Topic/Named/topic", hostName)); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "localhost", + 8080, DestinationType.NAMED_QUEUE, "queue"); + Mockito.verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Produce/Queue/Named/queue", hostName)); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "localhost", + 8080, DestinationType.TEMP_TOPIC, null); + Mockito.verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Produce/Topic/Temp", hostName)); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "localhost", + 8080, DestinationType.TEMP_QUEUE, null); + Mockito.verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Produce/Queue/Temp", hostName)); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "example.com", + 8080, DestinationType.NAMED_QUEUE, "queue"); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/8080/Produce/Queue/Named/queue"); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", null, + 8080, DestinationType.EXCHANGE, "queue"); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/unknown/8080/Produce/Exchange/Named/queue"); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "example.com", + null, DestinationType.NAMED_QUEUE, "queue"); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/unknown/Produce/Queue/Named/queue"); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, null, + null, DestinationType.EXCHANGE, null); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/unknown/Produce/Exchange/unknown"); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "example.com", + 8080, DestinationType.EXCHANGE, null); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/8080/Produce/Exchange/unknown"); + } + + @Test + public void testCollectMessageConsumerRollupMetrics() { + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "localhost", + 8080, DestinationType.NAMED_TOPIC, "topic"); + Mockito.verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Consume/Topic/Named/topic", hostName)); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "localhost", + 8080, DestinationType.NAMED_QUEUE, "queue"); + Mockito.verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Consume/Queue/Named/queue", hostName)); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "localhost", + 8080, DestinationType.TEMP_TOPIC, null); + Mockito.verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Consume/Topic/Temp", hostName)); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "localhost", + 8080, DestinationType.TEMP_QUEUE, null); + Mockito.verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Consume/Queue/Temp", hostName)); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "example.com", + 8080, DestinationType.NAMED_QUEUE, "queue"); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/8080/Consume/Queue/Named/queue"); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", null, + 8080, DestinationType.EXCHANGE, "queue"); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/unknown/8080/Consume/Exchange/Named/queue"); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "example.com", + null, DestinationType.NAMED_QUEUE, "queue"); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/unknown/Consume/Queue/Named/queue"); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, null, + null, DestinationType.EXCHANGE, null); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/unknown/Consume/Exchange/unknown"); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "example.com", + 8080, DestinationType.EXCHANGE, null); + Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/8080/Consume/Exchange/unknown"); + } +} 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 a59d7a711a..1da3c0d0da 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 @@ -281,6 +281,23 @@ public void shouldSetCloudResourceIdOnSpanFromMessageProduceParameters() { assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); } + @Test + public void shouldSetEndpointOnSpanFromMessageProduceParameters() { + String host = "localhost"; + Integer port = 61616; + MessageProduceParameters mockParameters = mock(MessageProduceParameters.class); + when(mockParameters.getLibrary()).thenReturn("JMS"); + when(mockParameters.getDestinationName()).thenReturn("queueName"); + when(mockParameters.getDestinationType()).thenReturn(DestinationType.NAMED_QUEUE); + when(mockParameters.getHost()).thenReturn(host); + when(mockParameters.getPort()).thenReturn(port); + SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); + + Map agentAttrs = target.getAgentAttributes(); + assertEquals(host, agentAttrs.get("server.address")); + assertEquals(port, agentAttrs.get("server.port")); + } + @Test public void shouldSetCloudResourceIdOnSpanFromMessageConsumeParameters() { String expectedArn = "arn:aws:sqs:us-east-1:123456789012:queueName"; @@ -295,6 +312,23 @@ public void shouldSetCloudResourceIdOnSpanFromMessageConsumeParameters() { assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); } + @Test + public void shouldSetEndpointOnSpanFromMessageConsumeParameters() { + String host = "localhost"; + Integer port = 61616; + MessageConsumeParameters mockParameters = mock(MessageConsumeParameters.class); + when(mockParameters.getLibrary()).thenReturn("JMS"); + when(mockParameters.getDestinationName()).thenReturn("queueName"); + when(mockParameters.getDestinationType()).thenReturn(DestinationType.NAMED_QUEUE); + when(mockParameters.getHost()).thenReturn(host); + when(mockParameters.getPort()).thenReturn(port); + SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); + + Map agentAttrs = target.getAgentAttributes(); + assertEquals(host, agentAttrs.get("server.address")); + assertEquals(port, agentAttrs.get("server.port")); + } + @Test public void shouldStoreStackTrace() { SpanEventFactory spanEventFactory = new SpanEventFactory("MyApp", new AttributeFilter.PassEverythingAttributeFilter(), From e348b2a9cda50f6537501d1fe50fb438b3a3da57 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Fri, 17 May 2024 16:34:22 -0400 Subject: [PATCH 23/42] Improve caching for parsing hostname in activemq --- .../agent/bridge/messaging/JmsProperties.java | 5 +++ .../agent/bridge/messaging/JmsUtil.java | 27 -------------- .../activemqclient580/ActiveMQUtil.java | 35 ++++++++++++++----- .../ActiveMQMessage_Instrumentation.java | 7 ++-- .../activemqclient580/ActiveMQUtilTest.java | 12 +++---- .../instrumentation/jms11/JmsMetricUtil.java | 4 +-- .../instrumentation/jms3/JmsMetricUtil.java | 4 +-- 7 files changed, 43 insertions(+), 51 deletions(-) create mode 100644 agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java delete mode 100644 agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java new file mode 100644 index 0000000000..976c9c2ad9 --- /dev/null +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java @@ -0,0 +1,5 @@ +package com.newrelic.agent.bridge.messaging; + +public class JmsProperties { + public static final String NR_JMS_HOST_AND_PORT_PROPERTY = "com.nr.agent.instrumentation.jms.HostAndPort"; +} diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java deleted file mode 100644 index c4abe4e1df..0000000000 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsUtil.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.newrelic.agent.bridge.messaging; - -import com.newrelic.agent.bridge.AgentBridge; - -import java.util.Map; - -public class JmsUtil { - private static final Map hostAndPortCache = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); - - public static final String NR_JMS_HOST_AND_PORT_PROPERTY = "com.nr.agent.instrumentation.jms.HostAndPort"; - - public static HostAndPort getHostAndPortFromCache(String address) { - Object obj = hostAndPortCache.get(address); - if (obj instanceof HostAndPort) { - return (HostAndPort) obj; - } - if (obj != null) { - hostAndPortCache.remove(address); - } - return null; - } - - public static HostAndPort cacheAndReturnHostAndPort(String address, HostAndPort hostAndPort) { - hostAndPortCache.put(address, hostAndPort); - return hostAndPort; - } -} diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java index 78abc6bc91..ec10f0cb6d 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java @@ -1,18 +1,29 @@ package com.nr.agent.instrumentation.activemqclient580; +import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.messaging.HostAndPort; -import com.newrelic.agent.bridge.messaging.JmsUtil; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ActiveMQUtil { - public static final Pattern addressPattern = Pattern.compile("^\\w+://(.+)/.+:(\\d+)"); - public static HostAndPort parseHostAndPort(String address) { - HostAndPort cached = JmsUtil.getHostAndPortFromCache(address); - if (cached != null) { - return cached; - } + + private static final ActiveMQUtil INSTANCE = ActiveMQUtil.create(); + + public static ActiveMQUtil get() { + return INSTANCE; + } + + private final Pattern addressPattern = Pattern.compile("^\\w+://(.+)/.+:(\\d+)"); + + public HostAndPort parseHostAndPort(String address) { + return CACHE.apply(address); + } + + private final Function CACHE = AgentBridge.collectionFactory.memoize(this::doParseHostAndPort, 32); + + public HostAndPort doParseHostAndPort(String address) { Matcher m = addressPattern.matcher(address); if(!m.find()) { @@ -28,8 +39,14 @@ public static HostAndPort parseHostAndPort(String address) { } catch (NumberFormatException e) { return HostAndPort.empty(); } + return new HostAndPort(hostName, port); + } + + private ActiveMQUtil() { + // prevent instantiation of utility class + } - HostAndPort hostAndPort = new HostAndPort(hostName, port); - return JmsUtil.cacheAndReturnHostAndPort(address, hostAndPort); + private static ActiveMQUtil create() { + return new ActiveMQUtil(); } } diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java index e17fdb869f..60a6330ea4 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java @@ -1,15 +1,12 @@ package org.apache.activemq.command; -import com.newrelic.agent.bridge.messaging.HostAndPort; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.agent.instrumentation.activemqclient580.ActiveMQUtil; import org.apache.activemq.ActiveMQConnection; -import javax.jms.JMSException; - -import static com.newrelic.agent.bridge.messaging.JmsUtil.NR_JMS_HOST_AND_PORT_PROPERTY; +import static com.newrelic.agent.bridge.messaging.JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY; @Weave(type = MatchType.BaseClass, originalName = "org.apache.activemq.command.ActiveMQMessage") public abstract class ActiveMQMessage_Instrumentation { @@ -18,7 +15,7 @@ public abstract class ActiveMQMessage_Instrumentation { // This is so the JMS instrumentation can grab host and port of the Active MQ instance public Object getObjectProperty(String name) { if (NR_JMS_HOST_AND_PORT_PROPERTY.equals(name)) { - return ActiveMQUtil.parseHostAndPort(getConnection().getTransport().toString()); + return ActiveMQUtil.get().parseHostAndPort(getConnection().getTransport().toString()); } return Weaver.callOriginal(); } diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java index 74be5ec89a..b950e035fb 100644 --- a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java +++ b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java @@ -22,11 +22,11 @@ public void testAwsUrlParsing() { final String expectedHost = "b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com"; final Integer expectedPort = 61617; - final HostAndPort hostAndPort = ActiveMQUtil.parseHostAndPort(awsAddress); + final HostAndPort hostAndPort = ActiveMQUtil.get().parseHostAndPort(awsAddress); assertHostAndPort(expectedHost, expectedPort, hostAndPort); // Verify caching works - final HostAndPort hostAndPortRepeated = ActiveMQUtil.parseHostAndPort(awsAddress); + final HostAndPort hostAndPortRepeated = ActiveMQUtil.get().parseHostAndPort(awsAddress); assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); } @@ -38,11 +38,11 @@ public void testAwsUrlParsingWithLocalPort() { final String expectedHost = "b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com"; final Integer expectedPort = 61617; - final HostAndPort hostAndPort = ActiveMQUtil.parseHostAndPort(awsAddress); + final HostAndPort hostAndPort = ActiveMQUtil.get().parseHostAndPort(awsAddress); assertHostAndPort(expectedHost, expectedPort, hostAndPort); // Verify caching works - final HostAndPort hostAndPortRepeated = ActiveMQUtil.parseHostAndPort(awsAddress); + final HostAndPort hostAndPortRepeated = ActiveMQUtil.get().parseHostAndPort(awsAddress); assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); } @@ -54,11 +54,11 @@ public void testLocalhostParsing() { final String expectedHost = "localhost"; final Integer expectedPort = 61616; - final HostAndPort hostAndPort = ActiveMQUtil.parseHostAndPort(localhostAddress); + final HostAndPort hostAndPort = ActiveMQUtil.get().parseHostAndPort(localhostAddress); assertHostAndPort(expectedHost, expectedPort, hostAndPort); // Verify caching works - final HostAndPort hostAndPortRepeated = ActiveMQUtil.parseHostAndPort(localhostAddress); + final HostAndPort hostAndPortRepeated = ActiveMQUtil.get().parseHostAndPort(localhostAddress); assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); } diff --git a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java index c1484bfe88..dc1afd93c7 100644 --- a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java +++ b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java @@ -10,7 +10,7 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TracedMethod; import com.newrelic.agent.bridge.messaging.HostAndPort; -import com.newrelic.agent.bridge.messaging.JmsUtil; +import com.newrelic.agent.bridge.messaging.JmsProperties; import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.MessageConsumeParameters; import com.newrelic.api.agent.MessageProduceParameters; @@ -173,7 +173,7 @@ public static void processConsume(Message message, TracedMethod tracer) { } private static HostAndPort getHostAndPort(Message message) throws JMSException { - Object obj = message.getObjectProperty(JmsUtil.NR_JMS_HOST_AND_PORT_PROPERTY); + Object obj = message.getObjectProperty(JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY); if (obj instanceof HostAndPort) { return (HostAndPort) obj; } diff --git a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java index 0c4609ee76..bc3680eeec 100644 --- a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java +++ b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java @@ -10,7 +10,7 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TracedMethod; import com.newrelic.agent.bridge.messaging.HostAndPort; -import com.newrelic.agent.bridge.messaging.JmsUtil; +import com.newrelic.agent.bridge.messaging.JmsProperties; import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.MessageConsumeParameters; import com.newrelic.api.agent.MessageProduceParameters; @@ -173,7 +173,7 @@ public static void processConsume(Message message, TracedMethod tracer) { } private static HostAndPort getHostAndPort(Message message) throws JMSException { - Object obj = message.getObjectProperty(JmsUtil.NR_JMS_HOST_AND_PORT_PROPERTY); + Object obj = message.getObjectProperty(JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY); if (obj instanceof HostAndPort) { return (HostAndPort) obj; } From 7afaa8e87c8c0885a335dde40312a2b1dbc8dcae Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Fri, 17 May 2024 16:45:15 -0400 Subject: [PATCH 24/42] Add copyright headers --- .../com/newrelic/agent/bridge/messaging/HostAndPort.java | 6 ++++++ .../com/newrelic/agent/bridge/messaging/JmsProperties.java | 6 ++++++ .../instrumentation/activemqclient580/ActiveMQUtil.java | 6 ++++++ .../activemq/command/ActiveMQMessage_Instrumentation.java | 6 ++++++ .../activemqclient580/ActiveMQUtilTest.java | 2 +- .../integration-test/java/RabbitMQTest_Integration.java | 6 ++++++ .../integration-test/java/RabbitMQTest_Integration.java | 6 ++++++ .../com/newrelic/agent/config/MessageBrokerConfig.java | 7 +++++++ .../com/newrelic/agent/config/MessageBrokerConfigImpl.java | 7 +++++++ .../java/com/newrelic/agent/messaging/MessageMetrics.java | 6 ++++++ .../com/newrelic/agent/messaging/MessageMetricsTest.java | 6 ++++++ 11 files changed, 63 insertions(+), 1 deletion(-) diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java index 5c0a2282d5..270de2e6f7 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java @@ -1,3 +1,9 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ package com.newrelic.agent.bridge.messaging; public class HostAndPort { diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java index 976c9c2ad9..306a23846d 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java @@ -1,3 +1,9 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ package com.newrelic.agent.bridge.messaging; public class JmsProperties { diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java index ec10f0cb6d..c5c11852cb 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java @@ -1,3 +1,9 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ package com.nr.agent.instrumentation.activemqclient580; import com.newrelic.agent.bridge.AgentBridge; diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java index 60a6330ea4..79adb8666e 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java @@ -1,3 +1,9 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ package org.apache.activemq.command; import com.newrelic.api.agent.weaver.MatchType; diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java index b950e035fb..2a46bc4d3f 100644 --- a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java +++ b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2024 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ diff --git a/instrumentation/rabbit-amqp-1.7.2/src/integration-test/java/RabbitMQTest_Integration.java b/instrumentation/rabbit-amqp-1.7.2/src/integration-test/java/RabbitMQTest_Integration.java index f7119786a3..fe4225b5a0 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/integration-test/java/RabbitMQTest_Integration.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/integration-test/java/RabbitMQTest_Integration.java @@ -1,3 +1,9 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ import com.newrelic.agent.introspec.InstrumentationTestConfig; import com.newrelic.agent.introspec.InstrumentationTestRunner; import com.newrelic.agent.introspec.Introspector; diff --git a/instrumentation/rabbit-amqp-2.4.1/src/integration-test/java/RabbitMQTest_Integration.java b/instrumentation/rabbit-amqp-2.4.1/src/integration-test/java/RabbitMQTest_Integration.java index 388048856b..6ee5ef19c0 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/integration-test/java/RabbitMQTest_Integration.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/integration-test/java/RabbitMQTest_Integration.java @@ -1,3 +1,9 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ import com.newrelic.agent.introspec.InstrumentationTestConfig; import com.newrelic.agent.introspec.InstrumentationTestRunner; import com.newrelic.agent.introspec.Introspector; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java index 9ea0567a29..29a0e5d2b4 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java @@ -1,3 +1,10 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + package com.newrelic.agent.config; public interface MessageBrokerConfig { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java index 0a89f67d71..41c0653191 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java @@ -1,3 +1,10 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + package com.newrelic.agent.config; import java.util.Map; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index 8953bfda5b..c0c0b96d85 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -1,3 +1,9 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ package com.newrelic.agent.messaging; import com.newrelic.agent.config.Hostname; diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java index a0806179ce..9da5c44587 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java @@ -1,3 +1,9 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ package com.newrelic.agent.messaging; import com.newrelic.agent.AgentHelper; From 8281ba3ea8e6d60f48956e753d4d38671cc7b85a Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 20 May 2024 13:10:56 -0400 Subject: [PATCH 25/42] Remove span attributes --- .../agent/attributes/AttributeNames.java | 5 --- .../service/analytics/SpanEventFactory.java | 14 -------- .../newrelic/agent/tracers/DefaultTracer.java | 14 -------- .../analytics/SpanEventFactoryTest.java | 34 ------------------- 4 files changed, 67 deletions(-) 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 080282cb07..65869d3563 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 @@ -55,11 +55,6 @@ public final class AttributeNames { // cloud provider identifier for the resource being used public static final String CLOUD_RESOURCE_ID = "cloud.resource_id"; - - // Message broker endpoint information (open telemetry compatible) - public static final String MESSAGE_BROKER_HOST = "server.address"; - public static final String MESSAGE_BROKER_PORT = "server.port"; - public static final String RESPONSE_CONTENT_TYPE_PARAMETER_NAME = "response.headers.contentType"; // high security matches 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 0bc343a7b8..4fd60056b5 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 @@ -288,16 +288,6 @@ public SpanEventFactory setCloudResourceId(String cloudResourceId) { return this; } - public SpanEventFactory setMessageBrokerHost(String host) { - builder.putAgentAttribute(AttributeNames.MESSAGE_BROKER_HOST, host); - return this; - } - - public SpanEventFactory setMessageBrokerPort(Integer port) { - builder.putAgentAttribute(AttributeNames.MESSAGE_BROKER_PORT, port); - return this; - } - public SpanEventFactory setServerPort(int port) { builder.putAgentAttribute("server.port", port); return this; @@ -420,14 +410,10 @@ public SpanEventFactory setExternalParameterAttributes(ExternalParameters parame MessageProduceParameters messageProduceParameters = (MessageProduceParameters) parameters; setCategory(SpanCategory.generic); setCloudResourceId(messageProduceParameters.getCloudResourceId()); - setMessageBrokerHost(messageProduceParameters.getHost()); - setMessageBrokerPort(messageProduceParameters.getPort()); } else if (parameters instanceof MessageConsumeParameters) { MessageConsumeParameters messageConsumeParameters = (MessageConsumeParameters) parameters; setCategory(SpanCategory.generic); setCloudResourceId(messageConsumeParameters.getCloudResourceId()); - setMessageBrokerHost(messageConsumeParameters.getHost()); - setMessageBrokerPort(messageConsumeParameters.getPort()); } else { setCategory(SpanCategory.generic); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index e4126640b4..2c6ad6da52 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -828,13 +828,6 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP Integer port = messageProduceParameters.getPort(); MessageMetrics.collectMessageProducerRollupMetrics(this, library, host, port, destinationType, messageProduceParameters.getDestinationName()); - - MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - - if (messageBrokerConfig.isInstanceReportingEnabled() && MessageMetrics.isAnyEndpointParamsKnown(host, port)) { - setAgentAttribute(AttributeNames.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); - setAgentAttribute(AttributeNames.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); - } } private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeParameters) { @@ -864,13 +857,6 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP Integer port = messageConsumeParameters.getPort(); MessageMetrics.collectMessageConsumerRollupMetrics(this, library, host, port, destinationType, messageConsumeParameters.getDestinationName()); - - MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - - if (messageBrokerConfig.isInstanceReportingEnabled() && MessageMetrics.isAnyEndpointParamsKnown(host, port)) { - setAgentAttribute(AttributeNames.MESSAGE_BROKER_HOST, MessageMetrics.replaceLocalhost(host)); - setAgentAttribute(AttributeNames.MESSAGE_BROKER_PORT, MessageMetrics.replacePort(port)); - } } private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { 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 1da3c0d0da..a59d7a711a 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 @@ -281,23 +281,6 @@ public void shouldSetCloudResourceIdOnSpanFromMessageProduceParameters() { assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); } - @Test - public void shouldSetEndpointOnSpanFromMessageProduceParameters() { - String host = "localhost"; - Integer port = 61616; - MessageProduceParameters mockParameters = mock(MessageProduceParameters.class); - when(mockParameters.getLibrary()).thenReturn("JMS"); - when(mockParameters.getDestinationName()).thenReturn("queueName"); - when(mockParameters.getDestinationType()).thenReturn(DestinationType.NAMED_QUEUE); - when(mockParameters.getHost()).thenReturn(host); - when(mockParameters.getPort()).thenReturn(port); - SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); - - Map agentAttrs = target.getAgentAttributes(); - assertEquals(host, agentAttrs.get("server.address")); - assertEquals(port, agentAttrs.get("server.port")); - } - @Test public void shouldSetCloudResourceIdOnSpanFromMessageConsumeParameters() { String expectedArn = "arn:aws:sqs:us-east-1:123456789012:queueName"; @@ -312,23 +295,6 @@ public void shouldSetCloudResourceIdOnSpanFromMessageConsumeParameters() { assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); } - @Test - public void shouldSetEndpointOnSpanFromMessageConsumeParameters() { - String host = "localhost"; - Integer port = 61616; - MessageConsumeParameters mockParameters = mock(MessageConsumeParameters.class); - when(mockParameters.getLibrary()).thenReturn("JMS"); - when(mockParameters.getDestinationName()).thenReturn("queueName"); - when(mockParameters.getDestinationType()).thenReturn(DestinationType.NAMED_QUEUE); - when(mockParameters.getHost()).thenReturn(host); - when(mockParameters.getPort()).thenReturn(port); - SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); - - Map agentAttrs = target.getAgentAttributes(); - assertEquals(host, agentAttrs.get("server.address")); - assertEquals(port, agentAttrs.get("server.port")); - } - @Test public void shouldStoreStackTrace() { SpanEventFactory spanEventFactory = new SpanEventFactory("MyApp", new AttributeFilter.PassEverythingAttributeFilter(), From 2c1c79441f33e58fda34cc6d94459c16eead2612 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 20 May 2024 16:22:53 -0400 Subject: [PATCH 26/42] Change unit to instrumentation test for activemq --- .../ActiveMQMessageTest.java | 97 +++++++++++++++++++ .../activemqclient580/ActiveMQUtilTest.java | 70 ------------- 2 files changed, 97 insertions(+), 70 deletions(-) create mode 100644 instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java delete mode 100644 instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java new file mode 100644 index 0000000000..505423272e --- /dev/null +++ b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java @@ -0,0 +1,97 @@ +package com.nr.agent.instrumentation.activemqclient580; + +import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import org.apache.activemq.ActiveMQConnection; +import org.apache.activemq.command.ActiveMQMessage; +import org.apache.activemq.transport.Transport; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import javax.jms.JMSException; + +import static com.newrelic.agent.bridge.messaging.JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "org.apache" }) +public class ActiveMQMessageTest { + + private Transport transport; + private ActiveMQMessage activeMQMessage; + + @Before + public void setUp() { + ActiveMQConnection connection = Mockito.mock(ActiveMQConnection.class); + transport = Mockito.mock(Transport.class); + Mockito.when(connection.getTransport()).thenReturn(transport); + activeMQMessage = new ActiveMQMessage(); + activeMQMessage.setConnection(connection); + } + + @Test + public void fromMessageGetUnsetProperty() throws Exception { + Object object = activeMQMessage.getObjectProperty("unsetProperty"); + assertFalse("Object must not be an instance of HostAndPort", object instanceof HostAndPort); + assertNull("object must be null", object); + } + + @Test + public void fromMessageGetHostAndPort() throws Exception { + final String awsAddress = "ssl://b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com/174.65.25.235:61617"; + + final String expectedHost = "b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com"; + final Integer expectedPort = 61617; + + setStubs(awsAddress); + + assertMessage(expectedHost, expectedPort, activeMQMessage); + // Verify Caching + assertMessage(expectedHost, expectedPort, activeMQMessage); + } + + @Test + public void fromMessageGetHostAndPortWithLocalPort() throws Exception { + final String awsAddress = "ssl://b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com/174.65.25.235:61617@59925"; + + final String expectedHost = "b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com"; + final Integer expectedPort = 61617; + + setStubs(awsAddress); + + assertMessage(expectedHost, expectedPort, activeMQMessage); + // Verify Caching + assertMessage(expectedHost, expectedPort, activeMQMessage); + } + + @Test + public void fromMessageGetHostAndPortWithLocalHost() throws Exception { + final String localhostAddress = "tcp://localhost/127.0.0.1:61616@59925"; + final String expectedHost = "localhost"; + final Integer expectedPort = 61616; + + setStubs(localhostAddress); + + assertMessage(expectedHost, expectedPort, activeMQMessage); + // Verify Caching + assertMessage(expectedHost, expectedPort, activeMQMessage); + } + + private void setStubs(String transportString) { + Mockito.when(transport.toString()).thenReturn(transportString); + } + + private void assertMessage(String expectedHost, Integer expectedPort, ActiveMQMessage message) throws JMSException { + HostAndPort hostAndPort = (HostAndPort)message.getObjectProperty(NR_JMS_HOST_AND_PORT_PROPERTY); + assertNotNull("Failed to retrieve hostAndPort from ActiveMQ message", hostAndPort); + assertEquals("Expected host did not match", expectedHost, hostAndPort.getHostName()); + assertEquals("Expected port did not match", expectedPort, hostAndPort.getPort()); + } + +} diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java deleted file mode 100644 index 2a46bc4d3f..0000000000 --- a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtilTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.agent.instrumentation.activemqclient580; - -import com.newrelic.agent.bridge.messaging.HostAndPort; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class ActiveMQUtilTest { - - @Test - public void testAwsUrlParsing() { - final String awsAddress = "ssl://b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com/174.65.25.235:61617"; - - final String expectedHost = "b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com"; - final Integer expectedPort = 61617; - - final HostAndPort hostAndPort = ActiveMQUtil.get().parseHostAndPort(awsAddress); - assertHostAndPort(expectedHost, expectedPort, hostAndPort); - - // Verify caching works - final HostAndPort hostAndPortRepeated = ActiveMQUtil.get().parseHostAndPort(awsAddress); - assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); - - } - - @Test - public void testAwsUrlParsingWithLocalPort() { - final String awsAddress = "ssl://b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com/174.65.25.235:61617@59925"; - - final String expectedHost = "b-cd914095-3880-10d3-bb93-ee07ce1f57a5-1.mq.us-east-2.amazonaws.com"; - final Integer expectedPort = 61617; - - final HostAndPort hostAndPort = ActiveMQUtil.get().parseHostAndPort(awsAddress); - assertHostAndPort(expectedHost, expectedPort, hostAndPort); - - // Verify caching works - final HostAndPort hostAndPortRepeated = ActiveMQUtil.get().parseHostAndPort(awsAddress); - assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); - - } - - @Test - public void testLocalhostParsing() { - final String localhostAddress = "tcp://localhost/127.0.0.1:61616@59925"; - - final String expectedHost = "localhost"; - final Integer expectedPort = 61616; - - final HostAndPort hostAndPort = ActiveMQUtil.get().parseHostAndPort(localhostAddress); - assertHostAndPort(expectedHost, expectedPort, hostAndPort); - - // Verify caching works - final HostAndPort hostAndPortRepeated = ActiveMQUtil.get().parseHostAndPort(localhostAddress); - assertHostAndPort(expectedHost, expectedPort, hostAndPortRepeated); - } - - private void assertHostAndPort(final String expectedHostName, final Integer expectedPort, final HostAndPort hostAndPort) { - assertNotNull(hostAndPort); - assertEquals(expectedHostName, hostAndPort.getHostName()); - assertEquals(expectedPort, hostAndPort.getPort()); - } -} From 8edd40566fa4f7d74bff4737184f42027742935a Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 20 May 2024 16:32:24 -0400 Subject: [PATCH 27/42] Mockito verify asserts --- .../ActiveMQMessageTest.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java index 505423272e..fe93c8c9eb 100644 --- a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java +++ b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java @@ -30,9 +30,12 @@ public class ActiveMQMessageTest { public void setUp() { ActiveMQConnection connection = Mockito.mock(ActiveMQConnection.class); transport = Mockito.mock(Transport.class); + Mockito.when(connection.getTransport()).thenReturn(transport); - activeMQMessage = new ActiveMQMessage(); - activeMQMessage.setConnection(connection); + + ActiveMQMessage message = new ActiveMQMessage(); + message.setConnection(connection); + activeMQMessage = Mockito.spy(message); } @Test @@ -51,9 +54,9 @@ public void fromMessageGetHostAndPort() throws Exception { setStubs(awsAddress); - assertMessage(expectedHost, expectedPort, activeMQMessage); + assertMessage(expectedHost, expectedPort, activeMQMessage, 1); // Verify Caching - assertMessage(expectedHost, expectedPort, activeMQMessage); + assertMessage(expectedHost, expectedPort, activeMQMessage, 2); } @Test @@ -65,9 +68,9 @@ public void fromMessageGetHostAndPortWithLocalPort() throws Exception { setStubs(awsAddress); - assertMessage(expectedHost, expectedPort, activeMQMessage); + assertMessage(expectedHost, expectedPort, activeMQMessage, 1); // Verify Caching - assertMessage(expectedHost, expectedPort, activeMQMessage); + assertMessage(expectedHost, expectedPort, activeMQMessage, 2); } @Test @@ -78,17 +81,18 @@ public void fromMessageGetHostAndPortWithLocalHost() throws Exception { setStubs(localhostAddress); - assertMessage(expectedHost, expectedPort, activeMQMessage); + assertMessage(expectedHost, expectedPort, activeMQMessage, 1); // Verify Caching - assertMessage(expectedHost, expectedPort, activeMQMessage); + assertMessage(expectedHost, expectedPort, activeMQMessage, 2); } private void setStubs(String transportString) { Mockito.when(transport.toString()).thenReturn(transportString); } - private void assertMessage(String expectedHost, Integer expectedPort, ActiveMQMessage message) throws JMSException { + private void assertMessage(String expectedHost, Integer expectedPort, ActiveMQMessage message, Integer timesGetConnectionCalled) throws JMSException { HostAndPort hostAndPort = (HostAndPort)message.getObjectProperty(NR_JMS_HOST_AND_PORT_PROPERTY); + Mockito.verify(message, Mockito.times(timesGetConnectionCalled)).getConnection(); assertNotNull("Failed to retrieve hostAndPort from ActiveMQ message", hostAndPort); assertEquals("Expected host did not match", expectedHost, hostAndPort.getHostName()); assertEquals("Expected port did not match", expectedPort, hostAndPort.getPort()); From 75b8ce923557dbc527efe76a876095b783316b85 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 20 May 2024 17:56:21 -0400 Subject: [PATCH 28/42] Change external api to pass host & port in one method --- .../{HostAndPort.java => BrokerInstance.java} | 8 +++---- .../agent/bridge/messaging/JmsProperties.java | 2 +- .../test/newrelic/test/agent/api/ApiTest.java | 6 ++--- .../activemqclient580/ActiveMQUtil.java | 14 +++++------ .../ActiveMQMessageTest.java | 12 +++++----- .../instrumentation/jms11/JmsMetricUtil.java | 24 ++++++++----------- .../instrumentation/jms3/JmsMetricUtil.java | 24 ++++++++----------- .../rabbitamqp172/RabbitAMQPMetricUtil.java | 6 ++--- .../rabbitamqp241/RabbitAMQPMetricUtil.java | 6 ++--- .../rabbitamqp250/RabbitAMQPMetricUtil.java | 6 ++--- .../rabbitamqp270/RabbitAMQPMetricUtil.java | 6 ++--- .../rabbitamqp500/RabbitAMQPMetricUtil.java | 6 ++--- .../api/agent/MessageConsumeParameters.java | 16 +++---------- .../api/agent/MessageProduceParameters.java | 16 +++---------- 14 files changed, 56 insertions(+), 96 deletions(-) rename agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/{HostAndPort.java => BrokerInstance.java} (71%) diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/BrokerInstance.java similarity index 71% rename from agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java rename to agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/BrokerInstance.java index 270de2e6f7..72fb8fdc2f 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/HostAndPort.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/BrokerInstance.java @@ -6,15 +6,15 @@ */ package com.newrelic.agent.bridge.messaging; -public class HostAndPort { +public class BrokerInstance { private final String hostName; private final Integer port; - public static HostAndPort empty() { - return new HostAndPort(null, null); + public static BrokerInstance empty() { + return new BrokerInstance(null, null); } - public HostAndPort(String host, Integer port) { + public BrokerInstance(String host, Integer port) { this.hostName = host; this.port = port; } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java index 306a23846d..7fc9c703c3 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java @@ -7,5 +7,5 @@ package com.newrelic.agent.bridge.messaging; public class JmsProperties { - public static final String NR_JMS_HOST_AND_PORT_PROPERTY = "com.nr.agent.instrumentation.jms.HostAndPort"; + public static final String NR_JMS_HOST_AND_PORT_PROPERTY = "com.nr.agent.instrumentation.jms.BrokerInstance"; } diff --git a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java index 91a4754a0a..8b32571a19 100644 --- a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java +++ b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java @@ -2087,8 +2087,7 @@ private void runTestMessagingAPIWithHostAndPort() { .destinationType(DestinationType.NAMED_QUEUE) .destinationName("Message Destination") .outboundHeaders(outboundRequestWrapper) - .host(myURL.getHost()) - .port(myURL.getPort()) + .instance(myURL.getHost(), myURL.getPort()) .build(); NewRelic.getAgent().getTracedMethod().reportAsExternal(messageProduceParameters); @@ -2104,8 +2103,7 @@ private void runTestMessagingAPIWithHostAndPort() { .destinationType(DestinationType.TEMP_QUEUE) .destinationName("Message Destination") .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) - .host(myURL.getHost()) - .port(myURL.getPort()) + .hostAndPort(myURL.getHost(), myURL.getPort()) .build(); NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java index c5c11852cb..72b4e4e8c6 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQUtil.java @@ -7,7 +7,7 @@ package com.nr.agent.instrumentation.activemqclient580; import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.agent.bridge.messaging.BrokerInstance; import java.util.function.Function; import java.util.regex.Matcher; @@ -23,17 +23,17 @@ public static ActiveMQUtil get() { private final Pattern addressPattern = Pattern.compile("^\\w+://(.+)/.+:(\\d+)"); - public HostAndPort parseHostAndPort(String address) { + public BrokerInstance parseHostAndPort(String address) { return CACHE.apply(address); } - private final Function CACHE = AgentBridge.collectionFactory.memoize(this::doParseHostAndPort, 32); + private final Function CACHE = AgentBridge.collectionFactory.memoize(this::doParseHostAndPort, 32); - public HostAndPort doParseHostAndPort(String address) { + public BrokerInstance doParseHostAndPort(String address) { Matcher m = addressPattern.matcher(address); if(!m.find()) { - return HostAndPort.empty(); + return BrokerInstance.empty(); } String hostName = m.group(1); @@ -43,9 +43,9 @@ public HostAndPort doParseHostAndPort(String address) { String portStr = m.group(2); port = Integer.parseInt(portStr); } catch (NumberFormatException e) { - return HostAndPort.empty(); + return BrokerInstance.empty(); } - return new HostAndPort(hostName, port); + return new BrokerInstance(hostName, port); } private ActiveMQUtil() { diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java index fe93c8c9eb..59d981ac2e 100644 --- a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java +++ b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java @@ -1,6 +1,6 @@ package com.nr.agent.instrumentation.activemqclient580; -import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.agent.bridge.messaging.BrokerInstance; import com.newrelic.agent.introspec.InstrumentationTestConfig; import com.newrelic.agent.introspec.InstrumentationTestRunner; import org.apache.activemq.ActiveMQConnection; @@ -41,7 +41,7 @@ public void setUp() { @Test public void fromMessageGetUnsetProperty() throws Exception { Object object = activeMQMessage.getObjectProperty("unsetProperty"); - assertFalse("Object must not be an instance of HostAndPort", object instanceof HostAndPort); + assertFalse("Object must not be an instance of BrokerInstance", object instanceof BrokerInstance); assertNull("object must be null", object); } @@ -91,11 +91,11 @@ private void setStubs(String transportString) { } private void assertMessage(String expectedHost, Integer expectedPort, ActiveMQMessage message, Integer timesGetConnectionCalled) throws JMSException { - HostAndPort hostAndPort = (HostAndPort)message.getObjectProperty(NR_JMS_HOST_AND_PORT_PROPERTY); + BrokerInstance brokerInstance = (BrokerInstance)message.getObjectProperty(NR_JMS_HOST_AND_PORT_PROPERTY); Mockito.verify(message, Mockito.times(timesGetConnectionCalled)).getConnection(); - assertNotNull("Failed to retrieve hostAndPort from ActiveMQ message", hostAndPort); - assertEquals("Expected host did not match", expectedHost, hostAndPort.getHostName()); - assertEquals("Expected port did not match", expectedPort, hostAndPort.getPort()); + assertNotNull("Failed to retrieve brokerInstance from ActiveMQ message", brokerInstance); + assertEquals("Expected host did not match", expectedHost, brokerInstance.getHostName()); + assertEquals("Expected port did not match", expectedPort, brokerInstance.getPort()); } } diff --git a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java index dc1afd93c7..d6653f7375 100644 --- a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java +++ b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java @@ -9,7 +9,7 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TracedMethod; -import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.agent.bridge.messaging.BrokerInstance; import com.newrelic.agent.bridge.messaging.JmsProperties; import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.MessageConsumeParameters; @@ -132,11 +132,9 @@ public static void processSendMessage(Message message, Destination dest, TracedM .destinationType(destinationType) .destinationName(destinationName) .outboundHeaders(new OutboundWrapper(message)); - HostAndPort hostAndPort = getHostAndPort(message); - if (hostAndPort != null) { - builder = builder - .host(hostAndPort.getHostName()) - .port(hostAndPort.getPort()); + BrokerInstance brokerInstance = getHostAndPort(message); + if (brokerInstance != null) { + builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); } tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { @@ -159,11 +157,9 @@ public static void processConsume(Message message, TracedMethod tracer) { .destinationType(destinationType) .destinationName(destinationName) .inboundHeaders(new InboundWrapper(message)); - HostAndPort hostAndPort = getHostAndPort(message); - if (hostAndPort != null) { - builder = builder - .host(hostAndPort.getHostName()) - .port(hostAndPort.getPort()); + BrokerInstance brokerInstance = getHostAndPort(message); + if (brokerInstance != null) { + builder = builder.hostAndPort(brokerInstance.getHostName(), brokerInstance.getPort()); } tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { @@ -172,10 +168,10 @@ public static void processConsume(Message message, TracedMethod tracer) { } } - private static HostAndPort getHostAndPort(Message message) throws JMSException { + private static BrokerInstance getHostAndPort(Message message) throws JMSException { Object obj = message.getObjectProperty(JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY); - if (obj instanceof HostAndPort) { - return (HostAndPort) obj; + if (obj instanceof BrokerInstance) { + return (BrokerInstance) obj; } return null; } diff --git a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java index bc3680eeec..23ceb783d9 100644 --- a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java +++ b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java @@ -9,7 +9,7 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.TracedMethod; -import com.newrelic.agent.bridge.messaging.HostAndPort; +import com.newrelic.agent.bridge.messaging.BrokerInstance; import com.newrelic.agent.bridge.messaging.JmsProperties; import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.MessageConsumeParameters; @@ -132,11 +132,9 @@ public static void processSendMessage(Message message, Destination dest, TracedM .destinationType(destinationType) .destinationName(destinationName) .outboundHeaders(new OutboundWrapper(message)); - HostAndPort hostAndPort = getHostAndPort(message); - if (hostAndPort != null) { - builder = builder - .host(hostAndPort.getHostName()) - .port(hostAndPort.getPort()); + BrokerInstance brokerInstance = getHostAndPort(message); + if (brokerInstance != null) { + builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); } tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { @@ -159,11 +157,9 @@ public static void processConsume(Message message, TracedMethod tracer) { .destinationType(destinationType) .destinationName(destinationName) .inboundHeaders(new InboundWrapper(message)); - HostAndPort hostAndPort = getHostAndPort(message); - if (hostAndPort != null) { - builder = builder - .host(hostAndPort.getHostName()) - .port(hostAndPort.getPort()); + BrokerInstance brokerInstance = getHostAndPort(message); + if (brokerInstance != null) { + builder = builder.hostAndPort(brokerInstance.getHostName(), brokerInstance.getPort()); } tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { @@ -172,10 +168,10 @@ public static void processConsume(Message message, TracedMethod tracer) { } } - private static HostAndPort getHostAndPort(Message message) throws JMSException { + private static BrokerInstance getHostAndPort(Message message) throws JMSException { Object obj = message.getObjectProperty(JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY); - if (obj instanceof HostAndPort) { - return (HostAndPort) obj; + if (obj instanceof BrokerInstance) { + return (BrokerInstance) obj; } return null; } diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index 9a3d4938cf..d611e23858 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -48,8 +48,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .host(getHost(connection)) - .port(getPort(connection)) + .instance(getHost(connection), getPort(connection)) .build()); addAttributes(routingKey, props); @@ -62,8 +61,7 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .host(getHost(connection)) - .port(getPort(connection)) + .hostAndPort(getHost(connection), getPort(connection)) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java index 4c3168cee3..6bb6a4cbd3 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -49,8 +49,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .host(getHost(connection)) - .port(getPort(connection)) + .instance(getHost(connection), getPort(connection)) .build()); addAttributes(routingKey, props); @@ -63,8 +62,7 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .host(getHost(connection)) - .port(getPort(connection)) + .hostAndPort(getHost(connection), getPort(connection)) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index 7130e096f5..d6b6ca0b38 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -47,8 +47,7 @@ public static void processSendMessage(String exchangeName, String routingKey, Ma .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .host(getHost(connection)) - .port(getPort(connection)) + .instance(getHost(connection), getPort(connection)) .build()); addAttributes(routingKey, props); @@ -61,8 +60,7 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .host(getHost(connection)) - .port(getPort(connection)) + .hostAndPort(getHost(connection), getPort(connection)) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java index 7aec547c5a..7783ef15b4 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -47,8 +47,7 @@ public static void processSendMessage(String exchangeName, String routingKey, Ma .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .host(getHost(connection)) - .port(getPort(connection)) + .instance(getHost(connection), getPort(connection)) .build()); addAttributes(routingKey, props); @@ -61,8 +60,7 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .host(getHost(connection)) - .port(getPort(connection)) + .hostAndPort(getHost(connection), getPort(connection)) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index 7430bafd8c..328558e51d 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -48,8 +48,7 @@ public static void processSendMessage(String exchangeName, String routingKey, Ma .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .host(getHost(connection)) - .port(getPort(connection)) + .instance(getHost(connection), getPort(connection)) .build()); addAttributes(routingKey, props); @@ -62,8 +61,7 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .host(getHost(connection)) - .port(getPort(connection)) + .hostAndPort(getHost(connection), getPort(connection)) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java index 5a603d409a..227a2bcc42 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java @@ -113,12 +113,8 @@ public Build cloudResourceId(String cloudResourceId) { return this; } - public Build host(String host) { + public Build hostAndPort(String host, Integer port) { this.host = host; - return this; - } - - public Build port(Integer port) { this.port = port; return this; } @@ -180,16 +176,10 @@ public interface Build { Build cloudResourceId(String cloudResourceId); /** - * Set the host name for the message queue. - * This method is optional and can be bypassed by calling build directly. - */ - Build host(String host); - - /** - * Set the port for the message queue. + * Set the host name and port number for the message queue. * This method is optional and can be bypassed by calling build directly. */ - Build port(Integer port); + Build hostAndPort(String host, Integer port); /** * Build the final {@link MessageConsumeParameters} for the API call. diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java index 6e0ec2f6da..4b0e2670d0 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java @@ -111,12 +111,8 @@ public Build cloudResourceId(String cloudResourceId) { return this; } - public Build host(String host) { + public Build instance(String host, Integer port) { this.host = host; - return this; - } - - public Build port(Integer port) { this.port = port; return this; } @@ -179,16 +175,10 @@ public interface Build { Build cloudResourceId(String cloudResourceId); /** - * Set the host name for the message queue. - * This method is optional and can be bypassed by calling build directly. - */ - Build host(String host); - - /** - * Set the port for the message queue. + * Set the host name and port number for the message queue. * This method is optional and can be bypassed by calling build directly. */ - Build port(Integer port); + Build instance(String host, Integer port); /** * Build the final {@link MessageProduceParameters} for the API call. From ef102b54a0debc4877118e1a73c97231f285722c Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 20 May 2024 18:03:03 -0400 Subject: [PATCH 29/42] Update newrelic.yml --- newrelic-agent/src/main/resources/newrelic.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/newrelic-agent/src/main/resources/newrelic.yml b/newrelic-agent/src/main/resources/newrelic.yml index 259456e73f..7ebd6bdf4d 100644 --- a/newrelic-agent/src/main/resources/newrelic.yml +++ b/newrelic-agent/src/main/resources/newrelic.yml @@ -452,6 +452,12 @@ common: &default_settings # with every SlowTransaction event, if a large number of transaction exceed the threshold. evaluate_completed_transactions: false + # Configuration settings for reporting external calls to/from message brokers + message_broker_tracer: + # Determines if the message broker endpoint (host & port) is reported. By default it is enabled. + instance_reporting: + enabled: true + # Application Environments # ------------------------------------------ # Environment specific settings are in this section. From 3d59d6145267b3a739b5b3bc690d88deaa898409 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Tue, 21 May 2024 14:52:36 -0400 Subject: [PATCH 30/42] Simplify instance metric --- .../test/newrelic/test/agent/api/ApiTest.java | 4 +- .../ActiveMQMessageTest.java | 18 ++-- .../java/com/newrelic/agent/MetricNames.java | 2 - .../agent/messaging/MessageMetrics.java | 31 ++----- .../newrelic/agent/tracers/DefaultTracer.java | 5 +- .../agent/messaging/MessageMetricsTest.java | 93 ++++++++++--------- 6 files changed, 72 insertions(+), 81 deletions(-) diff --git a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java index 8b32571a19..b989dc872e 100644 --- a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java +++ b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java @@ -1994,7 +1994,7 @@ public void testMessagingAPI() throws Exception { server.start(); runTestMessagingAPI(); String messageBrokerMetric = "MessageBroker/JMS/Queue/Consume/Temp"; - String endpointMetric = "MessageBroker/instance/JMS/unknown/unknown/Consume/Queue/Temp"; + String endpointMetric = "MessageBroker/instance/unknown/unknown/Queue/Temp"; Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); } catch (IOException e) { @@ -2017,7 +2017,7 @@ public void testMessagingAPIWithHostAndPort() throws Exception { server.start(); runTestMessagingAPIWithHostAndPort(); String messageBrokerMetric = "MessageBroker/JMS/Queue/Consume/Temp"; - String endpointMetric = String.format("MessageBroker/instance/JMS/%s/8088/Consume/Queue/Temp", HOSTNAME); + String endpointMetric = String.format("MessageBroker/instance/%s/8088/Queue/Temp", HOSTNAME); Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); } catch (IOException e) { diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java index 59d981ac2e..0fddc1d41c 100644 --- a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java +++ b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java @@ -9,7 +9,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; import javax.jms.JMSException; @@ -18,6 +17,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "org.apache" }) @@ -28,14 +32,14 @@ public class ActiveMQMessageTest { @Before public void setUp() { - ActiveMQConnection connection = Mockito.mock(ActiveMQConnection.class); - transport = Mockito.mock(Transport.class); + ActiveMQConnection connection = mock(ActiveMQConnection.class); + transport = mock(Transport.class); - Mockito.when(connection.getTransport()).thenReturn(transport); + when(connection.getTransport()).thenReturn(transport); ActiveMQMessage message = new ActiveMQMessage(); message.setConnection(connection); - activeMQMessage = Mockito.spy(message); + activeMQMessage = spy(message); } @Test @@ -87,12 +91,12 @@ public void fromMessageGetHostAndPortWithLocalHost() throws Exception { } private void setStubs(String transportString) { - Mockito.when(transport.toString()).thenReturn(transportString); + when(transport.toString()).thenReturn(transportString); } private void assertMessage(String expectedHost, Integer expectedPort, ActiveMQMessage message, Integer timesGetConnectionCalled) throws JMSException { BrokerInstance brokerInstance = (BrokerInstance)message.getObjectProperty(NR_JMS_HOST_AND_PORT_PROPERTY); - Mockito.verify(message, Mockito.times(timesGetConnectionCalled)).getConnection(); + verify(message, times(timesGetConnectionCalled)).getConnection(); assertNotNull("Failed to retrieve brokerInstance from ActiveMQ message", brokerInstance); assertEquals("Expected host did not match", expectedHost, brokerInstance.getHostName()); assertEquals("Expected port did not match", expectedPort, brokerInstance.getPort()); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/MetricNames.java b/newrelic-agent/src/main/java/com/newrelic/agent/MetricNames.java index 484ac49bd8..0a426c81b7 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/MetricNames.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/MetricNames.java @@ -320,8 +320,6 @@ public class MetricNames { public static final String MESSAGE_BROKER_CONSUME_NAMED = "MessageBroker/{0}/{1}/Consume/Named/{2}"; public static final String MESSAGE_BROKER_CONSUME_TEMP = "MessageBroker/{0}/{1}/Consume/Temp"; - public static final String MESSAGE_BROKER_INSTANCE = "MessageBroker/Instance/{0}/{1}"; - // API tracking supportability metrics that include API source. e.g. Supportability/API/Ignore/{source} // tokens public static final String SUPPORTABILITY_API_TOKEN = "Token"; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index c0c0b96d85..f30895edb0 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -18,47 +18,43 @@ public class MessageMetrics { public static final String MESSAGE_BROKER_INSTANCE = METRIC_NAMESPACE + "/instance/"; public static final String UNKNOWN = "unknown"; - - public static final String PRODUCE = "Produce"; - public static final String CONSUME = "Consume"; public static String HOSTNAME = Hostname.getHostname(ServiceFactory.getConfigService().getDefaultAgentConfig()); public static boolean isAnyEndpointParamsKnown(String host, Integer port) { return !(isParamUnknown(host) && isParamUnknown(port)); } - public static void collectMessageProducerRollupMetrics(TracedMethod method, String library, String host, Integer port, + public static void collectMessageProducerRollupMetrics(TracedMethod method, String host, Integer port, DestinationType destinationType, String destinationName) { - reportInstanceIfEnabled(method, library, PRODUCE, host, port, destinationType, destinationName); + reportInstanceIfEnabled(method, host, port, destinationType, destinationName); } - public static void collectMessageConsumerRollupMetrics(TracedMethod method, String library, String host, Integer port, + public static void collectMessageConsumerRollupMetrics(TracedMethod method, String host, Integer port, DestinationType destinationType, String destinationName) { - reportInstanceIfEnabled(method, library, CONSUME, host, port, destinationType, destinationName); + reportInstanceIfEnabled(method, host, port, destinationType, destinationName); } - public static void reportInstanceIfEnabled(TracedMethod method, String library, String operation, String host, Integer port, + public static void reportInstanceIfEnabled(TracedMethod method, String host, Integer port, DestinationType destinationType, String destinationName) { MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); if (messageBrokerConfig.isInstanceReportingEnabled()) { - String metric = buildInstanceMetric(library, host, port, operation, destinationType, destinationName); + String metric = buildInstanceMetric(host, port, destinationType, destinationName); method.addRollupMetricName(metric); } } - public static String buildInstanceMetric(String library, String host, Integer port, String operation, + public static String buildInstanceMetric(String host, Integer port, DestinationType destinationType, String destinationName) { - String instance = buildInstanceIdentifier(library, host, port, operation, destinationType, destinationName); + String instance = buildInstanceIdentifier(host, port, destinationType, destinationName); return MESSAGE_BROKER_INSTANCE + instance; } - public static String buildInstanceIdentifier(String library, String host, Integer port, String operation, + public static String buildInstanceIdentifier(String host, Integer port, DestinationType destinationType, String destinationName) { - String libraryName = replaceLibrary(library); String hostname = replaceLocalhost(host); String portName = replacePort(port); String parsedDestinationName = replaceDestinationName(destinationType, destinationName); - return libraryName + SLASH + hostname + SLASH + portName + SLASH + operation + SLASH + destinationType.getTypeName() + SLASH + parsedDestinationName; + return hostname + SLASH + portName + SLASH + destinationType.getTypeName() + SLASH + parsedDestinationName; } public static String replaceDestinationName(DestinationType destinationType, String destinationName) { @@ -71,13 +67,6 @@ public static String replaceDestinationName(DestinationType destinationType, Str return "Named" + SLASH + destinationName; } - public static String replaceLibrary(String library) { - if (isParamUnknown(library)) { - return UNKNOWN; - } - return library; - } - public static String replaceLocalhost(String host) { if (isParamUnknown(host)) { return UNKNOWN; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 2c6ad6da52..a67dcda756 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -16,7 +16,6 @@ import com.newrelic.agent.bridge.external.ExternalMetrics; import com.newrelic.agent.config.AgentConfigImpl; import com.newrelic.agent.config.DatastoreConfig; -import com.newrelic.agent.config.MessageBrokerConfig; import com.newrelic.agent.config.TransactionTracerConfig; import com.newrelic.agent.database.DatastoreMetrics; import com.newrelic.agent.database.SqlObfuscator; @@ -827,7 +826,7 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP String host = messageProduceParameters.getHost(); Integer port = messageProduceParameters.getPort(); - MessageMetrics.collectMessageProducerRollupMetrics(this, library, host, port, destinationType, messageProduceParameters.getDestinationName()); + MessageMetrics.collectMessageProducerRollupMetrics(this, host, port, destinationType, messageProduceParameters.getDestinationName()); } private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeParameters) { @@ -856,7 +855,7 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP String host = messageConsumeParameters.getHost(); Integer port = messageConsumeParameters.getPort(); - MessageMetrics.collectMessageConsumerRollupMetrics(this, library, host, port, destinationType, messageConsumeParameters.getDestinationName()); + MessageMetrics.collectMessageConsumerRollupMetrics(this, host, port, destinationType, messageConsumeParameters.getDestinationName()); } private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java index 9da5c44587..6580c0893b 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java @@ -11,7 +11,6 @@ import com.newrelic.api.agent.TracedMethod; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import java.net.InetAddress; import java.net.UnknownHostException; @@ -20,6 +19,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; public class MessageMetricsTest { @@ -34,7 +35,7 @@ public MessageMetricsTest() throws UnknownHostException { @Before public void before() throws Exception { AgentHelper.bootstrap(AgentHelper.createAgentConfig(true)); - mockTracedMethod = Mockito.mock(TracedMethod.class); + mockTracedMethod = mock(TracedMethod.class); } @Test @@ -74,87 +75,87 @@ public void testIsAnyEndpointParamsKnown() { @Test public void testCollectMessageProducerRollupMetrics() { - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "localhost", + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", 8080, DestinationType.NAMED_TOPIC, "topic"); - Mockito.verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Produce/Topic/Named/topic", hostName)); + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName)); - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "localhost", + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", 8080, DestinationType.NAMED_QUEUE, "queue"); - Mockito.verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Produce/Queue/Named/queue", hostName)); + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue", hostName)); - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "localhost", + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", 8080, DestinationType.TEMP_TOPIC, null); - Mockito.verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Produce/Topic/Temp", hostName)); + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Temp", hostName)); - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "localhost", + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", 8080, DestinationType.TEMP_QUEUE, null); - Mockito.verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Produce/Queue/Temp", hostName)); + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Temp", hostName)); - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "example.com", + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", 8080, DestinationType.NAMED_QUEUE, "queue"); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/8080/Produce/Queue/Named/queue"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Queue/Named/queue"); - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", null, + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, 8080, DestinationType.EXCHANGE, "queue"); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/unknown/8080/Produce/Exchange/Named/queue"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/queue"); - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "example.com", + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", null, DestinationType.NAMED_QUEUE, "queue"); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/unknown/Produce/Queue/Named/queue"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/unknown/Queue/Named/queue"); - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, null, + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, null, DestinationType.EXCHANGE, null); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/unknown/Produce/Exchange/unknown"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/Exchange/unknown"); - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "JMS", "example.com", + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", 8080, DestinationType.EXCHANGE, null); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/8080/Produce/Exchange/unknown"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Exchange/unknown"); } @Test public void testCollectMessageConsumerRollupMetrics() { - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "localhost", + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", 8080, DestinationType.NAMED_TOPIC, "topic"); - Mockito.verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Consume/Topic/Named/topic", hostName)); + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName)); - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "localhost", + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", 8080, DestinationType.NAMED_QUEUE, "queue"); - Mockito.verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Consume/Queue/Named/queue", hostName)); + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue", hostName)); - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "localhost", + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", 8080, DestinationType.TEMP_TOPIC, null); - Mockito.verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Consume/Topic/Temp", hostName)); + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Temp", hostName)); - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "localhost", + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", 8080, DestinationType.TEMP_QUEUE, null); - Mockito.verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/JMS/{0}/8080/Consume/Queue/Temp", hostName)); + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Temp", hostName)); - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "example.com", + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", 8080, DestinationType.NAMED_QUEUE, "queue"); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/8080/Consume/Queue/Named/queue"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Queue/Named/queue"); - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", null, + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, 8080, DestinationType.EXCHANGE, "queue"); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/unknown/8080/Consume/Exchange/Named/queue"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/queue"); - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "example.com", + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", null, DestinationType.NAMED_QUEUE, "queue"); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/unknown/Consume/Queue/Named/queue"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/unknown/Queue/Named/queue"); - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, null, + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, null, DestinationType.EXCHANGE, null); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/unknown/Consume/Exchange/unknown"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/Exchange/unknown"); - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "JMS", "example.com", + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", 8080, DestinationType.EXCHANGE, null); - Mockito.verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/JMS/example.com/8080/Consume/Exchange/unknown"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Exchange/unknown"); } } From a4e8bb5604244073ee4ce0c1f7947d9ecd4a1a94 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Tue, 21 May 2024 16:56:53 -0400 Subject: [PATCH 31/42] Add new metrics for rabbitmq --- .../external/MessageConsumeParameters.java | 2 +- .../external/MessageProduceParameters.java | 2 +- .../test/newrelic/test/agent/api/ApiTest.java | 142 +++++++++++++++++- .../instrumentation/jms11/JmsMetricUtil.java | 2 +- .../instrumentation/jms3/JmsMetricUtil.java | 2 +- .../rabbitamqp172/RabbitAMQPMetricUtil.java | 4 +- .../rabbitamqp241/RabbitAMQPMetricUtil.java | 4 +- .../rabbitamqp250/RabbitAMQPMetricUtil.java | 4 +- .../rabbitamqp270/RabbitAMQPMetricUtil.java | 4 +- .../rabbitamqp500/RabbitAMQPMetricUtil.java | 4 +- .../agent/messaging/MessageMetrics.java | 40 ++++- .../newrelic/agent/tracers/DefaultTracer.java | 23 +-- .../agent/messaging/MessageMetricsTest.java | 64 +++++--- .../api/agent/MessageConsumeParameters.java | 37 ++++- .../api/agent/MessageProduceParameters.java | 24 ++- 15 files changed, 306 insertions(+), 52 deletions(-) diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java index 7024ae0dc6..adf4b4082e 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java @@ -26,7 +26,7 @@ public class MessageConsumeParameters extends com.newrelic.api.agent.MessageCons @Deprecated protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, InboundHeaders inboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null, null, null); + super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null, null, null, null, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java index f5560508a1..d1e2771dc1 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java @@ -26,7 +26,7 @@ public class MessageProduceParameters extends com.newrelic.api.agent.MessageProd @Deprecated protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, OutboundHeaders outboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null, null, null); + super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null, null, null, null); } } diff --git a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java index b989dc872e..f24131cec8 100644 --- a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java +++ b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java @@ -2030,6 +2030,56 @@ public void testMessagingAPIWithHostAndPort() throws Exception { } } + @Test + public void testMessagingAPIWithAmqpQueueEndpoint() throws Exception { + // override default agent config to disabled distributed tracing and use CAT instead + EnvironmentHolder holder = setupEnvironmentHolder(CAT_CONFIG_FILE, "cat_enabled_dt_disabled_test"); + MessagingTestServer server = new MessagingTestServer(8088); + + try { + server.start(); + runTestMessagingAPIWithAmqpQueueEndpoint(); + String messageBrokerMetric = "MessageBroker/RabbitMQ/Exchange/Consume/Named/SomeExchange"; + String endpointMetric = String.format("MessageBroker/instance/%s/8088/Exchange/Named/SomeExchange", HOSTNAME); + String amqpEndpointMetric = String.format("MessageBroker/instance/%s/8088/Exchange/Named/SomeExchange/Queue/SomeQueue", HOSTNAME); + Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); + Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); + Assert.assertTrue("The following metric should exist: " + amqpEndpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(amqpEndpointMetric)); + } catch (IOException e) { + e.printStackTrace(); + Assert.fail(); + } finally { + Transaction.clearTransaction(); + server.closeAllConnections(); + holder.close(); + } + } + + @Test + public void testMessagingAPIAmqpRoutingKeyOnlyEndpoint() throws Exception { + // override default agent config to disabled distributed tracing and use CAT instead + EnvironmentHolder holder = setupEnvironmentHolder(CAT_CONFIG_FILE, "cat_enabled_dt_disabled_test"); + MessagingTestServer server = new MessagingTestServer(8088); + + try { + server.start(); + runTestMessagingAPIWithAmqpRoutingKeyOnlyEndpoint(); + String messageBrokerMetric = "MessageBroker/RabbitMQ/Exchange/Consume/Named/SomeExchange"; + String endpointMetric = String.format("MessageBroker/instance/%s/8088/Exchange/Named/SomeExchange", HOSTNAME); + String amqpEndpointMetric = String.format("MessageBroker/instance/%s/8088/Exchange/Named/SomeExchange/RoutingKey/SomeRoutingKey", HOSTNAME); + Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); + Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); + Assert.assertTrue("The following metric should exist: " + amqpEndpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(amqpEndpointMetric)); + } catch (IOException e) { + e.printStackTrace(); + Assert.fail(); + } finally { + Transaction.clearTransaction(); + server.closeAllConnections(); + holder.close(); + } + } + @Trace(dispatcher = true) private void runTestMessagingAPI() { URL myURL = null; @@ -2103,7 +2153,97 @@ private void runTestMessagingAPIWithHostAndPort() { .destinationType(DestinationType.TEMP_QUEUE) .destinationName("Message Destination") .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) - .hostAndPort(myURL.getHost(), myURL.getPort()) + .instance(myURL.getHost(), myURL.getPort()) + .build(); + NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); + + Assert.assertTrue(response.getHeaders("NewRelicAppData").length != 0); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Trace(dispatcher = true) + private void runTestMessagingAPIWithAmqpQueueEndpoint() { + URL myURL = null; + try { + Thread.sleep(600); + myURL = new URL("http://localhost:8088"); + HttpUriRequest request = RequestBuilder.get().setUri(myURL.toURI()).build(); + + ApiTestHelper.OutboundWrapper outboundRequestWrapper = new ApiTestHelper.OutboundWrapper(request, HeaderType.MESSAGE); + + // MessageProducer + ExternalParameters messageProduceParameters = MessageProduceParameters + .library("RabbitMQ") + .destinationType(DestinationType.EXCHANGE) + .destinationName("SomeExchange") + .outboundHeaders(outboundRequestWrapper) + .instance(myURL.getHost(), myURL.getPort()) + .amqp("SomeQueue") + .build(); + NewRelic.getAgent().getTracedMethod().reportAsExternal(messageProduceParameters); + + Assert.assertTrue(request.getHeaders("NewRelicID").length != 0); + Assert.assertTrue(request.getHeaders("NewRelicTransaction").length != 0); + + CloseableHttpClient connection = HttpClientBuilder.create().build(); + CloseableHttpResponse response = connection.execute(request); + + // MessageConsumer + ExternalParameters messageResponseParameters = MessageConsumeParameters + .library("RabbitMQ") + .destinationType(DestinationType.EXCHANGE) + .destinationName("SomeExchange") + .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) + .instance(myURL.getHost(), myURL.getPort()) + .amqp("SomeQueue", "SomeRoutingkey") + .build(); + NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); + + Assert.assertTrue(response.getHeaders("NewRelicAppData").length != 0); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Trace(dispatcher = true) + private void runTestMessagingAPIWithAmqpRoutingKeyOnlyEndpoint() { + URL myURL = null; + try { + Thread.sleep(600); + myURL = new URL("http://localhost:8088"); + HttpUriRequest request = RequestBuilder.get().setUri(myURL.toURI()).build(); + + ApiTestHelper.OutboundWrapper outboundRequestWrapper = new ApiTestHelper.OutboundWrapper(request, HeaderType.MESSAGE); + + // MessageProducer + ExternalParameters messageProduceParameters = MessageProduceParameters + .library("RabbitMQ") + .destinationType(DestinationType.EXCHANGE) + .destinationName("SomeExchange") + .outboundHeaders(outboundRequestWrapper) + .instance(myURL.getHost(), myURL.getPort()) + .amqp("SomeRoutingKey") + .build(); + NewRelic.getAgent().getTracedMethod().reportAsExternal(messageProduceParameters); + + Assert.assertTrue(request.getHeaders("NewRelicID").length != 0); + Assert.assertTrue(request.getHeaders("NewRelicTransaction").length != 0); + + CloseableHttpClient connection = HttpClientBuilder.create().build(); + CloseableHttpResponse response = connection.execute(request); + + // MessageConsumer + ExternalParameters messageResponseParameters = MessageConsumeParameters + .library("RabbitMQ") + .destinationType(DestinationType.EXCHANGE) + .destinationName("SomeExchange") + .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) + .instance(myURL.getHost(), myURL.getPort()) + .amqp(null, "SomeRoutingKey") .build(); NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); diff --git a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java index d6653f7375..d9dac0124e 100644 --- a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java +++ b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java @@ -159,7 +159,7 @@ public static void processConsume(Message message, TracedMethod tracer) { .inboundHeaders(new InboundWrapper(message)); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - builder = builder.hostAndPort(brokerInstance.getHostName(), brokerInstance.getPort()); + builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); } tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { diff --git a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java index 23ceb783d9..19fc51c39f 100644 --- a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java +++ b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java @@ -159,7 +159,7 @@ public static void processConsume(Message message, TracedMethod tracer) { .inboundHeaders(new InboundWrapper(message)); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - builder = builder.hostAndPort(brokerInstance.getHostName(), brokerInstance.getPort()); + builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); } tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index d611e23858..b015cf9fb3 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -49,6 +49,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) .instance(getHost(connection), getPort(connection)) + .amqp(routingKey) .build()); addAttributes(routingKey, props); @@ -61,7 +62,8 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .hostAndPort(getHost(connection), getPort(connection)) + .instance(getHost(connection), getPort(connection)) + .amqp(queueName, routingKey) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java index 6bb6a4cbd3..1dbf500615 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -50,6 +50,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) .instance(getHost(connection), getPort(connection)) + .amqp(routingKey) .build()); addAttributes(routingKey, props); @@ -62,7 +63,8 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .hostAndPort(getHost(connection), getPort(connection)) + .instance(getHost(connection), getPort(connection)) + .amqp(queueName, routingKey) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index d6b6ca0b38..767baa76fc 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -48,6 +48,7 @@ public static void processSendMessage(String exchangeName, String routingKey, Ma .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) .instance(getHost(connection), getPort(connection)) + .amqp(routingKey) .build()); addAttributes(routingKey, props); @@ -60,7 +61,8 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .hostAndPort(getHost(connection), getPort(connection)) + .instance(getHost(connection), getPort(connection)) + .amqp(queueName, routingKey) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java index 7783ef15b4..0ddd827414 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -48,6 +48,7 @@ public static void processSendMessage(String exchangeName, String routingKey, Ma .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) .instance(getHost(connection), getPort(connection)) + .amqp(routingKey) .build()); addAttributes(routingKey, props); @@ -60,7 +61,8 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .hostAndPort(getHost(connection), getPort(connection)) + .instance(getHost(connection), getPort(connection)) + .amqp(queueName, routingKey) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index 328558e51d..e846dc6757 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -49,6 +49,7 @@ public static void processSendMessage(String exchangeName, String routingKey, Ma .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) .instance(getHost(connection), getPort(connection)) + .amqp(routingKey) .build()); addAttributes(routingKey, props); @@ -61,7 +62,8 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .hostAndPort(getHost(connection), getPort(connection)) + .instance(getHost(connection), getPort(connection)) + .amqp(queueName, routingKey) .build()); addConsumeAttributes(queueName, routingKey, properties); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index f30895edb0..43ea94e85c 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -17,6 +17,8 @@ public class MessageMetrics { public static final String SLASH = "/"; public static final String MESSAGE_BROKER_INSTANCE = METRIC_NAMESPACE + "/instance/"; + public static final String ROUTING_KEY = "RoutingKey"; + public static final String UNKNOWN = "unknown"; public static String HOSTNAME = Hostname.getHostname(ServiceFactory.getConfigService().getDefaultAgentConfig()); @@ -24,21 +26,25 @@ public static boolean isAnyEndpointParamsKnown(String host, Integer port) { return !(isParamUnknown(host) && isParamUnknown(port)); } public static void collectMessageProducerRollupMetrics(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName) { - reportInstanceIfEnabled(method, host, port, destinationType, destinationName); + DestinationType destinationType, String destinationName, String amqpRoutingKey) { + reportInstanceIfEnabled(method, host, port, destinationType, destinationName, null, amqpRoutingKey); } public static void collectMessageConsumerRollupMetrics(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName) { - reportInstanceIfEnabled(method, host, port, destinationType, destinationName); + DestinationType destinationType, String destinationName, String amqpQueueName, String amqpRoutingKey) { + reportInstanceIfEnabled(method, host, port, destinationType, destinationName, amqpQueueName, amqpRoutingKey); } public static void reportInstanceIfEnabled(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName) { + DestinationType destinationType, String destinationName, String amqpQueueName, String amqpRoutingKey) { MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); if (messageBrokerConfig.isInstanceReportingEnabled()) { - String metric = buildInstanceMetric(host, port, destinationType, destinationName); - method.addRollupMetricName(metric); + String instanceMetric = buildInstanceMetric(host, port, destinationType, destinationName); + method.addRollupMetricName(instanceMetric); + if (isAmqpCall(destinationType, amqpQueueName, amqpRoutingKey)) { + String amqpInstance = buildAmqpInstanceMetric(instanceMetric, destinationType, amqpQueueName, amqpRoutingKey); + method.addRollupMetricName(amqpInstance); + } } } @@ -57,6 +63,22 @@ public static String buildInstanceIdentifier(String host, Integer port, return hostname + SLASH + portName + SLASH + destinationType.getTypeName() + SLASH + parsedDestinationName; } + public static String buildAmqpInstanceMetric(String instanceMetric, DestinationType destinationType, String amqpQueueName, String amqpRoutingKey) { + String amqpSuffix = buildAmqpInstance(destinationType, amqpQueueName, amqpRoutingKey); + return instanceMetric + SLASH + amqpSuffix; + } + + public static String buildAmqpInstance(DestinationType destinationType, String amqpQueueName, String amqpRoutingKey) { + if (DestinationType.EXCHANGE.equals(destinationType)) { + if (!isParamUnknown(amqpQueueName)) { + return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + amqpQueueName; + } else if (!isParamUnknown(amqpRoutingKey)) { + return ROUTING_KEY + SLASH + amqpRoutingKey; + } + } + return ""; + } + public static String replaceDestinationName(DestinationType destinationType, String destinationName) { if (destinationType == DestinationType.TEMP_QUEUE || destinationType == DestinationType.TEMP_TOPIC) { return "Temp"; @@ -88,6 +110,10 @@ public static String replacePort(Integer port) { return String.valueOf(port); } + private static boolean isAmqpCall(DestinationType destinationType, String amqpQueueName, String amqpRoutingKey) { + return DestinationType.EXCHANGE.equals(destinationType) && !(isParamUnknown(amqpQueueName) && isParamUnknown(amqpRoutingKey)); + } + private static boolean isParamUnknown(String str) { return str == null || str.isEmpty(); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index a67dcda756..9efc2f9b86 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -822,11 +822,12 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP messageProduceParameters.getDestinationType().getTypeName())); } - String library = messageProduceParameters.getLibrary(); - String host = messageProduceParameters.getHost(); - Integer port = messageProduceParameters.getPort(); - - MessageMetrics.collectMessageProducerRollupMetrics(this, host, port, destinationType, messageProduceParameters.getDestinationName()); + MessageMetrics.collectMessageProducerRollupMetrics(this, + messageProduceParameters.getHost(), + messageProduceParameters.getPort(), + destinationType, + messageProduceParameters.getDestinationName(), + messageProduceParameters.getAmqpRoutingKey()); } private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeParameters) { @@ -851,11 +852,13 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageConsumeParameters.getCloudResourceId()); } - String library = messageConsumeParameters.getLibrary(); - String host = messageConsumeParameters.getHost(); - Integer port = messageConsumeParameters.getPort(); - - MessageMetrics.collectMessageConsumerRollupMetrics(this, host, port, destinationType, messageConsumeParameters.getDestinationName()); + MessageMetrics.collectMessageConsumerRollupMetrics(this, + messageConsumeParameters.getHost(), + messageConsumeParameters.getPort(), + destinationType, + messageConsumeParameters.getDestinationName(), + messageConsumeParameters.getAmqpQueue(), + messageConsumeParameters.getAmqpRoutingKey()); } private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java index 6580c0893b..a27d3b5516 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java @@ -76,86 +76,112 @@ public void testIsAnyEndpointParamsKnown() { @Test public void testCollectMessageProducerRollupMetrics() { MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_TOPIC, "topic"); + 8080, DestinationType.NAMED_TOPIC, "topic", null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_QUEUE, "queue"); + 8080, DestinationType.NAMED_QUEUE, "queue", null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_TOPIC, null); + 8080, DestinationType.NAMED_QUEUE, "queue2", "routingKey"); + // Expect routing key is ignored as we already have a queue + verify(mockTracedMethod).addRollupMetricName( + MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue2", hostName)); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", + 8080, DestinationType.TEMP_TOPIC, null, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Temp", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_QUEUE, null); + 8080, DestinationType.TEMP_QUEUE, null, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Temp", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.NAMED_QUEUE, "queue"); + 8080, DestinationType.NAMED_QUEUE, "queue", null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Queue/Named/queue"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "queue"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/queue"); + 8080, DestinationType.EXCHANGE, "exchange", null); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange"); + + MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, + 8080, DestinationType.EXCHANGE, "exchange2", "someKey"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2/RoutingKey/someKey"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - null, DestinationType.NAMED_QUEUE, "queue"); + null, DestinationType.NAMED_QUEUE, "queue", null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/unknown/Queue/Named/queue"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, - null, DestinationType.EXCHANGE, null); + null, DestinationType.EXCHANGE, null, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/Exchange/unknown"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.EXCHANGE, null); + 8080, DestinationType.EXCHANGE, null, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Exchange/unknown"); } @Test public void testCollectMessageConsumerRollupMetrics() { MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_TOPIC, "topic"); + 8080, DestinationType.NAMED_TOPIC, "topic", null, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName)); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_QUEUE, "queue"); + 8080, DestinationType.NAMED_QUEUE, "queue", null, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue", hostName)); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_TOPIC, null); + 8080, DestinationType.TEMP_TOPIC, null, null, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Temp", hostName)); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_QUEUE, null); + 8080, DestinationType.TEMP_QUEUE, null, null, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Temp", hostName)); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.NAMED_QUEUE, "queue"); + 8080, DestinationType.NAMED_QUEUE, "queue", null, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Queue/Named/queue"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "queue"); + 8080, DestinationType.EXCHANGE, "queue", null, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/queue"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - null, DestinationType.NAMED_QUEUE, "queue"); + null, DestinationType.NAMED_QUEUE, "queue", null, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/unknown/Queue/Named/queue"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - null, DestinationType.EXCHANGE, null); + null, DestinationType.EXCHANGE, null, null, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/Exchange/unknown"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.EXCHANGE, null); + 8080, DestinationType.EXCHANGE, null, null, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Exchange/unknown"); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, + 8080, DestinationType.EXCHANGE, "exchange2", "someQueue", "someKey"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2/Queue/someQueue"); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, + 8080, DestinationType.EXCHANGE, "exchange3", "someQueue", null); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange3"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange3/Queue/someQueue"); + + MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, + 8080, DestinationType.EXCHANGE, "exchange4", null, "someKey"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange4"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange4/RoutingKey/someKey"); } } diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java index 227a2bcc42..ec21dd0676 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java @@ -23,9 +23,11 @@ public class MessageConsumeParameters implements ExternalParameters { private final String cloudResourceId; private final String host; private final Integer port; + private final String amqpRoutingKey; + private final String amqpQueue; protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, - InboundHeaders inboundHeaders, String cloudResourceId, String host, Integer port) { + InboundHeaders inboundHeaders, String cloudResourceId, String host, Integer port, String amqpRoutingKey, String amqpQueue) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; @@ -33,6 +35,8 @@ protected MessageConsumeParameters(String library, DestinationType destinationTy this.cloudResourceId = cloudResourceId; this.host = host; this.port = port; + this.amqpRoutingKey = amqpRoutingKey; + this.amqpQueue = amqpQueue; } @@ -49,6 +53,8 @@ protected MessageConsumeParameters(MessageConsumeParameters messageConsumeParame this.cloudResourceId = messageConsumeParameters.cloudResourceId; this.host = messageConsumeParameters.host; this.port = messageConsumeParameters.port; + this.amqpRoutingKey = messageConsumeParameters.amqpRoutingKey; + this.amqpQueue = messageConsumeParameters.amqpQueue; } public String getDestinationName() { @@ -79,6 +85,14 @@ public String getLibrary() { return library; } + public String getAmqpRoutingKey() { + return amqpRoutingKey; + } + + public String getAmqpQueue() { + return amqpQueue; + } + protected static class Builder implements DestinationTypeParameter, DestinationNameParameter, InboundHeadersParameter, Build { private String library; @@ -88,6 +102,8 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String cloudResourceId; private String host; private Integer port; + private String amqpRoutingKey; + private String amqpQueue; public Builder(String library) { this.library = library; @@ -113,14 +129,21 @@ public Build cloudResourceId(String cloudResourceId) { return this; } - public Build hostAndPort(String host, Integer port) { + public Build instance(String host, Integer port) { this.host = host; this.port = port; return this; } + public Build amqp(String queue, String routingKey) { + this.amqpQueue = queue; + this.amqpRoutingKey = routingKey; + return this; + } + public MessageConsumeParameters build() { - return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId, host, port); + return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, + cloudResourceId, host, port, amqpRoutingKey, amqpQueue); } } @@ -179,7 +202,13 @@ public interface Build { * Set the host name and port number for the message queue. * This method is optional and can be bypassed by calling build directly. */ - Build hostAndPort(String host, Integer port); + Build instance(String host, Integer port); + + /** + * If you are using the amqp protocol, this sets the routing key and queue. + * This method is optional and can be bypassed by calling build directly. + */ + Build amqp(String queue, String routingKey); /** * Build the final {@link MessageConsumeParameters} for the API call. diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java index 4b0e2670d0..6235d0d412 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java @@ -22,9 +22,10 @@ public class MessageProduceParameters implements ExternalParameters { private final String cloudResourceId; private final String host; private final Integer port; + private final String amqpRoutingKey; protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, - OutboundHeaders outboundHeaders, String cloudResourceId, String host, Integer port) { + OutboundHeaders outboundHeaders, String cloudResourceId, String host, Integer port, String amqpRoutingKey) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; @@ -32,6 +33,7 @@ protected MessageProduceParameters(String library, DestinationType destinationTy this.cloudResourceId = cloudResourceId; this.host = host; this.port = port; + this.amqpRoutingKey = amqpRoutingKey; } /** @@ -47,6 +49,7 @@ protected MessageProduceParameters(MessageProduceParameters messageProduceParame this.cloudResourceId = messageProduceParameters.cloudResourceId; this.host = messageProduceParameters.host; this.port = messageProduceParameters.port; + this.amqpRoutingKey = messageProduceParameters.amqpRoutingKey; } public String getDestinationName() { @@ -77,6 +80,10 @@ public String getLibrary() { return library; } + public String getAmqpRoutingKey() { + return amqpRoutingKey; + } + protected static class Builder implements DestinationTypeParameter, DestinationNameParameter, OutboundHeadersParameter, Build { private String library; @@ -86,6 +93,7 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String cloudResourceId; private String host; private Integer port; + private String amqpRoutingKey; public Builder(String library) { this.library = library; @@ -117,8 +125,13 @@ public Build instance(String host, Integer port) { return this; } + public Build amqp(String routingKey) { + this.amqpRoutingKey = routingKey; + return this; + } + public MessageProduceParameters build() { - return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId, host, port); + return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId, host, port, amqpRoutingKey); } } @@ -180,6 +193,13 @@ public interface Build { */ Build instance(String host, Integer port); + + /** + * If your external call is using the amqp protocol, this sets the routing key. + * This method is optional and can be bypassed by calling build directly. + */ + Build amqp(String routingKey); + /** * Build the final {@link MessageProduceParameters} for the API call. * From 8e0cd151863344a320fa934c928cf039657edce2 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Thu, 23 May 2024 12:52:28 -0400 Subject: [PATCH 32/42] Move reporting amqp instance to PrivateApi --- .../newrelic/agent/bridge/NoOpPrivateApi.java | 7 + .../com/newrelic/agent/bridge/PrivateApi.java | 14 ++ .../external/MessageConsumeParameters.java | 2 +- .../external/MessageProduceParameters.java | 2 +- .../agent/bridge/messaging/MessageUtils.java | 10 ++ .../test/newrelic/test/agent/api/ApiTest.java | 140 ------------------ .../graphql/helper/PrivateApiStub.java | 8 + .../rabbitamqp172/RabbitAMQPMetricUtil.java | 12 +- .../rabbitamqp241/RabbitAMQPMetricUtil.java | 12 +- .../rabbitamqp250/RabbitAMQPMetricUtil.java | 20 ++- .../rabbitamqp270/RabbitAMQPMetricUtil.java | 20 ++- .../rabbitamqp500/RabbitAMQPMetricUtil.java | 20 ++- .../com/newrelic/agent/PrivateApiImpl.java | 7 + .../agent/messaging/MessageMetrics.java | 43 +++--- .../newrelic/agent/tracers/DefaultTracer.java | 7 +- .../agent/messaging/MessageMetricsTest.java | 63 ++++---- .../api/agent/MessageConsumeParameters.java | 33 +---- .../api/agent/MessageProduceParameters.java | 25 +--- 18 files changed, 168 insertions(+), 277 deletions(-) create mode 100644 agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/MessageUtils.java diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java index cc2ec78f5e..80422a8354 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java @@ -7,6 +7,8 @@ package com.newrelic.agent.bridge; +import com.newrelic.api.agent.TracedMethod; + import javax.management.MBeanServer; import java.io.Closeable; import java.util.Map; @@ -67,6 +69,11 @@ public void setServerInfo(String dispatcherName, String version) { public void setInstanceName(String instanceName) { } + @Override + public void reportAmqpInstance(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey) { + + } + @Override public void addTracerParameter(String key, String value) { } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java index 4cfdaa4bea..bd9df90ec4 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java @@ -7,6 +7,8 @@ package com.newrelic.agent.bridge; +import com.newrelic.api.agent.TracedMethod; + import javax.management.MBeanServer; import java.io.Closeable; import java.util.Map; @@ -122,4 +124,16 @@ public interface PrivateApi { */ @Deprecated void setInstanceName(String instanceName); + + /** + * Report the instance of a message broker using the AMQP protocol + * + * @param method Traced method + * @param host Broker host name + * @param port Broker port + * @param exchangeName Name of the exchange + * @param queueName Queue name + * @param routingKey Routing key + */ + void reportAmqpInstance(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey); } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java index adf4b4082e..7024ae0dc6 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java @@ -26,7 +26,7 @@ public class MessageConsumeParameters extends com.newrelic.api.agent.MessageCons @Deprecated protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, InboundHeaders inboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null, null, null, null, null); + super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null, null, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java index d1e2771dc1..f5560508a1 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java @@ -26,7 +26,7 @@ public class MessageProduceParameters extends com.newrelic.api.agent.MessageProd @Deprecated protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, OutboundHeaders outboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null, null, null, null); + super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null, null, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/MessageUtils.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/MessageUtils.java new file mode 100644 index 0000000000..10edfb10dd --- /dev/null +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/MessageUtils.java @@ -0,0 +1,10 @@ +package com.newrelic.agent.bridge.messaging; + +import com.newrelic.api.agent.DestinationType; +import com.newrelic.api.agent.TracedMethod; + +public class MessageUtils { + public static void addAmqpRollupMetrics(TracedMethod method, String host, Integer port, + DestinationType destinationType, String destinationName) { + } +} diff --git a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java index f24131cec8..dbfe20d3bb 100644 --- a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java +++ b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java @@ -2030,56 +2030,6 @@ public void testMessagingAPIWithHostAndPort() throws Exception { } } - @Test - public void testMessagingAPIWithAmqpQueueEndpoint() throws Exception { - // override default agent config to disabled distributed tracing and use CAT instead - EnvironmentHolder holder = setupEnvironmentHolder(CAT_CONFIG_FILE, "cat_enabled_dt_disabled_test"); - MessagingTestServer server = new MessagingTestServer(8088); - - try { - server.start(); - runTestMessagingAPIWithAmqpQueueEndpoint(); - String messageBrokerMetric = "MessageBroker/RabbitMQ/Exchange/Consume/Named/SomeExchange"; - String endpointMetric = String.format("MessageBroker/instance/%s/8088/Exchange/Named/SomeExchange", HOSTNAME); - String amqpEndpointMetric = String.format("MessageBroker/instance/%s/8088/Exchange/Named/SomeExchange/Queue/SomeQueue", HOSTNAME); - Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); - Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); - Assert.assertTrue("The following metric should exist: " + amqpEndpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(amqpEndpointMetric)); - } catch (IOException e) { - e.printStackTrace(); - Assert.fail(); - } finally { - Transaction.clearTransaction(); - server.closeAllConnections(); - holder.close(); - } - } - - @Test - public void testMessagingAPIAmqpRoutingKeyOnlyEndpoint() throws Exception { - // override default agent config to disabled distributed tracing and use CAT instead - EnvironmentHolder holder = setupEnvironmentHolder(CAT_CONFIG_FILE, "cat_enabled_dt_disabled_test"); - MessagingTestServer server = new MessagingTestServer(8088); - - try { - server.start(); - runTestMessagingAPIWithAmqpRoutingKeyOnlyEndpoint(); - String messageBrokerMetric = "MessageBroker/RabbitMQ/Exchange/Consume/Named/SomeExchange"; - String endpointMetric = String.format("MessageBroker/instance/%s/8088/Exchange/Named/SomeExchange", HOSTNAME); - String amqpEndpointMetric = String.format("MessageBroker/instance/%s/8088/Exchange/Named/SomeExchange/RoutingKey/SomeRoutingKey", HOSTNAME); - Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); - Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); - Assert.assertTrue("The following metric should exist: " + amqpEndpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(amqpEndpointMetric)); - } catch (IOException e) { - e.printStackTrace(); - Assert.fail(); - } finally { - Transaction.clearTransaction(); - server.closeAllConnections(); - holder.close(); - } - } - @Trace(dispatcher = true) private void runTestMessagingAPI() { URL myURL = null; @@ -2164,96 +2114,6 @@ private void runTestMessagingAPIWithHostAndPort() { } } - @Trace(dispatcher = true) - private void runTestMessagingAPIWithAmqpQueueEndpoint() { - URL myURL = null; - try { - Thread.sleep(600); - myURL = new URL("http://localhost:8088"); - HttpUriRequest request = RequestBuilder.get().setUri(myURL.toURI()).build(); - - ApiTestHelper.OutboundWrapper outboundRequestWrapper = new ApiTestHelper.OutboundWrapper(request, HeaderType.MESSAGE); - - // MessageProducer - ExternalParameters messageProduceParameters = MessageProduceParameters - .library("RabbitMQ") - .destinationType(DestinationType.EXCHANGE) - .destinationName("SomeExchange") - .outboundHeaders(outboundRequestWrapper) - .instance(myURL.getHost(), myURL.getPort()) - .amqp("SomeQueue") - .build(); - NewRelic.getAgent().getTracedMethod().reportAsExternal(messageProduceParameters); - - Assert.assertTrue(request.getHeaders("NewRelicID").length != 0); - Assert.assertTrue(request.getHeaders("NewRelicTransaction").length != 0); - - CloseableHttpClient connection = HttpClientBuilder.create().build(); - CloseableHttpResponse response = connection.execute(request); - - // MessageConsumer - ExternalParameters messageResponseParameters = MessageConsumeParameters - .library("RabbitMQ") - .destinationType(DestinationType.EXCHANGE) - .destinationName("SomeExchange") - .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) - .instance(myURL.getHost(), myURL.getPort()) - .amqp("SomeQueue", "SomeRoutingkey") - .build(); - NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); - - Assert.assertTrue(response.getHeaders("NewRelicAppData").length != 0); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(); - } - } - - @Trace(dispatcher = true) - private void runTestMessagingAPIWithAmqpRoutingKeyOnlyEndpoint() { - URL myURL = null; - try { - Thread.sleep(600); - myURL = new URL("http://localhost:8088"); - HttpUriRequest request = RequestBuilder.get().setUri(myURL.toURI()).build(); - - ApiTestHelper.OutboundWrapper outboundRequestWrapper = new ApiTestHelper.OutboundWrapper(request, HeaderType.MESSAGE); - - // MessageProducer - ExternalParameters messageProduceParameters = MessageProduceParameters - .library("RabbitMQ") - .destinationType(DestinationType.EXCHANGE) - .destinationName("SomeExchange") - .outboundHeaders(outboundRequestWrapper) - .instance(myURL.getHost(), myURL.getPort()) - .amqp("SomeRoutingKey") - .build(); - NewRelic.getAgent().getTracedMethod().reportAsExternal(messageProduceParameters); - - Assert.assertTrue(request.getHeaders("NewRelicID").length != 0); - Assert.assertTrue(request.getHeaders("NewRelicTransaction").length != 0); - - CloseableHttpClient connection = HttpClientBuilder.create().build(); - CloseableHttpResponse response = connection.execute(request); - - // MessageConsumer - ExternalParameters messageResponseParameters = MessageConsumeParameters - .library("RabbitMQ") - .destinationType(DestinationType.EXCHANGE) - .destinationName("SomeExchange") - .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) - .instance(myURL.getHost(), myURL.getPort()) - .amqp(null, "SomeRoutingKey") - .build(); - NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); - - Assert.assertTrue(response.getHeaders("NewRelicAppData").length != 0); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(); - } - } - @Test public void testNotNull() { Assert.assertNotNull(NewRelic.getAgent().getTransaction().getTracedMethod()); diff --git a/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java b/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java index 971076d338..ad837c2ab4 100644 --- a/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java +++ b/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java @@ -8,6 +8,9 @@ package com.nr.instrumentation.graphql.helper; import com.newrelic.agent.bridge.PrivateApi; +import com.newrelic.agent.messaging.MessageMetrics; +import com.newrelic.api.agent.DestinationType; +import com.newrelic.api.agent.TracedMethod; import javax.management.MBeanServer; import java.io.Closeable; @@ -96,4 +99,9 @@ public void setServerInfo(String dispatcherName, String version) { public void setInstanceName(String instanceName) { } + + @Override + public void reportAmqpInstance(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey) { + + } } diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index b015cf9fb3..1f7808d46b 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -43,28 +43,32 @@ public static void nameTransaction(String exchangeName) { public static void processSendMessage(String exchangeName, String routingKey, HashMap headers, AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(getHost(connection), getPort(connection)) - .amqp(routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); addAttributes(routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(getHost(connection), getPort(connection)) - .amqp(queueName, routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); addConsumeAttributes(queueName, routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java index 1dbf500615..462593eb0d 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -44,28 +44,32 @@ public static void nameTransaction(String exchangeName) { public static void processSendMessage(String exchangeName, String routingKey, HashMap headers, AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(getHost(connection), getPort(connection)) - .amqp(routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); addAttributes(routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(getHost(connection), getPort(connection)) - .amqp(queueName, routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); addConsumeAttributes(queueName, routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index 767baa76fc..3585701d4c 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -18,6 +18,7 @@ import java.net.InetAddress; import java.text.MessageFormat; +import java.util.HashMap; import java.util.Map; public abstract class RabbitAMQPMetricUtil { @@ -40,30 +41,35 @@ public static void nameTransaction(String exchangeName) { .setTransactionName(TransactionNamePriority.FRAMEWORK, false, MESSAGE, transactionName); } - public static void processSendMessage(String exchangeName, String routingKey, Map headers, - AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { + public static void processSendMessage(String exchangeName, String routingKey, + HashMap headers, + AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(getHost(connection), getPort(connection)) - .amqp(routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); addAttributes(routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, - AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { + AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(getHost(connection), getPort(connection)) - .amqp(queueName, routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); addConsumeAttributes(queueName, routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java index 0ddd827414..74b7a4dca6 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -18,6 +18,7 @@ import java.net.InetAddress; import java.text.MessageFormat; +import java.util.HashMap; import java.util.Map; public abstract class RabbitAMQPMetricUtil { @@ -40,30 +41,35 @@ public static void nameTransaction(String exchangeName) { .setTransactionName(TransactionNamePriority.FRAMEWORK, false, MESSAGE, transactionName); } - public static void processSendMessage(String exchangeName, String routingKey, Map headers, - AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { + public static void processSendMessage(String exchangeName, String routingKey, + HashMap headers, + AMQP.BasicProperties props, TracedMethod tracedMethod, Connection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(getHost(connection), getPort(connection)) - .amqp(routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); addAttributes(routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, - AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { + AMQP.BasicProperties properties, TracedMethod tracedMethod, Connection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(getHost(connection), getPort(connection)) - .amqp(queueName, routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); addConsumeAttributes(queueName, routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index e846dc6757..3de80681aa 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -19,6 +19,7 @@ import java.net.InetAddress; import java.text.MessageFormat; +import java.util.HashMap; import java.util.Map; public abstract class RabbitAMQPMetricUtil { @@ -41,30 +42,35 @@ public static void nameTransaction(String exchangeName) { .setTransactionName(TransactionNamePriority.FRAMEWORK, false, MESSAGE, transactionName); } - public static void processSendMessage(String exchangeName, String routingKey, Map headers, - AMQP.BasicProperties props, TracedMethod tracedMethod, AMQConnection connection) { + public static void processSendMessage(String exchangeName, String routingKey, + HashMap headers, + AMQP.BasicProperties props, TracedMethod tracedMethod, AMQConnection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(getHost(connection), getPort(connection)) - .amqp(routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); addAttributes(routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, - AMQP.BasicProperties properties, TracedMethod tracedMethod, AMQConnection connection) { + AMQP.BasicProperties properties, TracedMethod tracedMethod, AMQConnection connection) { + String host = getHost(connection); + Integer port = getPort(connection); tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(getHost(connection), getPort(connection)) - .amqp(queueName, routingKey) + .instance(host, port) .build()); + AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); addConsumeAttributes(queueName, routingKey, properties); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java index 900b821db8..88e9c90288 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java @@ -13,9 +13,11 @@ import com.newrelic.agent.bridge.PrivateApi; import com.newrelic.agent.environment.Environment; import com.newrelic.agent.jmx.JmxApiImpl; +import com.newrelic.agent.messaging.MessageMetrics; import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.util.AgentCollectionFactory; import com.newrelic.api.agent.Logger; +import com.newrelic.api.agent.TracedMethod; import javax.management.MBeanServer; import java.io.Closeable; @@ -110,6 +112,11 @@ public void setInstanceName(String instanceName) { AgentBridge.publicApi.setInstanceName(instanceName); } + @Override + public void reportAmqpInstance(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey) { + MessageMetrics.collectAmqpMetrics(method, host, port, exchangeName, queueName, routingKey); + } + /** * Allows modules to add strings to a segment in a transaction trace. */ diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index 43ea94e85c..f0c6877e29 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -26,25 +26,30 @@ public static boolean isAnyEndpointParamsKnown(String host, Integer port) { return !(isParamUnknown(host) && isParamUnknown(port)); } public static void collectMessageProducerRollupMetrics(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName, String amqpRoutingKey) { - reportInstanceIfEnabled(method, host, port, destinationType, destinationName, null, amqpRoutingKey); + DestinationType destinationType, String destinationName) { + reportInstanceIfEnabled(method, host, port, destinationType, destinationName); } public static void collectMessageConsumerRollupMetrics(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName, String amqpQueueName, String amqpRoutingKey) { - reportInstanceIfEnabled(method, host, port, destinationType, destinationName, amqpQueueName, amqpRoutingKey); + DestinationType destinationType, String destinationName) { + reportInstanceIfEnabled(method, host, port, destinationType, destinationName); + } + + public static void collectAmqpMetrics(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey) { + MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); + if (messageBrokerConfig.isInstanceReportingEnabled() && isAmqpParamsValid(queueName, routingKey)) { + String instanceMetric = buildInstanceMetric(host, port, DestinationType.EXCHANGE, exchangeName); + String amqpInstance = buildAmqpInstanceMetric(instanceMetric, queueName, routingKey); + method.addRollupMetricName(amqpInstance); + } } public static void reportInstanceIfEnabled(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName, String amqpQueueName, String amqpRoutingKey) { + DestinationType destinationType, String destinationName) { MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); if (messageBrokerConfig.isInstanceReportingEnabled()) { String instanceMetric = buildInstanceMetric(host, port, destinationType, destinationName); method.addRollupMetricName(instanceMetric); - if (isAmqpCall(destinationType, amqpQueueName, amqpRoutingKey)) { - String amqpInstance = buildAmqpInstanceMetric(instanceMetric, destinationType, amqpQueueName, amqpRoutingKey); - method.addRollupMetricName(amqpInstance); - } } } @@ -63,18 +68,16 @@ public static String buildInstanceIdentifier(String host, Integer port, return hostname + SLASH + portName + SLASH + destinationType.getTypeName() + SLASH + parsedDestinationName; } - public static String buildAmqpInstanceMetric(String instanceMetric, DestinationType destinationType, String amqpQueueName, String amqpRoutingKey) { - String amqpSuffix = buildAmqpInstance(destinationType, amqpQueueName, amqpRoutingKey); + public static String buildAmqpInstanceMetric(String instanceMetric, String queueName, String routingKey) { + String amqpSuffix = buildAmqpInstance(queueName, routingKey); return instanceMetric + SLASH + amqpSuffix; } - public static String buildAmqpInstance(DestinationType destinationType, String amqpQueueName, String amqpRoutingKey) { - if (DestinationType.EXCHANGE.equals(destinationType)) { - if (!isParamUnknown(amqpQueueName)) { - return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + amqpQueueName; - } else if (!isParamUnknown(amqpRoutingKey)) { - return ROUTING_KEY + SLASH + amqpRoutingKey; - } + public static String buildAmqpInstance(String queueName, String routingKey) { + if (!isParamUnknown(queueName)) { + return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; + } else if (!isParamUnknown(routingKey)) { + return ROUTING_KEY + SLASH + routingKey; } return ""; } @@ -110,8 +113,8 @@ public static String replacePort(Integer port) { return String.valueOf(port); } - private static boolean isAmqpCall(DestinationType destinationType, String amqpQueueName, String amqpRoutingKey) { - return DestinationType.EXCHANGE.equals(destinationType) && !(isParamUnknown(amqpQueueName) && isParamUnknown(amqpRoutingKey)); + private static boolean isAmqpParamsValid(String amqpQueueName, String amqpRoutingKey) { + return !(isParamUnknown(amqpQueueName) && isParamUnknown(amqpRoutingKey)); } private static boolean isParamUnknown(String str) { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 9efc2f9b86..cc65376a1e 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -826,8 +826,7 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP messageProduceParameters.getHost(), messageProduceParameters.getPort(), destinationType, - messageProduceParameters.getDestinationName(), - messageProduceParameters.getAmqpRoutingKey()); + messageProduceParameters.getDestinationName()); } private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeParameters) { @@ -856,9 +855,7 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP messageConsumeParameters.getHost(), messageConsumeParameters.getPort(), destinationType, - messageConsumeParameters.getDestinationName(), - messageConsumeParameters.getAmqpQueue(), - messageConsumeParameters.getAmqpRoutingKey()); + messageConsumeParameters.getDestinationName()); } private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java index a27d3b5516..977d3c125a 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java @@ -76,112 +76,121 @@ public void testIsAnyEndpointParamsKnown() { @Test public void testCollectMessageProducerRollupMetrics() { MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_TOPIC, "topic", null); + 8080, DestinationType.NAMED_TOPIC, "topic"); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_QUEUE, "queue", null); + 8080, DestinationType.NAMED_QUEUE, "queue"); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_QUEUE, "queue2", "routingKey"); + 8080, DestinationType.NAMED_QUEUE, "queue2"); // Expect routing key is ignored as we already have a queue verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue2", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_TOPIC, null, null); + 8080, DestinationType.TEMP_TOPIC, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Temp", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_QUEUE, null, null); + 8080, DestinationType.TEMP_QUEUE, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Temp", hostName)); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.NAMED_QUEUE, "queue", null); + 8080, DestinationType.NAMED_QUEUE, "queue"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Queue/Named/queue"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange", null); + 8080, DestinationType.EXCHANGE, "exchange"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange2", "someKey"); + 8080, DestinationType.EXCHANGE, "exchange2"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2/RoutingKey/someKey"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - null, DestinationType.NAMED_QUEUE, "queue", null); + null, DestinationType.NAMED_QUEUE, "queue"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/unknown/Queue/Named/queue"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, - null, DestinationType.EXCHANGE, null, null); + null, DestinationType.EXCHANGE, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/Exchange/unknown"); MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.EXCHANGE, null, null); + 8080, DestinationType.EXCHANGE, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Exchange/unknown"); } @Test public void testCollectMessageConsumerRollupMetrics() { MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_TOPIC, "topic", null, null); + 8080, DestinationType.NAMED_TOPIC, "topic"); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName)); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_QUEUE, "queue", null, null); + 8080, DestinationType.NAMED_QUEUE, "queue"); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue", hostName)); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_TOPIC, null, null, null); + 8080, DestinationType.TEMP_TOPIC, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Temp", hostName)); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_QUEUE, null, null, null); + 8080, DestinationType.TEMP_QUEUE, null); verify(mockTracedMethod).addRollupMetricName( MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Temp", hostName)); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.NAMED_QUEUE, "queue", null, null); + 8080, DestinationType.NAMED_QUEUE, "queue"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Queue/Named/queue"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "queue", null, null); + 8080, DestinationType.EXCHANGE, "queue"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/queue"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - null, DestinationType.NAMED_QUEUE, "queue", null, null); + null, DestinationType.NAMED_QUEUE, "queue"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/unknown/Queue/Named/queue"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - null, DestinationType.EXCHANGE, null, null, null); + null, DestinationType.EXCHANGE, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/Exchange/unknown"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.EXCHANGE, null, null, null); + 8080, DestinationType.EXCHANGE, null); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Exchange/unknown"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange2", "someQueue", "someKey"); + 8080, DestinationType.EXCHANGE, "exchange2"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2/Queue/someQueue"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange3", "someQueue", null); + 8080, DestinationType.EXCHANGE, "exchange3"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange3"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange3/Queue/someQueue"); MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange4", null, "someKey"); + 8080, DestinationType.EXCHANGE, "exchange4"); verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange4"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange4/RoutingKey/someKey"); + } + + @Test + public void testCollectAmqpRollupMetrics() { + MessageMetrics.collectAmqpMetrics(mockTracedMethod, "amqp.com", + 8080, "exchange2", null, "someKey"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/amqp.com/8080/Exchange/Named/exchange2/RoutingKey/someKey"); + MessageMetrics.collectAmqpMetrics(mockTracedMethod, "amqp.com", + 8080, "exchange2", "someQueue", "someKey"); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/amqp.com/8080/Exchange/Named/exchange2/Queue/someQueue"); + MessageMetrics.collectAmqpMetrics(mockTracedMethod, "amqp.com", + 8080, "exchange3", "someQueue", null); + verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/amqp.com/8080/Exchange/Named/exchange3/Queue/someQueue"); } } diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java index ec21dd0676..0df116360b 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java @@ -23,11 +23,9 @@ public class MessageConsumeParameters implements ExternalParameters { private final String cloudResourceId; private final String host; private final Integer port; - private final String amqpRoutingKey; - private final String amqpQueue; protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, - InboundHeaders inboundHeaders, String cloudResourceId, String host, Integer port, String amqpRoutingKey, String amqpQueue) { + InboundHeaders inboundHeaders, String cloudResourceId, String host, Integer port) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; @@ -35,8 +33,6 @@ protected MessageConsumeParameters(String library, DestinationType destinationTy this.cloudResourceId = cloudResourceId; this.host = host; this.port = port; - this.amqpRoutingKey = amqpRoutingKey; - this.amqpQueue = amqpQueue; } @@ -53,8 +49,6 @@ protected MessageConsumeParameters(MessageConsumeParameters messageConsumeParame this.cloudResourceId = messageConsumeParameters.cloudResourceId; this.host = messageConsumeParameters.host; this.port = messageConsumeParameters.port; - this.amqpRoutingKey = messageConsumeParameters.amqpRoutingKey; - this.amqpQueue = messageConsumeParameters.amqpQueue; } public String getDestinationName() { @@ -85,14 +79,6 @@ public String getLibrary() { return library; } - public String getAmqpRoutingKey() { - return amqpRoutingKey; - } - - public String getAmqpQueue() { - return amqpQueue; - } - protected static class Builder implements DestinationTypeParameter, DestinationNameParameter, InboundHeadersParameter, Build { private String library; @@ -102,8 +88,6 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String cloudResourceId; private String host; private Integer port; - private String amqpRoutingKey; - private String amqpQueue; public Builder(String library) { this.library = library; @@ -135,15 +119,8 @@ public Build instance(String host, Integer port) { return this; } - public Build amqp(String queue, String routingKey) { - this.amqpQueue = queue; - this.amqpRoutingKey = routingKey; - return this; - } - public MessageConsumeParameters build() { - return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, - cloudResourceId, host, port, amqpRoutingKey, amqpQueue); + return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId, host, port); } } @@ -204,12 +181,6 @@ public interface Build { */ Build instance(String host, Integer port); - /** - * If you are using the amqp protocol, this sets the routing key and queue. - * This method is optional and can be bypassed by calling build directly. - */ - Build amqp(String queue, String routingKey); - /** * Build the final {@link MessageConsumeParameters} for the API call. * diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java index 6235d0d412..d03555cc83 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java @@ -22,10 +22,8 @@ public class MessageProduceParameters implements ExternalParameters { private final String cloudResourceId; private final String host; private final Integer port; - private final String amqpRoutingKey; - protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, - OutboundHeaders outboundHeaders, String cloudResourceId, String host, Integer port, String amqpRoutingKey) { + OutboundHeaders outboundHeaders, String cloudResourceId, String host, Integer port) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; @@ -33,7 +31,6 @@ protected MessageProduceParameters(String library, DestinationType destinationTy this.cloudResourceId = cloudResourceId; this.host = host; this.port = port; - this.amqpRoutingKey = amqpRoutingKey; } /** @@ -49,7 +46,6 @@ protected MessageProduceParameters(MessageProduceParameters messageProduceParame this.cloudResourceId = messageProduceParameters.cloudResourceId; this.host = messageProduceParameters.host; this.port = messageProduceParameters.port; - this.amqpRoutingKey = messageProduceParameters.amqpRoutingKey; } public String getDestinationName() { @@ -80,10 +76,6 @@ public String getLibrary() { return library; } - public String getAmqpRoutingKey() { - return amqpRoutingKey; - } - protected static class Builder implements DestinationTypeParameter, DestinationNameParameter, OutboundHeadersParameter, Build { private String library; @@ -93,7 +85,6 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String cloudResourceId; private String host; private Integer port; - private String amqpRoutingKey; public Builder(String library) { this.library = library; @@ -125,13 +116,8 @@ public Build instance(String host, Integer port) { return this; } - public Build amqp(String routingKey) { - this.amqpRoutingKey = routingKey; - return this; - } - public MessageProduceParameters build() { - return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId, host, port, amqpRoutingKey); + return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId, host, port); } } @@ -193,13 +179,6 @@ public interface Build { */ Build instance(String host, Integer port); - - /** - * If your external call is using the amqp protocol, this sets the routing key. - * This method is optional and can be bypassed by calling build directly. - */ - Build amqp(String routingKey); - /** * Build the final {@link MessageProduceParameters} for the API call. * From c23e6df19a3bd9242a1161f9f3dc65707b2edd73 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Fri, 24 May 2024 15:24:31 -0400 Subject: [PATCH 33/42] Report msg broker inst in PrivateApi & move amqp code to instr modules --- .../newrelic/agent/bridge/NoOpPrivateApi.java | 15 +- .../com/newrelic/agent/bridge/PrivateApi.java | 36 ++-- .../external/MessageConsumeParameters.java | 2 +- .../external/MessageProduceParameters.java | 2 +- .../agent/bridge/messaging/JmsProperties.java | 2 +- .../agent/bridge/messaging/MessageUtils.java | 10 -- .../test/newrelic/test/agent/api/ApiTest.java | 11 +- .../ActiveMQMessage_Instrumentation.java | 4 +- .../ActiveMQMessageTest.java | 4 +- .../graphql/helper/PrivateApiStub.java | 16 +- .../instrumentation/jms11/JmsMetricUtil.java | 10 +- .../MessageConsumer_Instrumentation.java | 1 - .../instrumentation/jms3/JmsMetricUtil.java | 10 +- .../rabbitamqp172/RabbitAMQPMetricUtil.java | 31 +++- .../rabbitamqp241/RabbitAMQPMetricUtil.java | 32 +++- .../rabbitamqp250/RabbitAMQPMetricUtil.java | 31 +++- .../rabbitamqp270/RabbitAMQPMetricUtil.java | 31 +++- .../rabbitamqp500/RabbitAMQPMetricUtil.java | 32 +++- .../client/impl/ChannelN_Instrumentation.java | 1 - .../com/newrelic/agent/PrivateApiImpl.java | 20 ++- .../agent/messaging/MessageMetrics.java | 66 +++---- .../newrelic/agent/tracers/DefaultTracer.java | 13 -- .../agent/messaging/MessageMetricsTest.java | 162 ++++-------------- .../api/agent/MessageConsumeParameters.java | 32 +--- .../api/agent/MessageProduceParameters.java | 32 +--- 25 files changed, 282 insertions(+), 324 deletions(-) delete mode 100644 agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/MessageUtils.java diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java index 80422a8354..da1d5a46e4 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java @@ -7,7 +7,7 @@ package com.newrelic.agent.bridge; -import com.newrelic.api.agent.TracedMethod; +import com.newrelic.api.agent.DestinationType; import javax.management.MBeanServer; import java.io.Closeable; @@ -58,20 +58,25 @@ public void reportException(Throwable throwable) { } @Override - public void setAppServerPort(int port) { + public String buildMessageBrokerInstanceMetric(String host, Integer port, DestinationType destinationType, String destination) { + return null; } @Override - public void setServerInfo(String dispatcherName, String version) { + public void reportMessageBrokerInstance(String host, Integer port, DestinationType destinationType, String destination) { + } @Override - public void setInstanceName(String instanceName) { + public void setAppServerPort(int port) { } @Override - public void reportAmqpInstance(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey) { + public void setServerInfo(String dispatcherName, String version) { + } + @Override + public void setInstanceName(String instanceName) { } @Override diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java index bd9df90ec4..a6a308035a 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java @@ -7,7 +7,7 @@ package com.newrelic.agent.bridge; -import com.newrelic.api.agent.TracedMethod; +import com.newrelic.api.agent.DestinationType; import javax.management.MBeanServer; import java.io.Closeable; @@ -91,6 +91,29 @@ public interface PrivateApi { */ void reportException(Throwable throwable); + /** + * Creates a message broker instance metric if enabled via agent configuration. + * Otherwise, a null value is returned. + * + * @param host Broker host + * @param port Broker port + * @param destinationType Destination type + * @param destination Destination (can be the destination name or a more complex value related to the destination type) + */ + String buildMessageBrokerInstanceMetric(String host, Integer port, + DestinationType destinationType, String destination); + + /** + * Creates a message broker instance rollup metric if enabled via agent configuration. + * + * @param host Broker host + * @param port Broker port + * @param destinationType Destination type + * @param destination Destination (can be the destination name or a more complex value related to the destination type) + */ + void reportMessageBrokerInstance(String host, Integer port, + DestinationType destinationType, String destination); + /** * Set the app server port which is reported to RPM. * @@ -125,15 +148,4 @@ public interface PrivateApi { @Deprecated void setInstanceName(String instanceName); - /** - * Report the instance of a message broker using the AMQP protocol - * - * @param method Traced method - * @param host Broker host name - * @param port Broker port - * @param exchangeName Name of the exchange - * @param queueName Queue name - * @param routingKey Routing key - */ - void reportAmqpInstance(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey); } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java index 7024ae0dc6..98dfad581b 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java @@ -26,7 +26,7 @@ public class MessageConsumeParameters extends com.newrelic.api.agent.MessageCons @Deprecated protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, InboundHeaders inboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null, null, null); + super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java index f5560508a1..f4ed341478 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java @@ -26,7 +26,7 @@ public class MessageProduceParameters extends com.newrelic.api.agent.MessageProd @Deprecated protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, OutboundHeaders outboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null, null, null); + super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java index 7fc9c703c3..8436a23c90 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/JmsProperties.java @@ -7,5 +7,5 @@ package com.newrelic.agent.bridge.messaging; public class JmsProperties { - public static final String NR_JMS_HOST_AND_PORT_PROPERTY = "com.nr.agent.instrumentation.jms.BrokerInstance"; + public static final String NR_JMS_BROKER_INSTANCE_PROPERTY = "NR_JMS_BROKER_INSTANCE_PROPERTY"; } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/MessageUtils.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/MessageUtils.java deleted file mode 100644 index 10edfb10dd..0000000000 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/messaging/MessageUtils.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.newrelic.agent.bridge.messaging; - -import com.newrelic.api.agent.DestinationType; -import com.newrelic.api.agent.TracedMethod; - -public class MessageUtils { - public static void addAmqpRollupMetrics(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName) { - } -} diff --git a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java index dbfe20d3bb..ba6a1666f3 100644 --- a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java +++ b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java @@ -22,6 +22,7 @@ import com.newrelic.agent.environment.AgentIdentity; import com.newrelic.agent.errors.ErrorService; import com.newrelic.agent.errors.TracedError; +import com.newrelic.agent.messaging.MessageMetrics; import com.newrelic.agent.metric.MetricName; import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.stats.ResponseTimeStats; @@ -1994,9 +1995,7 @@ public void testMessagingAPI() throws Exception { server.start(); runTestMessagingAPI(); String messageBrokerMetric = "MessageBroker/JMS/Queue/Consume/Temp"; - String endpointMetric = "MessageBroker/instance/unknown/unknown/Queue/Temp"; Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); - Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); } catch (IOException e) { e.printStackTrace(); Assert.fail(); @@ -2085,11 +2084,11 @@ private void runTestMessagingAPIWithHostAndPort() { ExternalParameters messageProduceParameters = MessageProduceParameters .library("JMS") .destinationType(DestinationType.NAMED_QUEUE) - .destinationName("Message Destination") + .destinationName("MessageDestination") .outboundHeaders(outboundRequestWrapper) - .instance(myURL.getHost(), myURL.getPort()) .build(); NewRelic.getAgent().getTracedMethod().reportAsExternal(messageProduceParameters); + MessageMetrics.buildInstanceMetric(myURL.getHost(), myURL.getPort(), DestinationType.NAMED_QUEUE, "MessageDestination"); Assert.assertTrue(request.getHeaders("NewRelicID").length != 0); Assert.assertTrue(request.getHeaders("NewRelicTransaction").length != 0); @@ -2101,11 +2100,11 @@ private void runTestMessagingAPIWithHostAndPort() { ExternalParameters messageResponseParameters = MessageConsumeParameters .library("JMS") .destinationType(DestinationType.TEMP_QUEUE) - .destinationName("Message Destination") + .destinationName("MessageDestination") .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) - .instance(myURL.getHost(), myURL.getPort()) .build(); NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); + MessageMetrics.buildInstanceMetric(myURL.getHost(), myURL.getPort(), DestinationType.TEMP_QUEUE, "MessageDestination"); Assert.assertTrue(response.getHeaders("NewRelicAppData").length != 0); } catch (Exception e) { diff --git a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java index 79adb8666e..f9ee42ec88 100644 --- a/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java +++ b/instrumentation/activemq-client-5.8.0/src/main/java/org/apache/activemq/command/ActiveMQMessage_Instrumentation.java @@ -12,7 +12,7 @@ import com.nr.agent.instrumentation.activemqclient580.ActiveMQUtil; import org.apache.activemq.ActiveMQConnection; -import static com.newrelic.agent.bridge.messaging.JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY; +import static com.newrelic.agent.bridge.messaging.JmsProperties.NR_JMS_BROKER_INSTANCE_PROPERTY; @Weave(type = MatchType.BaseClass, originalName = "org.apache.activemq.command.ActiveMQMessage") public abstract class ActiveMQMessage_Instrumentation { @@ -20,7 +20,7 @@ public abstract class ActiveMQMessage_Instrumentation { // This is so the JMS instrumentation can grab host and port of the Active MQ instance public Object getObjectProperty(String name) { - if (NR_JMS_HOST_AND_PORT_PROPERTY.equals(name)) { + if (NR_JMS_BROKER_INSTANCE_PROPERTY.equals(name)) { return ActiveMQUtil.get().parseHostAndPort(getConnection().getTransport().toString()); } return Weaver.callOriginal(); diff --git a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java index 0fddc1d41c..62d25e9f54 100644 --- a/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java +++ b/instrumentation/activemq-client-5.8.0/src/test/java/com/nr/agent/instrumentation/activemqclient580/ActiveMQMessageTest.java @@ -12,7 +12,7 @@ import javax.jms.JMSException; -import static com.newrelic.agent.bridge.messaging.JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY; +import static com.newrelic.agent.bridge.messaging.JmsProperties.NR_JMS_BROKER_INSTANCE_PROPERTY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -95,7 +95,7 @@ private void setStubs(String transportString) { } private void assertMessage(String expectedHost, Integer expectedPort, ActiveMQMessage message, Integer timesGetConnectionCalled) throws JMSException { - BrokerInstance brokerInstance = (BrokerInstance)message.getObjectProperty(NR_JMS_HOST_AND_PORT_PROPERTY); + BrokerInstance brokerInstance = (BrokerInstance)message.getObjectProperty(NR_JMS_BROKER_INSTANCE_PROPERTY); verify(message, times(timesGetConnectionCalled)).getConnection(); assertNotNull("Failed to retrieve brokerInstance from ActiveMQ message", brokerInstance); assertEquals("Expected host did not match", expectedHost, brokerInstance.getHostName()); diff --git a/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java b/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java index ad837c2ab4..f1dce3f3e9 100644 --- a/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java +++ b/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java @@ -8,9 +8,7 @@ package com.nr.instrumentation.graphql.helper; import com.newrelic.agent.bridge.PrivateApi; -import com.newrelic.agent.messaging.MessageMetrics; import com.newrelic.api.agent.DestinationType; -import com.newrelic.api.agent.TracedMethod; import javax.management.MBeanServer; import java.io.Closeable; @@ -86,22 +84,28 @@ public void reportException(Throwable throwable) { } @Override - public void setAppServerPort(int port) { + public String buildMessageBrokerInstanceMetric(String host, Integer port, DestinationType destinationType, String destination) { + return null; + } + + @Override + public void reportMessageBrokerInstance(String host, Integer port, DestinationType destinationType, String destination) { } @Override - public void setServerInfo(String dispatcherName, String version) { + public void setAppServerPort(int port) { } @Override - public void setInstanceName(String instanceName) { + public void setServerInfo(String dispatcherName, String version) { } @Override - public void reportAmqpInstance(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey) { + public void setInstanceName(String instanceName) { } + } diff --git a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java index d9dac0124e..bf2605bff6 100644 --- a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java +++ b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java @@ -132,11 +132,11 @@ public static void processSendMessage(Message message, Destination dest, TracedM .destinationType(destinationType) .destinationName(destinationName) .outboundHeaders(new OutboundWrapper(message)); + tracer.reportAsExternal(builder.build()); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); + AgentBridge.privateApi.reportMessageBrokerInstance(brokerInstance.getHostName(), brokerInstance.getPort(), destinationType, destinationName); } - tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message produce."); @@ -157,11 +157,11 @@ public static void processConsume(Message message, TracedMethod tracer) { .destinationType(destinationType) .destinationName(destinationName) .inboundHeaders(new InboundWrapper(message)); + tracer.reportAsExternal(builder.build()); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); + AgentBridge.privateApi.reportMessageBrokerInstance(brokerInstance.getHostName(), brokerInstance.getPort(), destinationType, destinationName); } - tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message consume."); @@ -169,7 +169,7 @@ public static void processConsume(Message message, TracedMethod tracer) { } private static BrokerInstance getHostAndPort(Message message) throws JMSException { - Object obj = message.getObjectProperty(JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY); + Object obj = message.getObjectProperty(JmsProperties.NR_JMS_BROKER_INSTANCE_PROPERTY); if (obj instanceof BrokerInstance) { return (BrokerInstance) obj; } diff --git a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/MessageConsumer_Instrumentation.java b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/MessageConsumer_Instrumentation.java index 95aad57b7f..0463cdbc6e 100644 --- a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/MessageConsumer_Instrumentation.java +++ b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/MessageConsumer_Instrumentation.java @@ -12,7 +12,6 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.agent.instrumentation.jms11.JmsMetricUtil; import javax.jms.JMSException; import javax.jms.Message; diff --git a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java index 19fc51c39f..c387d5c3b4 100644 --- a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java +++ b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java @@ -132,11 +132,11 @@ public static void processSendMessage(Message message, Destination dest, TracedM .destinationType(destinationType) .destinationName(destinationName) .outboundHeaders(new OutboundWrapper(message)); + tracer.reportAsExternal(builder.build()); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); + AgentBridge.privateApi.reportMessageBrokerInstance(brokerInstance.getHostName(), brokerInstance.getPort(), destinationType, destinationName); } - tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message produce."); @@ -157,11 +157,11 @@ public static void processConsume(Message message, TracedMethod tracer) { .destinationType(destinationType) .destinationName(destinationName) .inboundHeaders(new InboundWrapper(message)); + tracer.reportAsExternal(builder.build()); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); + AgentBridge.privateApi.reportMessageBrokerInstance(brokerInstance.getHostName(), brokerInstance.getPort(), destinationType, destinationName); } - tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message consume."); @@ -169,7 +169,7 @@ public static void processConsume(Message message, TracedMethod tracer) { } private static BrokerInstance getHostAndPort(Message message) throws JMSException { - Object obj = message.getObjectProperty(JmsProperties.NR_JMS_HOST_AND_PORT_PROPERTY); + Object obj = message.getObjectProperty(JmsProperties.NR_JMS_BROKER_INSTANCE_PROPERTY); if (obj instanceof BrokerInstance) { return (BrokerInstance) obj; } diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index 1f7808d46b..76699c1980 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -27,6 +27,8 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; + public static final String SLASH = "/"; + public static final String ROUTING_KEY = "RoutingKey"; private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() @@ -50,9 +52,9 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); addAttributes(routingKey, props); } @@ -66,9 +68,9 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } @@ -116,4 +118,25 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } + public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { + String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); + if (amqpSuffix == null) { + return exchangeName; + } + return exchangeName + SLASH + amqpSuffix; + } + + public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { + if (isParamKnown(queueName)) { + return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; + } else if (isParamKnown(routingKey)) { + return ROUTING_KEY + SLASH + routingKey; + } + return null; + } + + private static boolean isParamKnown(String str) { + return str != null && !str.isEmpty(); + } + } diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java index 462593eb0d..f17a5a72f4 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -28,6 +28,8 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; + public static final String SLASH = "/"; + public static final String ROUTING_KEY = "RoutingKey"; private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() @@ -51,9 +53,10 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); + addAttributes(routingKey, props); } @@ -67,9 +70,9 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } @@ -124,4 +127,25 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } + public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { + String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); + if (amqpSuffix == null) { + return exchangeName; + } + return exchangeName + SLASH + amqpSuffix; + } + + public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { + if (isParamKnown(queueName)) { + return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; + } else if (isParamKnown(routingKey)) { + return ROUTING_KEY + SLASH + routingKey; + } + return null; + } + + private static boolean isParamKnown(String str) { + return str != null && !str.isEmpty(); + } + } diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index 3585701d4c..e60cd8f3e6 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -28,6 +28,8 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; + public static final String SLASH = "/"; + public static final String ROUTING_KEY = "RoutingKey"; private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() @@ -51,9 +53,9 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); addAttributes(routingKey, props); } @@ -67,9 +69,9 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } @@ -124,4 +126,25 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } + public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { + String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); + if (amqpSuffix == null) { + return exchangeName; + } + return exchangeName + SLASH + amqpSuffix; + } + + public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { + if (isParamKnown(queueName)) { + return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; + } else if (isParamKnown(routingKey)) { + return ROUTING_KEY + SLASH + routingKey; + } + return null; + } + + private static boolean isParamKnown(String str) { + return str != null && !str.isEmpty(); + } + } diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java index 74b7a4dca6..86be2010ac 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -28,6 +28,8 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; + public static final String SLASH = "/"; + public static final String ROUTING_KEY = "RoutingKey"; private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() @@ -51,9 +53,9 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); addAttributes(routingKey, props); } @@ -67,9 +69,9 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } @@ -124,4 +126,25 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } + public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { + String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); + if (amqpSuffix == null) { + return exchangeName; + } + return exchangeName + SLASH + amqpSuffix; + } + + public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { + if (isParamKnown(queueName)) { + return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; + } else if (isParamKnown(routingKey)) { + return ROUTING_KEY + SLASH + routingKey; + } + return null; + } + + private static boolean isParamKnown(String str) { + return str != null && !str.isEmpty(); + } + } diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index 3de80681aa..61185cb3c8 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -30,6 +30,9 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; + public static final String SLASH = "/"; + public static final String ROUTING_KEY = "RoutingKey"; + private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() .getValue("message_tracer.segment_parameters.enabled", Boolean.TRUE); @@ -52,9 +55,9 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, null, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, null, routingKey)); addAttributes(routingKey, props); } @@ -68,9 +71,9 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) - .instance(host, port) .build()); - AgentBridge.privateApi.reportAmqpInstance(tracedMethod, host, port, exchangeName, queueName, routingKey); + AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, + buildInstDestination(exchangeName, queueName, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } @@ -125,4 +128,25 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } + public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { + String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); + if (amqpSuffix == null) { + return exchangeName; + } + return exchangeName + SLASH + amqpSuffix; + } + + public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { + if (isParamKnown(queueName)) { + return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; + } else if (isParamKnown(routingKey)) { + return ROUTING_KEY + SLASH + routingKey; + } + return null; + } + + private static boolean isParamKnown(String str) { + return str != null && !str.isEmpty(); + } + } diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java index abfb18b949..8707b61567 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/impl/ChannelN_Instrumentation.java @@ -17,7 +17,6 @@ import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.MessageProperties; -import java.net.InetAddress; import java.util.HashMap; @Weave(type = MatchType.ExactClass, originalName = "com.rabbitmq.client.impl.ChannelN") diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java index 88e9c90288..6a376b7622 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java @@ -16,8 +16,8 @@ import com.newrelic.agent.messaging.MessageMetrics; import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.util.AgentCollectionFactory; +import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.Logger; -import com.newrelic.api.agent.TracedMethod; import javax.management.MBeanServer; import java.io.Closeable; @@ -97,6 +97,19 @@ public void reportException(Throwable throwable) { ServiceFactory.getRPMService().getErrorService().reportException(throwable); } + @Override + public String buildMessageBrokerInstanceMetric(String host, Integer port, DestinationType destinationType, String destination) { + return MessageMetrics.buildInstanceMetricIfEnabled(host, port, destinationType, destination); + } + + @Override + public void reportMessageBrokerInstance(String host, Integer port, DestinationType destinationType, String destination) { + Transaction currentTxn = Transaction.getTransaction(false); + if (currentTxn != null) { + MessageMetrics.reportInstanceMetric(currentTxn.getTransactionActivity().getLastTracer(), host, port, destinationType, destination); + } + } + @Override public void setAppServerPort(int port) { AgentBridge.publicApi.setAppServerPort(port); @@ -112,11 +125,6 @@ public void setInstanceName(String instanceName) { AgentBridge.publicApi.setInstanceName(instanceName); } - @Override - public void reportAmqpInstance(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey) { - MessageMetrics.collectAmqpMetrics(method, host, port, exchangeName, queueName, routingKey); - } - /** * Allows modules to add strings to a segment in a transaction trace. */ diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java index f0c6877e29..f0ddff05ae 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java @@ -6,6 +6,7 @@ */ package com.newrelic.agent.messaging; +import com.newrelic.agent.Transaction; import com.newrelic.agent.config.Hostname; import com.newrelic.agent.config.MessageBrokerConfig; import com.newrelic.agent.service.ServiceFactory; @@ -22,74 +23,63 @@ public class MessageMetrics { public static final String UNKNOWN = "unknown"; public static String HOSTNAME = Hostname.getHostname(ServiceFactory.getConfigService().getDefaultAgentConfig()); - public static boolean isAnyEndpointParamsKnown(String host, Integer port) { - return !(isParamUnknown(host) && isParamUnknown(port)); - } - public static void collectMessageProducerRollupMetrics(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName) { - reportInstanceIfEnabled(method, host, port, destinationType, destinationName); - } - - public static void collectMessageConsumerRollupMetrics(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName) { - reportInstanceIfEnabled(method, host, port, destinationType, destinationName); - } - - public static void collectAmqpMetrics(TracedMethod method, String host, Integer port, String exchangeName, String queueName, String routingKey) { - MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - if (messageBrokerConfig.isInstanceReportingEnabled() && isAmqpParamsValid(queueName, routingKey)) { - String instanceMetric = buildInstanceMetric(host, port, DestinationType.EXCHANGE, exchangeName); - String amqpInstance = buildAmqpInstanceMetric(instanceMetric, queueName, routingKey); - method.addRollupMetricName(amqpInstance); + public static void reportInstanceMetric(TracedMethod tracedMethod, String host, Integer port, + DestinationType destinationType, String destination) { + String instanceMetric = buildInstanceMetricIfEnabled(host, port, destinationType, destination); + if (instanceMetric != null && !instanceMetric.trim().isEmpty()) { + tracedMethod.addRollupMetricName(instanceMetric); } } - public static void reportInstanceIfEnabled(TracedMethod method, String host, Integer port, - DestinationType destinationType, String destinationName) { + public static String buildInstanceMetricIfEnabled(String host, Integer port, + DestinationType destinationType, String destination) { MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); if (messageBrokerConfig.isInstanceReportingEnabled()) { - String instanceMetric = buildInstanceMetric(host, port, destinationType, destinationName); - method.addRollupMetricName(instanceMetric); + return buildInstanceMetric(host, port, destinationType, destination); } + return null; } public static String buildInstanceMetric(String host, Integer port, - DestinationType destinationType, String destinationName) { - String instance = buildInstanceIdentifier(host, port, destinationType, destinationName); + DestinationType destinationType, String destination) { + String instance = buildInstanceIdentifier(host, port, destinationType, destination); return MESSAGE_BROKER_INSTANCE + instance; } public static String buildInstanceIdentifier(String host, Integer port, - DestinationType destinationType, String destinationName) { + DestinationType destinationType, String destination) { String hostname = replaceLocalhost(host); String portName = replacePort(port); - String parsedDestinationName = replaceDestinationName(destinationType, destinationName); + String parsedDestination = replaceDestination(destinationType, destination); - return hostname + SLASH + portName + SLASH + destinationType.getTypeName() + SLASH + parsedDestinationName; + return hostname + SLASH + portName + SLASH + destinationType.getTypeName() + SLASH + parsedDestination; } - public static String buildAmqpInstanceMetric(String instanceMetric, String queueName, String routingKey) { - String amqpSuffix = buildAmqpInstance(queueName, routingKey); - return instanceMetric + SLASH + amqpSuffix; + public static String buildAmqpDestination(String exchangeName, String queueName, String routingKey) { + String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); + if (amqpSuffix != null) { + return null; + } + return DestinationType.EXCHANGE.getTypeName() + SLASH + exchangeName + SLASH + amqpSuffix; } - public static String buildAmqpInstance(String queueName, String routingKey) { + public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { if (!isParamUnknown(queueName)) { return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; } else if (!isParamUnknown(routingKey)) { return ROUTING_KEY + SLASH + routingKey; } - return ""; + return null; } - public static String replaceDestinationName(DestinationType destinationType, String destinationName) { + public static String replaceDestination(DestinationType destinationType, String destination) { if (destinationType == DestinationType.TEMP_QUEUE || destinationType == DestinationType.TEMP_TOPIC) { return "Temp"; } - if (isParamUnknown(destinationName)) { + if (isParamUnknown(destination)) { return UNKNOWN; } - return "Named" + SLASH + destinationName; + return "Named" + SLASH + destination; } public static String replaceLocalhost(String host) { @@ -113,10 +103,6 @@ public static String replacePort(Integer port) { return String.valueOf(port); } - private static boolean isAmqpParamsValid(String amqpQueueName, String amqpRoutingKey) { - return !(isParamUnknown(amqpQueueName) && isParamUnknown(amqpRoutingKey)); - } - private static boolean isParamUnknown(String str) { return str == null || str.isEmpty(); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index cc65376a1e..9f0d68dc3e 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -19,7 +19,6 @@ import com.newrelic.agent.config.TransactionTracerConfig; import com.newrelic.agent.database.DatastoreMetrics; import com.newrelic.agent.database.SqlObfuscator; -import com.newrelic.agent.messaging.MessageMetrics; import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.stats.ResponseTimeStats; import com.newrelic.agent.stats.TransactionStats; @@ -821,12 +820,6 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP messageProduceParameters.getLibrary(), messageProduceParameters.getDestinationType().getTypeName())); } - - MessageMetrics.collectMessageProducerRollupMetrics(this, - messageProduceParameters.getHost(), - messageProduceParameters.getPort(), - destinationType, - messageProduceParameters.getDestinationName()); } private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeParameters) { @@ -850,12 +843,6 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP if (messageConsumeParameters.getCloudResourceId() != null) { setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageConsumeParameters.getCloudResourceId()); } - - MessageMetrics.collectMessageConsumerRollupMetrics(this, - messageConsumeParameters.getHost(), - messageConsumeParameters.getPort(), - destinationType, - messageConsumeParameters.getDestinationName()); } private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java index 977d3c125a..f8ce81cd26 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; public class MessageMetricsTest { @@ -63,134 +62,43 @@ public void testReplaceLocalhost() throws Exception { } @Test - public void testIsAnyEndpointParamsKnown() { - assertTrue(MessageMetrics.isAnyEndpointParamsKnown("example.com", 1000)); - assertTrue(MessageMetrics.isAnyEndpointParamsKnown("example.com", null)); - assertTrue(MessageMetrics.isAnyEndpointParamsKnown(null, 1000)); - assertFalse(MessageMetrics.isAnyEndpointParamsKnown(null, null)); - assertTrue(MessageMetrics.isAnyEndpointParamsKnown("example.com", -1)); - assertTrue(MessageMetrics.isAnyEndpointParamsKnown("", 1000)); - assertFalse(MessageMetrics.isAnyEndpointParamsKnown("", -1)); - } + public void testBuildInstanceMetrics() { + assertEquals( + MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName), + MessageMetrics.buildInstanceMetricIfEnabled("localhost", 8080, DestinationType.NAMED_TOPIC, "topic")); - @Test - public void testCollectMessageProducerRollupMetrics() { - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_TOPIC, "topic"); - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName)); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_QUEUE, "queue"); - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue", hostName)); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_QUEUE, "queue2"); - // Expect routing key is ignored as we already have a queue - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue2", hostName)); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_TOPIC, null); - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Temp", hostName)); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_QUEUE, null); - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Temp", hostName)); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.NAMED_QUEUE, "queue"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Queue/Named/queue"); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange"); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange2"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2"); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - null, DestinationType.NAMED_QUEUE, "queue"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/unknown/Queue/Named/queue"); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, null, - null, DestinationType.EXCHANGE, null); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/Exchange/unknown"); - - MessageMetrics.collectMessageProducerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.EXCHANGE, null); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Exchange/unknown"); - } + assertEquals( + "MessageBroker/instance/broker.com/8080/Topic/Named/topic2", + MessageMetrics.buildInstanceMetricIfEnabled("broker.com", 8080, DestinationType.NAMED_TOPIC, "topic2")); - @Test - public void testCollectMessageConsumerRollupMetrics() { - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_TOPIC, "topic"); - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName)); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.NAMED_QUEUE, "queue"); - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Named/queue", hostName)); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_TOPIC, null); - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Temp", hostName)); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "localhost", - 8080, DestinationType.TEMP_QUEUE, null); - verify(mockTracedMethod).addRollupMetricName( - MessageFormat.format("MessageBroker/instance/{0}/8080/Queue/Temp", hostName)); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.NAMED_QUEUE, "queue"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Queue/Named/queue"); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "queue"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/queue"); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - null, DestinationType.NAMED_QUEUE, "queue"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/unknown/Queue/Named/queue"); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - null, DestinationType.EXCHANGE, null); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/unknown/Exchange/unknown"); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, "example.com", - 8080, DestinationType.EXCHANGE, null); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/example.com/8080/Exchange/unknown"); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange2"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange2"); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange3"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange3"); - - MessageMetrics.collectMessageConsumerRollupMetrics(mockTracedMethod, null, - 8080, DestinationType.EXCHANGE, "exchange4"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/unknown/8080/Exchange/Named/exchange4"); - } + assertEquals( + "MessageBroker/instance/broker.com/8080/Queue/Named/queue2", + MessageMetrics.buildInstanceMetricIfEnabled("broker.com", 8080, DestinationType.NAMED_QUEUE, "queue2")); + + assertEquals( + "MessageBroker/instance/broker.com/8080/Topic/Temp", + MessageMetrics.buildInstanceMetricIfEnabled( "broker.com", 8080, DestinationType.TEMP_TOPIC, null)); + + assertEquals( + "MessageBroker/instance/broker.com/8081/Queue/Temp", + MessageMetrics.buildInstanceMetricIfEnabled( "broker.com", 8081, DestinationType.TEMP_QUEUE, null)); + + assertEquals( + "MessageBroker/instance/example.com/8080/Exchange/Named/exchange/Queue/Named/someQueue", + MessageMetrics.buildInstanceMetricIfEnabled("example.com", 8080, + DestinationType.EXCHANGE, "exchange/Queue/Named/someQueue")); + + assertEquals("MessageBroker/instance/unknown/8080/Exchange/Named/exchange", + MessageMetrics.buildInstanceMetricIfEnabled(null, 8080, DestinationType.EXCHANGE, "exchange")); + + assertEquals("MessageBroker/instance/example.com/unknown/Exchange/Named/exchange", + MessageMetrics.buildInstanceMetricIfEnabled("example.com", null, DestinationType.EXCHANGE, "exchange")); + + assertEquals("MessageBroker/instance/example.com/8080/Exchange/unknown", + MessageMetrics.buildInstanceMetricIfEnabled("example.com", 8080, DestinationType.EXCHANGE, null)); + + assertEquals("MessageBroker/instance/unknown/unknown/Exchange/unknown", + MessageMetrics.buildInstanceMetricIfEnabled(null, null, DestinationType.EXCHANGE, null)); - @Test - public void testCollectAmqpRollupMetrics() { - MessageMetrics.collectAmqpMetrics(mockTracedMethod, "amqp.com", - 8080, "exchange2", null, "someKey"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/amqp.com/8080/Exchange/Named/exchange2/RoutingKey/someKey"); - MessageMetrics.collectAmqpMetrics(mockTracedMethod, "amqp.com", - 8080, "exchange2", "someQueue", "someKey"); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/amqp.com/8080/Exchange/Named/exchange2/Queue/someQueue"); - MessageMetrics.collectAmqpMetrics(mockTracedMethod, "amqp.com", - 8080, "exchange3", "someQueue", null); - verify(mockTracedMethod).addRollupMetricName("MessageBroker/instance/amqp.com/8080/Exchange/Named/exchange3/Queue/someQueue"); } } diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java index 0df116360b..68b31594be 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java @@ -21,18 +21,14 @@ public class MessageConsumeParameters implements ExternalParameters { private final String destinationName; private final InboundHeaders inboundHeaders; private final String cloudResourceId; - private final String host; - private final Integer port; protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, - InboundHeaders inboundHeaders, String cloudResourceId, String host, Integer port) { + InboundHeaders inboundHeaders, String cloudResourceId) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; this.inboundHeaders = inboundHeaders; this.cloudResourceId = cloudResourceId; - this.host = host; - this.port = port; } @@ -47,8 +43,6 @@ protected MessageConsumeParameters(MessageConsumeParameters messageConsumeParame this.destinationName = messageConsumeParameters.destinationName; this.inboundHeaders = messageConsumeParameters.inboundHeaders; this.cloudResourceId = messageConsumeParameters.cloudResourceId; - this.host = messageConsumeParameters.host; - this.port = messageConsumeParameters.port; } public String getDestinationName() { @@ -67,14 +61,6 @@ public String getCloudResourceId() { return cloudResourceId; } - public String getHost() { - return host; - } - - public Integer getPort() { - return port; - } - public String getLibrary() { return library; } @@ -86,8 +72,6 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String destinationName; private InboundHeaders inboundHeaders; private String cloudResourceId; - private String host; - private Integer port; public Builder(String library) { this.library = library; @@ -113,14 +97,8 @@ public Build cloudResourceId(String cloudResourceId) { return this; } - public Build instance(String host, Integer port) { - this.host = host; - this.port = port; - return this; - } - public MessageConsumeParameters build() { - return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId, host, port); + return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId); } } @@ -175,12 +153,6 @@ public interface Build { */ Build cloudResourceId(String cloudResourceId); - /** - * Set the host name and port number for the message queue. - * This method is optional and can be bypassed by calling build directly. - */ - Build instance(String host, Integer port); - /** * Build the final {@link MessageConsumeParameters} for the API call. * diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java index d03555cc83..3c22f3f635 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java @@ -20,17 +20,13 @@ public class MessageProduceParameters implements ExternalParameters { private final String destinationName; private final OutboundHeaders outboundHeaders; private final String cloudResourceId; - private final String host; - private final Integer port; protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, - OutboundHeaders outboundHeaders, String cloudResourceId, String host, Integer port) { + OutboundHeaders outboundHeaders, String cloudResourceId) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; this.outboundHeaders = outboundHeaders; this.cloudResourceId = cloudResourceId; - this.host = host; - this.port = port; } /** @@ -44,8 +40,6 @@ protected MessageProduceParameters(MessageProduceParameters messageProduceParame this.destinationName = messageProduceParameters.destinationName; this.outboundHeaders = messageProduceParameters.outboundHeaders; this.cloudResourceId = messageProduceParameters.cloudResourceId; - this.host = messageProduceParameters.host; - this.port = messageProduceParameters.port; } public String getDestinationName() { @@ -64,14 +58,6 @@ public String getCloudResourceId() { return cloudResourceId; } - public String getHost() { - return host; - } - - public Integer getPort() { - return port; - } - public String getLibrary() { return library; } @@ -83,8 +69,6 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String destinationName; private OutboundHeaders outboundHeaders; private String cloudResourceId; - private String host; - private Integer port; public Builder(String library) { this.library = library; @@ -110,14 +94,8 @@ public Build cloudResourceId(String cloudResourceId) { return this; } - public Build instance(String host, Integer port) { - this.host = host; - this.port = port; - return this; - } - public MessageProduceParameters build() { - return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId, host, port); + return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId); } } @@ -173,12 +151,6 @@ public interface Build { */ Build cloudResourceId(String cloudResourceId); - /** - * Set the host name and port number for the message queue. - * This method is optional and can be bypassed by calling build directly. - */ - Build instance(String host, Integer port); - /** * Build the final {@link MessageProduceParameters} for the API call. * From cad828055f06e322f0f9d67b271e1da1a99652dd Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 3 Jun 2024 17:36:39 -0400 Subject: [PATCH 34/42] replace message instance metrics with spans --- .../newrelic/agent/bridge/NoOpPrivateApi.java | 15 +-- .../com/newrelic/agent/bridge/PrivateApi.java | 25 +--- .../external/MessageConsumeParameters.java | 2 +- .../external/MessageProduceParameters.java | 2 +- .../test/newrelic/test/agent/api/ApiTest.java | 6 +- .../graphql/helper/PrivateApiStub.java | 15 +-- .../instrumentation/jms11/JmsMetricUtil.java | 8 +- .../instrumentation/jms3/JmsMetricUtil.java | 8 +- .../rabbitamqp172/RabbitAMQPMetricUtil.java | 33 +---- .../rabbitamqp241/RabbitAMQPMetricUtil.java | 34 +----- .../rabbitamqp250/RabbitAMQPMetricUtil.java | 33 +---- .../rabbitamqp270/RabbitAMQPMetricUtil.java | 33 +---- .../rabbitamqp500/RabbitAMQPMetricUtil.java | 34 +----- .../com/newrelic/agent/PrivateApiImpl.java | 26 ++-- .../newrelic/agent/TransactionActivity.java | 2 +- .../agent/attributes/AttributeNames.java | 4 + .../newrelic/agent/config/AgentConfig.java | 2 - .../agent/config/AgentConfigImpl.java | 12 -- .../agent/config/MessageBrokerConfig.java | 15 --- .../agent/config/MessageBrokerConfigImpl.java | 35 ------ .../agent/messaging/MessageMetrics.java | 113 ------------------ .../service/analytics/SpanEventFactory.java | 20 +++- .../service/analytics/TracerToSpanEvent.java | 2 +- .../agent/tracers/AbstractTracer.java | 28 ++++- .../newrelic/agent/tracers/DefaultTracer.java | 15 +++ .../newrelic/agent/tracers/NoOpTracer.java | 11 ++ .../com/newrelic/agent/tracers/Tracer.java | 13 +- .../agent/tracers/UltraLightTracer.java | 10 ++ .../sql/NoOpTrackingSqlTracer.java | 11 ++ .../AgentPostprocessorsTest.java | 2 + .../agent/messaging/MessageMetricsTest.java | 104 ---------------- .../analytics/SpanEventFactoryTest.java | 51 ++++++++ .../analytics/TracerToSpanEventTest.java | 30 +++++ .../api/agent/MessageConsumeParameters.java | 37 +++++- .../api/agent/MessageProduceParameters.java | 36 +++++- 35 files changed, 314 insertions(+), 513 deletions(-) delete mode 100644 newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java delete mode 100644 newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java delete mode 100644 newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java delete mode 100644 newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java index da1d5a46e4..949e014354 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpPrivateApi.java @@ -57,16 +57,6 @@ public void reportHTTPError(String message, int statusCode, String uri) { public void reportException(Throwable throwable) { } - @Override - public String buildMessageBrokerInstanceMetric(String host, Integer port, DestinationType destinationType, String destination) { - return null; - } - - @Override - public void reportMessageBrokerInstance(String host, Integer port, DestinationType destinationType, String destination) { - - } - @Override public void setAppServerPort(int port) { } @@ -83,6 +73,11 @@ public void setInstanceName(String instanceName) { public void addTracerParameter(String key, String value) { } + @Override + public void addTracerParameter(String key, String value, boolean addToSpan) { + + } + @Override public void addTracerParameter(String key, Map values) { } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java index a6a308035a..fbb53afa2c 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/PrivateApi.java @@ -59,6 +59,8 @@ public interface PrivateApi { void addTracerParameter(String key, String value); + void addTracerParameter(String key, String value, boolean addToSpan); + void addTracerParameter(String key, Map values); /** @@ -91,29 +93,6 @@ public interface PrivateApi { */ void reportException(Throwable throwable); - /** - * Creates a message broker instance metric if enabled via agent configuration. - * Otherwise, a null value is returned. - * - * @param host Broker host - * @param port Broker port - * @param destinationType Destination type - * @param destination Destination (can be the destination name or a more complex value related to the destination type) - */ - String buildMessageBrokerInstanceMetric(String host, Integer port, - DestinationType destinationType, String destination); - - /** - * Creates a message broker instance rollup metric if enabled via agent configuration. - * - * @param host Broker host - * @param port Broker port - * @param destinationType Destination type - * @param destination Destination (can be the destination name or a more complex value related to the destination type) - */ - void reportMessageBrokerInstance(String host, Integer port, - DestinationType destinationType, String destination); - /** * Set the app server port which is reported to RPM. * diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java index 98dfad581b..7024ae0dc6 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageConsumeParameters.java @@ -26,7 +26,7 @@ public class MessageConsumeParameters extends com.newrelic.api.agent.MessageCons @Deprecated protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, InboundHeaders inboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null); + super(library, destinationType.toApiDestinationType(), destinationName, inboundHeaders, null, null, null); } } diff --git a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java index f4ed341478..f5560508a1 100644 --- a/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java +++ b/agent-bridge/src/main/java/com/newrelic/agent/bridge/external/MessageProduceParameters.java @@ -26,7 +26,7 @@ public class MessageProduceParameters extends com.newrelic.api.agent.MessageProd @Deprecated protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, OutboundHeaders outboundHeaders) { - super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null); + super(library, destinationType.toApiDestinationType(), destinationName, outboundHeaders, null, null, null); } } diff --git a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java index ba6a1666f3..a65148a65e 100644 --- a/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java +++ b/functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java @@ -22,7 +22,6 @@ import com.newrelic.agent.environment.AgentIdentity; import com.newrelic.agent.errors.ErrorService; import com.newrelic.agent.errors.TracedError; -import com.newrelic.agent.messaging.MessageMetrics; import com.newrelic.agent.metric.MetricName; import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.stats.ResponseTimeStats; @@ -2016,9 +2015,7 @@ public void testMessagingAPIWithHostAndPort() throws Exception { server.start(); runTestMessagingAPIWithHostAndPort(); String messageBrokerMetric = "MessageBroker/JMS/Queue/Consume/Temp"; - String endpointMetric = String.format("MessageBroker/instance/%s/8088/Queue/Temp", HOSTNAME); Assert.assertTrue("The following metric should exist: " + messageBrokerMetric, apiTestHelper.tranStats.getScopedStats().getStatsMap().containsKey(messageBrokerMetric)); - Assert.assertTrue("The following metric should exist: " + endpointMetric, apiTestHelper.tranStats.getUnscopedStats().getStatsMap().containsKey(endpointMetric)); } catch (IOException e) { e.printStackTrace(); Assert.fail(); @@ -2086,9 +2083,9 @@ private void runTestMessagingAPIWithHostAndPort() { .destinationType(DestinationType.NAMED_QUEUE) .destinationName("MessageDestination") .outboundHeaders(outboundRequestWrapper) + .instance(myURL.getHost(), myURL.getPort()) .build(); NewRelic.getAgent().getTracedMethod().reportAsExternal(messageProduceParameters); - MessageMetrics.buildInstanceMetric(myURL.getHost(), myURL.getPort(), DestinationType.NAMED_QUEUE, "MessageDestination"); Assert.assertTrue(request.getHeaders("NewRelicID").length != 0); Assert.assertTrue(request.getHeaders("NewRelicTransaction").length != 0); @@ -2104,7 +2101,6 @@ private void runTestMessagingAPIWithHostAndPort() { .inboundHeaders(new ApiTestHelper.InboundWrapper(response, HeaderType.MESSAGE)) .build(); NewRelic.getAgent().getTracedMethod().reportAsExternal(messageResponseParameters); - MessageMetrics.buildInstanceMetric(myURL.getHost(), myURL.getPort(), DestinationType.TEMP_QUEUE, "MessageDestination"); Assert.assertTrue(response.getHeaders("NewRelicAppData").length != 0); } catch (Exception e) { diff --git a/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java b/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java index f1dce3f3e9..b3b675ba88 100644 --- a/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java +++ b/instrumentation/graphql-java-16.2/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java @@ -58,6 +58,11 @@ public void addTracerParameter(String key, String value) { tracerParameters.put(key, value); } + @Override + public void addTracerParameter(String key, String value, boolean addToSpan) { + + } + @Override public void addTracerParameter(String key, Map values) { @@ -83,16 +88,6 @@ public void reportException(Throwable throwable) { } - @Override - public String buildMessageBrokerInstanceMetric(String host, Integer port, DestinationType destinationType, String destination) { - return null; - } - - @Override - public void reportMessageBrokerInstance(String host, Integer port, DestinationType destinationType, String destination) { - - } - @Override public void setAppServerPort(int port) { diff --git a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java index bf2605bff6..e01473f1a2 100644 --- a/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java +++ b/instrumentation/jms-1.1/src/main/java/com/nr/agent/instrumentation/jms11/JmsMetricUtil.java @@ -132,11 +132,11 @@ public static void processSendMessage(Message message, Destination dest, TracedM .destinationType(destinationType) .destinationName(destinationName) .outboundHeaders(new OutboundWrapper(message)); - tracer.reportAsExternal(builder.build()); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - AgentBridge.privateApi.reportMessageBrokerInstance(brokerInstance.getHostName(), brokerInstance.getPort(), destinationType, destinationName); + builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); } + tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message produce."); @@ -157,11 +157,11 @@ public static void processConsume(Message message, TracedMethod tracer) { .destinationType(destinationType) .destinationName(destinationName) .inboundHeaders(new InboundWrapper(message)); - tracer.reportAsExternal(builder.build()); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - AgentBridge.privateApi.reportMessageBrokerInstance(brokerInstance.getHostName(), brokerInstance.getPort(), destinationType, destinationName); + builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); } + tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message consume."); diff --git a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java index c387d5c3b4..75eee70030 100644 --- a/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java +++ b/instrumentation/jms-3/src/main/java/com/nr/agent/instrumentation/jms3/JmsMetricUtil.java @@ -132,11 +132,11 @@ public static void processSendMessage(Message message, Destination dest, TracedM .destinationType(destinationType) .destinationName(destinationName) .outboundHeaders(new OutboundWrapper(message)); - tracer.reportAsExternal(builder.build()); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - AgentBridge.privateApi.reportMessageBrokerInstance(brokerInstance.getHostName(), brokerInstance.getPort(), destinationType, destinationName); + builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); } + tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message produce."); @@ -157,11 +157,11 @@ public static void processConsume(Message message, TracedMethod tracer) { .destinationType(destinationType) .destinationName(destinationName) .inboundHeaders(new InboundWrapper(message)); - tracer.reportAsExternal(builder.build()); BrokerInstance brokerInstance = getHostAndPort(message); if (brokerInstance != null) { - AgentBridge.privateApi.reportMessageBrokerInstance(brokerInstance.getHostName(), brokerInstance.getPort(), destinationType, destinationName); + builder = builder.instance(brokerInstance.getHostName(), brokerInstance.getPort()); } + tracer.reportAsExternal(builder.build()); } catch (JMSException exception) { NewRelic.getAgent().getLogger().log(Level.FINE, exception, "Unable to record metrics for JMS message consume."); diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index 76699c1980..1950362609 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -27,8 +27,6 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; - public static final String SLASH = "/"; - public static final String ROUTING_KEY = "RoutingKey"; private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() @@ -52,9 +50,8 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); addAttributes(routingKey, props); } @@ -68,16 +65,15 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { - AgentBridge.privateApi.addTracerParameter("message.queueName", queueName); + AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); } addAttributes(routingKey, properties); } @@ -100,7 +96,7 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } @@ -118,25 +114,4 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } - public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { - String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); - if (amqpSuffix == null) { - return exchangeName; - } - return exchangeName + SLASH + amqpSuffix; - } - - public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { - if (isParamKnown(queueName)) { - return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; - } else if (isParamKnown(routingKey)) { - return ROUTING_KEY + SLASH + routingKey; - } - return null; - } - - private static boolean isParamKnown(String str) { - return str != null && !str.isEmpty(); - } - } diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java index f17a5a72f4..3bedbb2537 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -28,8 +28,6 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; - public static final String SLASH = "/"; - public static final String ROUTING_KEY = "RoutingKey"; private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() @@ -53,10 +51,8 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); - addAttributes(routingKey, props); } @@ -70,16 +66,15 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { - AgentBridge.privateApi.addTracerParameter("message.queueName", queueName); + AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); } addAttributes(routingKey, properties); } @@ -109,7 +104,7 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } @@ -127,25 +122,4 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } - public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { - String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); - if (amqpSuffix == null) { - return exchangeName; - } - return exchangeName + SLASH + amqpSuffix; - } - - public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { - if (isParamKnown(queueName)) { - return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; - } else if (isParamKnown(routingKey)) { - return ROUTING_KEY + SLASH + routingKey; - } - return null; - } - - private static boolean isParamKnown(String str) { - return str != null && !str.isEmpty(); - } - } diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index e60cd8f3e6..a737c35af4 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -28,8 +28,6 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; - public static final String SLASH = "/"; - public static final String ROUTING_KEY = "RoutingKey"; private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() @@ -53,9 +51,8 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); addAttributes(routingKey, props); } @@ -69,16 +66,15 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { - AgentBridge.privateApi.addTracerParameter("message.queueName", queueName); + AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); } addAttributes(routingKey, properties); } @@ -108,7 +104,7 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } @@ -126,25 +122,4 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } - public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { - String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); - if (amqpSuffix == null) { - return exchangeName; - } - return exchangeName + SLASH + amqpSuffix; - } - - public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { - if (isParamKnown(queueName)) { - return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; - } else if (isParamKnown(routingKey)) { - return ROUTING_KEY + SLASH + routingKey; - } - return null; - } - - private static boolean isParamKnown(String str) { - return str != null && !str.isEmpty(); - } - } diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java index 86be2010ac..f2720fe8c9 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -28,8 +28,6 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; - public static final String SLASH = "/"; - public static final String ROUTING_KEY = "RoutingKey"; private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() @@ -53,9 +51,8 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); addAttributes(routingKey, props); } @@ -69,16 +66,15 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { - AgentBridge.privateApi.addTracerParameter("message.queueName", queueName); + AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); } addAttributes(routingKey, properties); } @@ -108,7 +104,7 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } @@ -126,25 +122,4 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } - public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { - String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); - if (amqpSuffix == null) { - return exchangeName; - } - return exchangeName + SLASH + amqpSuffix; - } - - public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { - if (isParamKnown(queueName)) { - return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; - } else if (isParamKnown(routingKey)) { - return ROUTING_KEY + SLASH + routingKey; - } - return null; - } - - private static boolean isParamKnown(String str) { - return str != null && !str.isEmpty(); - } - } diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index 61185cb3c8..c4cb08431e 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -30,9 +30,6 @@ public abstract class RabbitAMQPMetricUtil { private static final String MESSAGE = "Message"; private static final String DEFAULT = "Default"; - public static final String SLASH = "/"; - public static final String ROUTING_KEY = "RoutingKey"; - private static final boolean captureSegmentParameters = AgentBridge.getAgent() .getConfig() .getValue("message_tracer.segment_parameters.enabled", Boolean.TRUE); @@ -55,9 +52,8 @@ public static void processSendMessage(String exchangeName, String routingKey, .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .outboundHeaders(new OutboundWrapper(headers)) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, null, routingKey)); addAttributes(routingKey, props); } @@ -71,16 +67,15 @@ public static void processGetMessage(String queueName, String routingKey, String .destinationType(DestinationType.EXCHANGE) .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) .inboundHeaders(new InboundWrapper(properties.getHeaders())) + .instance(host, port) .build()); - AgentBridge.privateApi.reportMessageBrokerInstance(host, port, DestinationType.EXCHANGE, - buildInstDestination(exchangeName, queueName, routingKey)); addConsumeAttributes(queueName, routingKey, properties); } public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { - AgentBridge.privateApi.addTracerParameter("message.queueName", queueName); + AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); } addAttributes(routingKey, properties); } @@ -110,7 +105,7 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } @@ -128,25 +123,4 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper } } - public static String buildInstDestination(String exchangeName, String queueName, String routingKey) { - String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); - if (amqpSuffix == null) { - return exchangeName; - } - return exchangeName + SLASH + amqpSuffix; - } - - public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { - if (isParamKnown(queueName)) { - return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; - } else if (isParamKnown(routingKey)) { - return ROUTING_KEY + SLASH + routingKey; - } - return null; - } - - private static boolean isParamKnown(String str) { - return str != null && !str.isEmpty(); - } - } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java index 6a376b7622..dd9d4f11c4 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/PrivateApiImpl.java @@ -13,10 +13,8 @@ import com.newrelic.agent.bridge.PrivateApi; import com.newrelic.agent.environment.Environment; import com.newrelic.agent.jmx.JmxApiImpl; -import com.newrelic.agent.messaging.MessageMetrics; import com.newrelic.agent.service.ServiceFactory; import com.newrelic.agent.util.AgentCollectionFactory; -import com.newrelic.api.agent.DestinationType; import com.newrelic.api.agent.Logger; import javax.management.MBeanServer; @@ -97,19 +95,6 @@ public void reportException(Throwable throwable) { ServiceFactory.getRPMService().getErrorService().reportException(throwable); } - @Override - public String buildMessageBrokerInstanceMetric(String host, Integer port, DestinationType destinationType, String destination) { - return MessageMetrics.buildInstanceMetricIfEnabled(host, port, destinationType, destination); - } - - @Override - public void reportMessageBrokerInstance(String host, Integer port, DestinationType destinationType, String destination) { - Transaction currentTxn = Transaction.getTransaction(false); - if (currentTxn != null) { - MessageMetrics.reportInstanceMetric(currentTxn.getTransactionActivity().getLastTracer(), host, port, destinationType, destination); - } - } - @Override public void setAppServerPort(int port) { AgentBridge.publicApi.setAppServerPort(port); @@ -136,6 +121,17 @@ public void addTracerParameter(String key, String value) { } } + /** + * Allows modules to add strings to a segment in a transaction trace. + */ + @Override + public void addTracerParameter(String key, String value, boolean addToSpan) { + Transaction currentTxn = Transaction.getTransaction(false); + if (currentTxn != null) { + currentTxn.getTransactionActivity().getLastTracer().setAgentAttribute(key, value, addToSpan); + } + } + @Override public void addTracerParameter(String key, Map values) { Transaction currentTxn = Transaction.getTransaction(false); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/TransactionActivity.java b/newrelic-agent/src/main/java/com/newrelic/agent/TransactionActivity.java index bd44601e50..a1f136cd67 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/TransactionActivity.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/TransactionActivity.java @@ -503,7 +503,7 @@ private void setRootTracer(Tracer tracer) { if (tracer instanceof DefaultTracer) { DefaultTracer dt = (DefaultTracer) tracer; // Only check limits if in a transaction - dt.setAttribute("async_context", asyncContext, !tracer.isAsync(), false); + dt.setAttribute("async_context", asyncContext, !tracer.isAsync(), false, false); } if (!tracer.isAsync() && transaction != null) { 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 65869d3563..e2c38610f0 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 @@ -55,6 +55,10 @@ public final class AttributeNames { // cloud provider identifier for the resource being used public static final String CLOUD_RESOURCE_ID = "cloud.resource_id"; + + // Opem Telemetry compatible attributes for host and port + public static final String SERVER_ADDRESS = "server.address"; + public static final String SERVER_PORT = "server.port"; public static final String RESPONSE_CONTENT_TYPE_PARAMETER_NAME = "response.headers.contentType"; // high security matches diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfig.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfig.java index e0d2eb3da7..d1e2d68fd9 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfig.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfig.java @@ -314,8 +314,6 @@ public interface AgentConfig extends com.newrelic.api.agent.Config, DataSenderCo DatastoreConfig getDatastoreConfig(); - MessageBrokerConfig getMessageBrokerConfig(); - ExternalTracerConfig getExternalTracerConfig(); boolean liteMode(); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java index cc6b9fa142..77704c4162 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java @@ -250,7 +250,6 @@ public class AgentConfigImpl extends BaseConfig implements AgentConfig { private final CrossProcessConfig crossProcessConfig; private final DatastoreConfig datastoreConfig; - private final MessageBrokerConfig messageBrokerConfig; private final DistributedTracingConfig distributedTracingConfig; private final ErrorCollectorConfig errorCollectorConfig; private final ExtensionsConfig extensionsConfig; @@ -353,7 +352,6 @@ private AgentConfigImpl(Map props) { labelsConfig = new LabelsConfigImpl(getProperty(LABELS)); utilizationConfig = initUtilizationConfig(); datastoreConfig = initDatastoreConfig(); - messageBrokerConfig = initMessageBrokerConfig(); externalTracerConfig = initExternalTracerConfig(); jfrConfig = initJfrConfig(); jmxConfig = initJmxConfig(); @@ -830,11 +828,6 @@ private DatastoreConfig initDatastoreConfig() { return new DatastoreConfigImpl(props); } - private MessageBrokerConfig initMessageBrokerConfig() { - Map props = nestedProps(MessageBrokerConfigImpl.PROPERTY_NAME); - return new MessageBrokerConfigImpl(props); - } - private ExternalTracerConfig initExternalTracerConfig() { Map props = nestedProps(ExternalTracerConfigImpl.PROPERTY_NAME); return new ExternalTracerConfigImpl(props); @@ -1409,11 +1402,6 @@ public DatastoreConfig getDatastoreConfig() { return datastoreConfig; } - @Override - public MessageBrokerConfig getMessageBrokerConfig() { - return messageBrokerConfig; - } - @Override public ExternalTracerConfig getExternalTracerConfig() { return externalTracerConfig; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java deleted file mode 100644 index 29a0e5d2b4..0000000000 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.newrelic.agent.config; - -public interface MessageBrokerConfig { - /** - * @return true if message_broker_tracer.instance_reporting.enabled is enabled - */ - boolean isInstanceReportingEnabled(); -} diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java deleted file mode 100644 index 41c0653191..0000000000 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/MessageBrokerConfigImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.newrelic.agent.config; - -import java.util.Map; - -public class MessageBrokerConfigImpl extends BaseConfig implements MessageBrokerConfig { - - public static final String ENABLED = "enabled"; - public static final String INSTANCE_REPORTING = "instance_reporting"; - public static final boolean INSTANCE_REPORTING_DEFAULT_ENABLED = true; - - public static final String PROPERTY_NAME = "message_broker_tracer"; - public static final String PROPERTY_ROOT = "newrelic.config." + PROPERTY_NAME + "."; - public static final String DOT = "."; - - private final boolean isInstanceEnabled; - - public MessageBrokerConfigImpl(Map props) { - super(props, PROPERTY_ROOT); - BaseConfig instanceReportConfig = new BaseConfig(nestedProps(INSTANCE_REPORTING), PROPERTY_ROOT + INSTANCE_REPORTING + DOT); - isInstanceEnabled = instanceReportConfig.getProperty(ENABLED, INSTANCE_REPORTING_DEFAULT_ENABLED); - - } - - @Override - public boolean isInstanceReportingEnabled() { - return isInstanceEnabled; - } -} diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java b/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java deleted file mode 100644 index f0ddff05ae..0000000000 --- a/newrelic-agent/src/main/java/com/newrelic/agent/messaging/MessageMetrics.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ -package com.newrelic.agent.messaging; - -import com.newrelic.agent.Transaction; -import com.newrelic.agent.config.Hostname; -import com.newrelic.agent.config.MessageBrokerConfig; -import com.newrelic.agent.service.ServiceFactory; -import com.newrelic.api.agent.DestinationType; -import com.newrelic.api.agent.TracedMethod; - -public class MessageMetrics { - public static final String METRIC_NAMESPACE = "MessageBroker"; - public static final String SLASH = "/"; - public static final String MESSAGE_BROKER_INSTANCE = METRIC_NAMESPACE + "/instance/"; - - public static final String ROUTING_KEY = "RoutingKey"; - - public static final String UNKNOWN = "unknown"; - public static String HOSTNAME = Hostname.getHostname(ServiceFactory.getConfigService().getDefaultAgentConfig()); - - public static void reportInstanceMetric(TracedMethod tracedMethod, String host, Integer port, - DestinationType destinationType, String destination) { - String instanceMetric = buildInstanceMetricIfEnabled(host, port, destinationType, destination); - if (instanceMetric != null && !instanceMetric.trim().isEmpty()) { - tracedMethod.addRollupMetricName(instanceMetric); - } - } - - public static String buildInstanceMetricIfEnabled(String host, Integer port, - DestinationType destinationType, String destination) { - MessageBrokerConfig messageBrokerConfig = ServiceFactory.getConfigService().getDefaultAgentConfig().getMessageBrokerConfig(); - if (messageBrokerConfig.isInstanceReportingEnabled()) { - return buildInstanceMetric(host, port, destinationType, destination); - } - return null; - } - - public static String buildInstanceMetric(String host, Integer port, - DestinationType destinationType, String destination) { - String instance = buildInstanceIdentifier(host, port, destinationType, destination); - return MESSAGE_BROKER_INSTANCE + instance; - } - - public static String buildInstanceIdentifier(String host, Integer port, - DestinationType destinationType, String destination) { - String hostname = replaceLocalhost(host); - String portName = replacePort(port); - String parsedDestination = replaceDestination(destinationType, destination); - - return hostname + SLASH + portName + SLASH + destinationType.getTypeName() + SLASH + parsedDestination; - } - - public static String buildAmqpDestination(String exchangeName, String queueName, String routingKey) { - String amqpSuffix = buildAmqpDestinationSuffix(queueName, routingKey); - if (amqpSuffix != null) { - return null; - } - return DestinationType.EXCHANGE.getTypeName() + SLASH + exchangeName + SLASH + amqpSuffix; - } - - public static String buildAmqpDestinationSuffix(String queueName, String routingKey) { - if (!isParamUnknown(queueName)) { - return DestinationType.NAMED_QUEUE.getTypeName() + SLASH + queueName; - } else if (!isParamUnknown(routingKey)) { - return ROUTING_KEY + SLASH + routingKey; - } - return null; - } - - public static String replaceDestination(DestinationType destinationType, String destination) { - if (destinationType == DestinationType.TEMP_QUEUE || destinationType == DestinationType.TEMP_TOPIC) { - return "Temp"; - } - if (isParamUnknown(destination)) { - return UNKNOWN; - } - return "Named" + SLASH + destination; - } - - public static String replaceLocalhost(String host) { - if (isParamUnknown(host)) { - return UNKNOWN; - } - - if ("localhost".equals(host) || "127.0.0.1".equals(host) || "0.0.0.0".equals(host) - || "0:0:0:0:0:0:0:1".equals(host) || "::1".equals(host) || "0:0:0:0:0:0:0:0".equals(host) - || "::".equals(host)) { - return HOSTNAME; - } - - return host; - } - - public static String replacePort(Integer port) { - if (isParamUnknown(port)) { - return UNKNOWN; - } - return String.valueOf(port); - } - - private static boolean isParamUnknown(String str) { - return str == null || str.isEmpty(); - } - - private static boolean isParamUnknown(Integer integer) { - return integer == null || integer == -1; - } -} 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 4fd60056b5..3bca6e403f 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 @@ -278,7 +278,7 @@ public SpanEventFactory setAddress(String hostName, String portPathOrId) { } public SpanEventFactory setServerAddress(String host) { - builder.putAgentAttribute("server.address", host); + builder.putAgentAttribute(AttributeNames.SERVER_ADDRESS, host); builder.putAgentAttribute("peer.hostname", host); return this; } @@ -289,7 +289,7 @@ public SpanEventFactory setCloudResourceId(String cloudResourceId) { } public SpanEventFactory setServerPort(int port) { - builder.putAgentAttribute("server.port", port); + builder.putAgentAttribute(AttributeNames.SERVER_PORT, port); return this; } @@ -410,16 +410,32 @@ public SpanEventFactory setExternalParameterAttributes(ExternalParameters parame MessageProduceParameters messageProduceParameters = (MessageProduceParameters) parameters; setCategory(SpanCategory.generic); setCloudResourceId(messageProduceParameters.getCloudResourceId()); + setServerAddress(messageProduceParameters.getHost()); + setServerPort(messageProduceParameters.getPort()); } else if (parameters instanceof MessageConsumeParameters) { MessageConsumeParameters messageConsumeParameters = (MessageConsumeParameters) parameters; setCategory(SpanCategory.generic); setCloudResourceId(messageConsumeParameters.getCloudResourceId()); + setServerAddress(messageConsumeParameters.getHost()); + setServerPort(messageConsumeParameters.getPort()); } else { setCategory(SpanCategory.generic); } return this; } + public SpanEventFactory setAgentAttributesMarkedForSpans(Set agentAttributesMarkedForSpans, Map agentAttributes) { + if (agentAttributesMarkedForSpans != null) { + for (String attributeName: agentAttributesMarkedForSpans) { + Object value = agentAttributes.get(attributeName); + if (value != null) { + builder.putAgentAttribute(attributeName, value); + } + } + } + return this; + } + private String determineObfuscationLevel(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { AgentConfig config = ServiceFactory.getConfigService().getDefaultAgentConfig(); if (config.isHighSecurity() || config.getTransactionTracerConfig().getRecordSql().equals(SqlObfuscator.OFF_SETTING)) { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/TracerToSpanEvent.java b/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/TracerToSpanEvent.java index e40c7d243b..5f6d666bc3 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/TracerToSpanEvent.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/TracerToSpanEvent.java @@ -7,7 +7,6 @@ package com.newrelic.agent.service.analytics; -import com.google.common.base.Predicate; import com.google.common.collect.Maps; import com.newrelic.agent.TransactionData; import com.newrelic.agent.attributes.AttributesUtils; @@ -94,6 +93,7 @@ public SpanEvent createSpanEvent(Tracer tracer, TransactionData transactionData, .setTimestamp(tracer.getStartTimeInMillis()) .setPriority(transactionData.getPriority()) .setExternalParameterAttributes(tracer.getExternalParameters()) + .setAgentAttributesMarkedForSpans(tracer.GetAgentAttributeNamesForSpans(), tracer.getAgentAttributes()) .setStackTraceAttributes(tracer.getAgentAttributes()) .setIsRootSpanEvent(isRoot) .setDecider(inboundPayload == null || inboundPayload.priority == null); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/AbstractTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/AbstractTracer.java index 34f690dfff..ded64b9da3 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/AbstractTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/AbstractTracer.java @@ -40,12 +40,15 @@ public abstract class AbstractTracer implements Tracer, AttributeHolder { static final int INITIAL_PARAMETER_MAP_SIZE = 5; + static final int INITIAL_PARAMETER_SET_SIZE = 5; protected static final String ATTRIBUTE_TYPE = "custom"; private final TransactionActivity transactionActivity; private AttributeValidator attributeValidator; private Set rollupMetricNames; private Set exclusiveRollupMetricNames; + + private Set agentAttributeNamesForSpans; Map customAttributes; Map agentAttributes; private String customPrefix = "Custom"; @@ -337,7 +340,7 @@ private void setAttributeIfValid(String key, Object value) { Object verifiedValue = attributeValidator.verifyParameterAndReturnValue( key, value, ATTRIBUTE_API_METHOD_NAME); if (verifiedValue != null) { - setAttribute(key, verifiedValue, true, true); + setAttribute(key, verifiedValue, true, true, false); } } @@ -345,7 +348,7 @@ private boolean shouldAddAttribute() { return getTransaction() != null && !getTransaction().getTransactionCounts().isOverTracerSegmentLimit(); } - public void setAttribute(String key, Object value, boolean checkLimits, boolean isCustom) { + public void setAttribute(String key, Object value, boolean checkLimits, boolean isCustom, boolean addAgentAttrToSpan) { if (checkLimits && !shouldAddAttribute()) { return; } @@ -368,6 +371,12 @@ public void setAttribute(String key, Object value, boolean checkLimits, boolean agentAttributes = new HashMap<>(1, INITIAL_PARAMETER_MAP_SIZE); } agentAttributes.put(key, value); + if(addAgentAttrToSpan) { + if (agentAttributeNamesForSpans == null) { + agentAttributeNamesForSpans = new HashSet<>(INITIAL_PARAMETER_SET_SIZE); + } + agentAttributeNamesForSpans.add(key); + } } } @@ -391,7 +400,12 @@ static int sizeof(Object value) { @Override public void setAgentAttribute(String key, Object value) { - setAttribute(key, value, true, false); + setAttribute(key, value, true, false, false); + } + + @Override + public void setAgentAttribute(String key, Object value, boolean addToSpan) { + setAttribute(key, value, true, false, addToSpan); } @Override @@ -422,6 +436,14 @@ public Map getCustomAttributes() { return Collections.unmodifiableMap(customAttributes); } + @Override + public Set GetAgentAttributeNamesForSpans() { + if (agentAttributeNamesForSpans == null) { + return Collections.emptySet(); + } + return Collections.unmodifiableSet(agentAttributeNamesForSpans); + } + public Object getCustomAttribute(String key) { return ((customAttributes == null) ? null : customAttributes.get(key)); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java index 9f0d68dc3e..d2802c4b58 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/DefaultTracer.java @@ -820,6 +820,15 @@ private void recordMessageBrokerMetrics(MessageProduceParameters messageProduceP messageProduceParameters.getLibrary(), messageProduceParameters.getDestinationType().getTypeName())); } + if (messageProduceParameters.getCloudResourceId() != null) { + setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageProduceParameters.getCloudResourceId()); + } + if (messageProduceParameters.getHost() != null) { + setAgentAttribute(AttributeNames.SERVER_ADDRESS, messageProduceParameters.getHost()); + } + if (messageProduceParameters.getPort() != null) { + setAgentAttribute(AttributeNames.SERVER_PORT, messageProduceParameters.getPort()); + } } private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeParameters) { @@ -843,6 +852,12 @@ private void recordMessageBrokerMetrics(MessageConsumeParameters messageConsumeP if (messageConsumeParameters.getCloudResourceId() != null) { setAgentAttribute(AttributeNames.CLOUD_RESOURCE_ID, messageConsumeParameters.getCloudResourceId()); } + if (messageConsumeParameters.getHost() != null) { + setAgentAttribute(AttributeNames.SERVER_ADDRESS, messageConsumeParameters.getHost()); + } + if (messageConsumeParameters.getPort() != null) { + setAgentAttribute(AttributeNames.SERVER_PORT, messageConsumeParameters.getPort()); + } } private void recordSlowQueryData(SlowQueryDatastoreParameters slowQueryDatastoreParameters) { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/NoOpTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/NoOpTracer.java index d10934477c..07f3ef4f6c 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/NoOpTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/NoOpTracer.java @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.Map; +import java.util.Set; public final class NoOpTracer implements Tracer { @@ -204,6 +205,11 @@ public void addExclusiveRollupMetricName(String... metricNameParts) { public void setAgentAttribute(String key, Object value) { } + @Override + public void setAgentAttribute(String key, Object value, boolean addToSpan) { + + } + @Override public void removeAgentAttribute(String key) { } @@ -285,6 +291,11 @@ public ExternalParameters getExternalParameters() { return null; } + @Override + public Set GetAgentAttributeNamesForSpans() { + return null; + } + @Override public void setNoticedError(Throwable throwable) { } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/Tracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/Tracer.java index 5d92adc397..17808113a5 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/Tracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/Tracer.java @@ -14,8 +14,8 @@ import com.newrelic.agent.trace.TransactionSegment; import com.newrelic.api.agent.ExternalParameters; -import java.lang.reflect.InvocationHandler; import java.util.Map; +import java.util.Set; /** * A tracer records information about a method invocation - primarily the start and stop time of the invocation. A @@ -92,6 +92,11 @@ public interface Tracer extends TimedItem, ExitTracer, ErrorTracer { */ void setAgentAttribute(String key, Object value); + /** + * Add some extra information to the invocation (Like the sql statement for a sql tracer). + */ + void setAgentAttribute(String key, Object value, boolean addToSpan); + /** * Remove attribute. * @@ -155,4 +160,10 @@ TransactionSegment getTransactionSegment(TransactionTracerConfig ttConfig, SqlOb ExternalParameters getExternalParameters(); + /** + * Returns the set of agent attribute names that are marked to be added to span events. + *

+ * Note: Some attributes will be added to spans even if they are not in the returned set. + */ + Set GetAgentAttributeNamesForSpans(); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/UltraLightTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/UltraLightTracer.java index 4b4929b04f..182e59ccc3 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/UltraLightTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/UltraLightTracer.java @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @@ -245,6 +246,10 @@ public String getTransactionSegmentUri() { public void setAgentAttribute(String key, Object value) { } + @Override + public void setAgentAttribute(String key, Object value, boolean addToSpan) { + } + @Override public void removeAgentAttribute(String key) { } @@ -306,6 +311,11 @@ public com.newrelic.api.agent.ExternalParameters getExternalParameters() { return null; } + @Override + public Set GetAgentAttributeNamesForSpans() { + return null; + } + @Override public void setNoticedError(Throwable throwable) { } diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/sql/NoOpTrackingSqlTracer.java b/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/sql/NoOpTrackingSqlTracer.java index c665475500..7790ecac49 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/sql/NoOpTrackingSqlTracer.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/sql/NoOpTrackingSqlTracer.java @@ -11,6 +11,7 @@ import java.sql.Connection; import java.util.Collections; import java.util.Map; +import java.util.Set; import com.newrelic.agent.Transaction; import com.newrelic.agent.TransactionActivity; @@ -191,6 +192,11 @@ public void setAgentAttribute(String key, Object value) { } + @Override + public void setAgentAttribute(String key, Object value, boolean addToSpan) { + + } + @Override public Object getAgentAttribute(String key) { return null; @@ -348,6 +354,11 @@ public ExternalParameters getExternalParameters() { return null; } + @Override + public Set GetAgentAttributeNamesForSpans() { + return null; + } + @Override public void setNoticedError(Throwable throwable) { } diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/weaver/preprocessors/AgentPostprocessorsTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/weaver/preprocessors/AgentPostprocessorsTest.java index 7f9fdace54..75c0bcd0b7 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/weaver/preprocessors/AgentPostprocessorsTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/weaver/preprocessors/AgentPostprocessorsTest.java @@ -899,6 +899,8 @@ public void privateApiTracerParameter() { AgentBridge.privateApi.addTracerParameter("key1", 10); AgentBridge.privateApi.addTracerParameter("key1", Collections.emptyMap()); AgentBridge.privateApi.addTracerParameter("key1", "value"); + AgentBridge.privateApi.addTracerParameter("key2", "value", true); + AgentBridge.privateApi.addTracerParameter("key3", "value", false); } public void privateApiReportError() { diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java deleted file mode 100644 index f8ce81cd26..0000000000 --- a/newrelic-agent/src/test/java/com/newrelic/agent/messaging/MessageMetricsTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ -package com.newrelic.agent.messaging; - -import com.newrelic.agent.AgentHelper; -import com.newrelic.api.agent.DestinationType; -import com.newrelic.api.agent.TracedMethod; -import org.junit.Before; -import org.junit.Test; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.text.MessageFormat; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; - -public class MessageMetricsTest { - - private final String hostName; - - TracedMethod mockTracedMethod; - - public MessageMetricsTest() throws UnknownHostException { - this.hostName = InetAddress.getLocalHost().getHostName(); - } - - @Before - public void before() throws Exception { - AgentHelper.bootstrap(AgentHelper.createAgentConfig(true)); - mockTracedMethod = mock(TracedMethod.class); - } - - @Test - public void testReplaceEmptyPort() { - assertEquals(MessageMetrics.UNKNOWN, MessageMetrics.replacePort(null)); - assertEquals(MessageMetrics.UNKNOWN, MessageMetrics.replacePort(-1)); - assertEquals("1234", MessageMetrics.replacePort(1234)); - } - - @Test - public void testReplaceLocalhost() throws Exception { - assertEquals(MessageMetrics.UNKNOWN, MessageMetrics.replaceLocalhost(null)); - - final String hostname = InetAddress.getLocalHost().getHostName(); - - assertEquals(hostname, MessageMetrics.replaceLocalhost("localhost")); - assertEquals(hostname, MessageMetrics.replaceLocalhost("127.0.0.1")); - assertEquals(hostname, MessageMetrics.replaceLocalhost("0.0.0.0")); - assertEquals(hostname, MessageMetrics.replaceLocalhost("0:0:0:0:0:0:0:1")); - assertEquals(hostname, MessageMetrics.replaceLocalhost("::1")); - assertEquals(hostname, MessageMetrics.replaceLocalhost("0:0:0:0:0:0:0:0")); - assertEquals(hostname, MessageMetrics.replaceLocalhost("::")); - - assertEquals("example.com", MessageMetrics.replaceLocalhost("example.com")); - } - - @Test - public void testBuildInstanceMetrics() { - assertEquals( - MessageFormat.format("MessageBroker/instance/{0}/8080/Topic/Named/topic", hostName), - MessageMetrics.buildInstanceMetricIfEnabled("localhost", 8080, DestinationType.NAMED_TOPIC, "topic")); - - assertEquals( - "MessageBroker/instance/broker.com/8080/Topic/Named/topic2", - MessageMetrics.buildInstanceMetricIfEnabled("broker.com", 8080, DestinationType.NAMED_TOPIC, "topic2")); - - assertEquals( - "MessageBroker/instance/broker.com/8080/Queue/Named/queue2", - MessageMetrics.buildInstanceMetricIfEnabled("broker.com", 8080, DestinationType.NAMED_QUEUE, "queue2")); - - assertEquals( - "MessageBroker/instance/broker.com/8080/Topic/Temp", - MessageMetrics.buildInstanceMetricIfEnabled( "broker.com", 8080, DestinationType.TEMP_TOPIC, null)); - - assertEquals( - "MessageBroker/instance/broker.com/8081/Queue/Temp", - MessageMetrics.buildInstanceMetricIfEnabled( "broker.com", 8081, DestinationType.TEMP_QUEUE, null)); - - assertEquals( - "MessageBroker/instance/example.com/8080/Exchange/Named/exchange/Queue/Named/someQueue", - MessageMetrics.buildInstanceMetricIfEnabled("example.com", 8080, - DestinationType.EXCHANGE, "exchange/Queue/Named/someQueue")); - - assertEquals("MessageBroker/instance/unknown/8080/Exchange/Named/exchange", - MessageMetrics.buildInstanceMetricIfEnabled(null, 8080, DestinationType.EXCHANGE, "exchange")); - - assertEquals("MessageBroker/instance/example.com/unknown/Exchange/Named/exchange", - MessageMetrics.buildInstanceMetricIfEnabled("example.com", null, DestinationType.EXCHANGE, "exchange")); - - assertEquals("MessageBroker/instance/example.com/8080/Exchange/unknown", - MessageMetrics.buildInstanceMetricIfEnabled("example.com", 8080, DestinationType.EXCHANGE, null)); - - assertEquals("MessageBroker/instance/unknown/unknown/Exchange/unknown", - MessageMetrics.buildInstanceMetricIfEnabled(null, null, DestinationType.EXCHANGE, null)); - - } -} 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 a59d7a711a..d7188c0bc8 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 @@ -8,6 +8,7 @@ package com.newrelic.agent.service.analytics; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.newrelic.agent.MockConfigService; import com.newrelic.agent.MockServiceManager; import com.newrelic.agent.attributes.AttributeNames; @@ -29,6 +30,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; +import java.util.Set; import static com.newrelic.agent.service.analytics.SpanEventFactory.DEFAULT_SYSTEM_TIMESTAMP_SUPPLIER; import static org.junit.Assert.assertEquals; @@ -295,6 +297,42 @@ public void shouldSetCloudResourceIdOnSpanFromMessageConsumeParameters() { assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); } + @Test + public void shouldSetInstanceOnSpanFromMessageProduceParameters() { + String expectedHost = "example.com"; + Integer expectedPort = 8080; + MessageProduceParameters mockParameters = mock(MessageProduceParameters.class); + when(mockParameters.getLibrary()).thenReturn("SQS"); + when(mockParameters.getDestinationName()).thenReturn("queueName"); + when(mockParameters.getDestinationType()).thenReturn(DestinationType.NAMED_QUEUE); + when(mockParameters.getHost()).thenReturn(expectedHost); + when(mockParameters.getPort()).thenReturn(expectedPort); + SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); + + Map agentAttrs = target.getAgentAttributes(); + assertEquals(expectedHost, agentAttrs.get("server.address")); + assertEquals(expectedHost, agentAttrs.get("peer.hostname")); + assertEquals(expectedPort, agentAttrs.get("server.port")); + } + + @Test + public void shouldSetInstanceOnSpanFromMessageConsumeParameters() { + String expectedHost = "example.com"; + Integer expectedPort = 8080; + MessageConsumeParameters mockParameters = mock(MessageConsumeParameters.class); + when(mockParameters.getLibrary()).thenReturn("SQS"); + when(mockParameters.getDestinationName()).thenReturn("queueName"); + when(mockParameters.getDestinationType()).thenReturn(DestinationType.NAMED_QUEUE); + when(mockParameters.getHost()).thenReturn(expectedHost); + when(mockParameters.getPort()).thenReturn(expectedPort); + SpanEvent target = spanEventFactory.setExternalParameterAttributes(mockParameters).build(); + + Map agentAttrs = target.getAgentAttributes(); + assertEquals(expectedHost, agentAttrs.get("server.address")); + assertEquals(expectedHost, agentAttrs.get("peer.hostname")); + assertEquals(expectedPort, agentAttrs.get("server.port")); + } + @Test public void shouldStoreStackTrace() { SpanEventFactory spanEventFactory = new SpanEventFactory("MyApp", new AttributeFilter.PassEverythingAttributeFilter(), @@ -310,6 +348,19 @@ public void shouldStoreStackTrace() { assertNotNull(stackTrace); } + @Test + public void shouldStoreAgentAttributesMarkedForSpans() { + Map agentAttributes = ImmutableMap.of( + "key1", "v1", + "key2", "v2" + ); + Set attributesToBeAddedToSpans = ImmutableSet.of("key1", "key3"); + SpanEvent target = spanEventFactory.setAgentAttributesMarkedForSpans(attributesToBeAddedToSpans, agentAttributes).build(); + assertEquals("v1", target.getAgentAttributes().get("key1")); + assertNull(target.getIntrinsics().get("key2")); + assertNull(target.getIntrinsics().get("key3")); + } + @Test public void shouldSetCLMParameters() { Map agentAttributes = ImmutableMap.of( 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 4870a274b3..a09fcf8170 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 @@ -31,7 +31,9 @@ import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.function.Supplier; import static com.newrelic.agent.MetricNames.QUEUE_TIME; @@ -50,6 +52,7 @@ import static com.newrelic.agent.attributes.AttributeNames.REQUEST_USER_AGENT_PARAMETER_NAME; import static com.newrelic.agent.attributes.AttributeNames.RESPONSE_CONTENT_TYPE_PARAMETER_NAME; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -83,6 +86,7 @@ public class TracerToSpanEventTest { private Map transactionUserAttributes; private Map tracerAgentAttributes; private Map tracerUserAttributes; + private Set tracerAgentAttributeNamesMarkedForSpans; private SpanProxy spanProxy; private SpanErrorBuilder spanErrorBuilder; private TransactionThrowable throwable; @@ -98,6 +102,7 @@ public void setup() { transactionUserAttributes = new HashMap<>(); tracerAgentAttributes = new HashMap<>(); tracerUserAttributes = new HashMap<>(); + tracerAgentAttributeNamesMarkedForSpans = new HashSet<>(); expectedAgentAttributes = new HashMap<>(); expectedUserAttributes = new HashMap<>(); expectedAgentAttributes.put("error.class", "0"); @@ -132,6 +137,7 @@ public void setup() { when(tracer.getStartTimeInMillis()).thenReturn(timestamp); when(tracer.getAgentAttributes()).thenReturn(tracerAgentAttributes); when(tracer.getCustomAttributes()).thenReturn(tracerUserAttributes); + when(tracer.GetAgentAttributeNamesForSpans()).thenReturn(tracerAgentAttributeNamesMarkedForSpans); when(spanErrorBuilder.buildSpanError(tracer, isRoot, responseStatus, statusMessage, throwable)).thenReturn(spanError); when(spanErrorBuilder.areErrorsEnabled()).thenReturn(true); when(txnData.getApplicationName()).thenReturn(appName); @@ -513,6 +519,30 @@ public void testUndesiredAttributesFiltered() { assertEquals(expectedSpanEvent, spanEvent); } + @Test + public void testAgentAttributesMarkedForSpansAdded() { + // set up + + tracerAgentAttributes.put("key1", "v1"); + tracerAgentAttributes.put("key2", "v2"); + + tracerAgentAttributeNamesMarkedForSpans.add("key1"); + tracerAgentAttributeNamesMarkedForSpans.add("key3"); + + when(txnData.getAgentAttributes()).thenReturn(transactionAgentAttributes); + + TracerToSpanEvent testClass = new TracerToSpanEvent(errorBuilderMap, new AttributeFilter.PassEverythingAttributeFilter(), timestampProvider, + environmentService, transactionDataToDistributedTraceIntrinsics, spanErrorBuilder); + + // execution + SpanEvent spanEvent = testClass.createSpanEvent(tracer, txnData, txnStats, true, false); + + // assertions + assertEquals("v1", spanEvent.getAgentAttributes().get("key1")); + assertNull(spanEvent.getAgentAttributes().get("key2")); + assertNull(spanEvent.getAgentAttributes().get("key3")); + } + @Test public void testErrorCollectorDisabled() { // setup diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java index 68b31594be..f3829d04a4 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageConsumeParameters.java @@ -22,13 +22,18 @@ public class MessageConsumeParameters implements ExternalParameters { private final InboundHeaders inboundHeaders; private final String cloudResourceId; + private final String host; + private final Integer port; + protected MessageConsumeParameters(String library, DestinationType destinationType, String destinationName, - InboundHeaders inboundHeaders, String cloudResourceId) { + InboundHeaders inboundHeaders, String cloudResourceId, String host, Integer port) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; this.inboundHeaders = inboundHeaders; this.cloudResourceId = cloudResourceId; + this.host = host; + this.port = port; } @@ -43,6 +48,8 @@ protected MessageConsumeParameters(MessageConsumeParameters messageConsumeParame this.destinationName = messageConsumeParameters.destinationName; this.inboundHeaders = messageConsumeParameters.inboundHeaders; this.cloudResourceId = messageConsumeParameters.cloudResourceId; + this.host = messageConsumeParameters.host; + this.port = messageConsumeParameters.port; } public String getDestinationName() { @@ -65,6 +72,14 @@ public String getLibrary() { return library; } + public String getHost() { + return host; + } + + public Integer getPort() { + return port; + } + protected static class Builder implements DestinationTypeParameter, DestinationNameParameter, InboundHeadersParameter, Build { private String library; @@ -72,6 +87,8 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String destinationName; private InboundHeaders inboundHeaders; private String cloudResourceId; + private String host; + private Integer port; public Builder(String library) { this.library = library; @@ -97,8 +114,14 @@ public Build cloudResourceId(String cloudResourceId) { return this; } + public Build instance(String host, Integer port) { + this.host = host; + this.port = port; + return this; + } + public MessageConsumeParameters build() { - return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId); + return new MessageConsumeParameters(library, destinationType, destinationName, inboundHeaders, cloudResourceId, host, port); } } @@ -153,6 +176,16 @@ public interface Build { */ Build cloudResourceId(String cloudResourceId); + /** + * Set the host and port of the message broker. + * This method is optional and can be bypassed by calling build directly. + * + * @param host The host where the message broker is located + * @param port The port for the connection to the message broker + * @return the next builder interface + */ + Build instance(String host, Integer port); + /** * Build the final {@link MessageConsumeParameters} for the API call. * diff --git a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java index 3c22f3f635..045bbfb07a 100644 --- a/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java +++ b/newrelic-api/src/main/java/com/newrelic/api/agent/MessageProduceParameters.java @@ -20,13 +20,17 @@ public class MessageProduceParameters implements ExternalParameters { private final String destinationName; private final OutboundHeaders outboundHeaders; private final String cloudResourceId; + private final String host; + private final Integer port; protected MessageProduceParameters(String library, DestinationType destinationType, String destinationName, - OutboundHeaders outboundHeaders, String cloudResourceId) { + OutboundHeaders outboundHeaders, String cloudResourceId, String host, Integer port) { this.library = library; this.destinationType = destinationType; this.destinationName = destinationName; this.outboundHeaders = outboundHeaders; this.cloudResourceId = cloudResourceId; + this.host = host; + this.port = port; } /** @@ -40,6 +44,8 @@ protected MessageProduceParameters(MessageProduceParameters messageProduceParame this.destinationName = messageProduceParameters.destinationName; this.outboundHeaders = messageProduceParameters.outboundHeaders; this.cloudResourceId = messageProduceParameters.cloudResourceId; + this.host = messageProduceParameters.host; + this.port = messageProduceParameters.port; } public String getDestinationName() { @@ -62,6 +68,14 @@ public String getLibrary() { return library; } + public String getHost() { + return host; + } + + public Integer getPort() { + return port; + } + protected static class Builder implements DestinationTypeParameter, DestinationNameParameter, OutboundHeadersParameter, Build { private String library; @@ -69,6 +83,8 @@ protected static class Builder implements DestinationTypeParameter, DestinationN private String destinationName; private OutboundHeaders outboundHeaders; private String cloudResourceId; + private String host; + private Integer port; public Builder(String library) { this.library = library; @@ -94,8 +110,14 @@ public Build cloudResourceId(String cloudResourceId) { return this; } + public Build instance(String host, Integer port) { + this.host = host; + this.port = port; + return this; + } + public MessageProduceParameters build() { - return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId); + return new MessageProduceParameters(library, destinationType, destinationName, outboundHeaders, cloudResourceId, host, port); } } @@ -151,6 +173,16 @@ public interface Build { */ Build cloudResourceId(String cloudResourceId); + /** + * Set the host and port of the message broker. + * This method is optional and can be bypassed by calling build directly. + * + * @param host The host where the message broker is located + * @param port The port for the connection to the message broker + * @return the next builder interface + */ + Build instance(String host, Integer port); + /** * Build the final {@link MessageProduceParameters} for the API call. * From 8a0aabfe5b2b970678c34906c39b40abf1bcc15a Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 3 Jun 2024 17:48:35 -0400 Subject: [PATCH 35/42] Remove unneeded configuration --- newrelic-agent/src/main/resources/newrelic.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/newrelic-agent/src/main/resources/newrelic.yml b/newrelic-agent/src/main/resources/newrelic.yml index 7ebd6bdf4d..259456e73f 100644 --- a/newrelic-agent/src/main/resources/newrelic.yml +++ b/newrelic-agent/src/main/resources/newrelic.yml @@ -452,12 +452,6 @@ common: &default_settings # with every SlowTransaction event, if a large number of transaction exceed the threshold. evaluate_completed_transactions: false - # Configuration settings for reporting external calls to/from message brokers - message_broker_tracer: - # Determines if the message broker endpoint (host & port) is reported. By default it is enabled. - instance_reporting: - enabled: true - # Application Environments # ------------------------------------------ # Environment specific settings are in this section. From afbef10b751bf06e94592d11a09be79b3ed8106b Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 3 Jun 2024 17:54:21 -0400 Subject: [PATCH 36/42] Add test for agent attribute names for spans empty in tracer --- .../agent/service/analytics/TracerToSpanEvent.java | 2 +- .../java/com/newrelic/agent/tracers/AbstractTracer.java | 2 +- .../main/java/com/newrelic/agent/tracers/NoOpTracer.java | 2 +- .../src/main/java/com/newrelic/agent/tracers/Tracer.java | 2 +- .../java/com/newrelic/agent/tracers/UltraLightTracer.java | 2 +- .../agent/instrumentation/sql/NoOpTrackingSqlTracer.java | 2 +- .../agent/service/analytics/TracerToSpanEventTest.java | 2 +- .../agent/tracers/MethodExitTracerNoSkipTest.java | 8 ++++++++ 8 files changed, 15 insertions(+), 7 deletions(-) diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/TracerToSpanEvent.java b/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/TracerToSpanEvent.java index 5f6d666bc3..fceee04c62 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/TracerToSpanEvent.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/service/analytics/TracerToSpanEvent.java @@ -93,7 +93,7 @@ public SpanEvent createSpanEvent(Tracer tracer, TransactionData transactionData, .setTimestamp(tracer.getStartTimeInMillis()) .setPriority(transactionData.getPriority()) .setExternalParameterAttributes(tracer.getExternalParameters()) - .setAgentAttributesMarkedForSpans(tracer.GetAgentAttributeNamesForSpans(), tracer.getAgentAttributes()) + .setAgentAttributesMarkedForSpans(tracer.getAgentAttributeNamesForSpans(), tracer.getAgentAttributes()) .setStackTraceAttributes(tracer.getAgentAttributes()) .setIsRootSpanEvent(isRoot) .setDecider(inboundPayload == null || inboundPayload.priority == null); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/AbstractTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/AbstractTracer.java index ded64b9da3..aed4933a72 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/AbstractTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/AbstractTracer.java @@ -437,7 +437,7 @@ public Map getCustomAttributes() { } @Override - public Set GetAgentAttributeNamesForSpans() { + public Set getAgentAttributeNamesForSpans() { if (agentAttributeNamesForSpans == null) { return Collections.emptySet(); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/NoOpTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/NoOpTracer.java index 07f3ef4f6c..92a0dabc50 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/NoOpTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/NoOpTracer.java @@ -292,7 +292,7 @@ public ExternalParameters getExternalParameters() { } @Override - public Set GetAgentAttributeNamesForSpans() { + public Set getAgentAttributeNamesForSpans() { return null; } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/Tracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/Tracer.java index 17808113a5..960d449edf 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/Tracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/Tracer.java @@ -165,5 +165,5 @@ TransactionSegment getTransactionSegment(TransactionTracerConfig ttConfig, SqlOb *

* Note: Some attributes will be added to spans even if they are not in the returned set. */ - Set GetAgentAttributeNamesForSpans(); + Set getAgentAttributeNamesForSpans(); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/UltraLightTracer.java b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/UltraLightTracer.java index 182e59ccc3..5961f052a2 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/tracers/UltraLightTracer.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/tracers/UltraLightTracer.java @@ -312,7 +312,7 @@ public com.newrelic.api.agent.ExternalParameters getExternalParameters() { } @Override - public Set GetAgentAttributeNamesForSpans() { + public Set getAgentAttributeNamesForSpans() { return null; } diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/sql/NoOpTrackingSqlTracer.java b/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/sql/NoOpTrackingSqlTracer.java index 7790ecac49..172dce6798 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/sql/NoOpTrackingSqlTracer.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/instrumentation/sql/NoOpTrackingSqlTracer.java @@ -355,7 +355,7 @@ public ExternalParameters getExternalParameters() { } @Override - public Set GetAgentAttributeNamesForSpans() { + public Set getAgentAttributeNamesForSpans() { return null; } 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 a09fcf8170..e41bf3db74 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 @@ -137,7 +137,7 @@ public void setup() { when(tracer.getStartTimeInMillis()).thenReturn(timestamp); when(tracer.getAgentAttributes()).thenReturn(tracerAgentAttributes); when(tracer.getCustomAttributes()).thenReturn(tracerUserAttributes); - when(tracer.GetAgentAttributeNamesForSpans()).thenReturn(tracerAgentAttributeNamesMarkedForSpans); + when(tracer.getAgentAttributeNamesForSpans()).thenReturn(tracerAgentAttributeNamesMarkedForSpans); when(spanErrorBuilder.buildSpanError(tracer, isRoot, responseStatus, statusMessage, throwable)).thenReturn(spanError); when(spanErrorBuilder.areErrorsEnabled()).thenReturn(true); when(txnData.getApplicationName()).thenReturn(appName); diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/tracers/MethodExitTracerNoSkipTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/tracers/MethodExitTracerNoSkipTest.java index a54d146fcc..508911e4bf 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/tracers/MethodExitTracerNoSkipTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/tracers/MethodExitTracerNoSkipTest.java @@ -15,6 +15,7 @@ import java.util.Collections; import static org.junit.Assert.assertEquals; +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; @@ -89,6 +90,13 @@ public void timingMethods_allReturnZero() { } @Test public void fetchingAgentAttributes_returnsNull() { + MethodExitTracerNoSkip instance = new TestMethodExitTracerNoSkip(classMethodSignature, mockTxn); + assertEquals(0, instance.getAgentAttributeNamesForSpans().size()); + assertFalse(instance.getAgentAttributeNamesForSpans().contains("foo")); + } + + @Test + public void fetchingAgentAttributeNamesMarkedForSpans_returnsEmptySet() { MethodExitTracerNoSkip instance = new TestMethodExitTracerNoSkip(classMethodSignature, mockTxn); assertEquals(0, instance.getAgentAttributes().size()); assertNull(instance.getAgentAttribute("foo")); From aa8f02a8d38412168fd26baab8af71e6cf77602d Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Tue, 4 Jun 2024 12:18:06 -0400 Subject: [PATCH 37/42] Add missing overrides for the private API --- .../nr/instrumentation/graphql/helper/PrivateApiStub.java | 5 +++++ .../nr/instrumentation/graphql/helper/PrivateApiStub.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/instrumentation/graphql-java-17.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java b/instrumentation/graphql-java-17.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java index 971076d338..3d751f852d 100644 --- a/instrumentation/graphql-java-17.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java +++ b/instrumentation/graphql-java-17.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java @@ -57,6 +57,11 @@ public void addTracerParameter(String key, String value) { tracerParameters.put(key, value); } + @Override + public void addTracerParameter(String key, String value, boolean addToSpan) { + + } + @Override public void addTracerParameter(String key, Map values) { diff --git a/instrumentation/graphql-java-21.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java b/instrumentation/graphql-java-21.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java index f5ed2da690..94275c8f00 100644 --- a/instrumentation/graphql-java-21.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java +++ b/instrumentation/graphql-java-21.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java @@ -60,6 +60,11 @@ public void addTracerParameter(String key, String value) { tracerParameters.put(key, value); } + @Override + public void addTracerParameter(String key, String value, boolean addToSpan) { + + } + @Override public void addTracerParameter(String key, Map values) { From 2aa42d0803008f5656b4f30b56c29cb175df028b Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Tue, 4 Jun 2024 12:32:29 -0400 Subject: [PATCH 38/42] Fix graphql 22 compilation for new private API changes --- .../nr/instrumentation/graphql/helper/PrivateApiStub.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/instrumentation/graphql-java-22.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java b/instrumentation/graphql-java-22.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java index f5ed2da690..94275c8f00 100644 --- a/instrumentation/graphql-java-22.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java +++ b/instrumentation/graphql-java-22.0/src/test/java/com/nr/instrumentation/graphql/helper/PrivateApiStub.java @@ -60,6 +60,11 @@ public void addTracerParameter(String key, String value) { tracerParameters.put(key, value); } + @Override + public void addTracerParameter(String key, String value, boolean addToSpan) { + + } + @Override public void addTracerParameter(String key, Map values) { From fe5cc8ffd4668b3512b5cb57a5830c4a02f00896 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Wed, 5 Jun 2024 14:57:46 -0400 Subject: [PATCH 39/42] Add otel compatible routing key attribute for RabbitMQ --- .../instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java | 4 +++- .../instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java | 4 +++- .../instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java | 4 +++- .../instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java | 4 +++- .../instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java | 4 +++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index 1950362609..0ce1533098 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -96,7 +96,9 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + // Add Open Telemetry attribute for routing key to be added to spans + AgentBridge.privateApi.addTracerParameter("messaging.rabbitmq.destination.routing_key", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java index 3bedbb2537..de6822762e 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -104,7 +104,9 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + // Add Open Telemetry attribute for routing key to be added to spans + AgentBridge.privateApi.addTracerParameter("messaging.rabbitmq.destination.routing_key", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index a737c35af4..c86a869076 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -104,7 +104,9 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + // Add Open Telemetry attribute for routing key to be added to spans + AgentBridge.privateApi.addTracerParameter("messaging.rabbitmq.destination.routing_key", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java index f2720fe8c9..db3953353a 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -104,7 +104,9 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + // Add Open Telemetry attribute for routing key to be added to spans + AgentBridge.privateApi.addTracerParameter("messaging.rabbitmq.destination.routing_key", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index c4cb08431e..da23208c70 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -105,7 +105,9 @@ private static void addAttributes(String routingKey, AMQP.BasicProperties proper return; } - AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey, true); + AgentBridge.privateApi.addTracerParameter("message.routingKey", routingKey); + // Add Open Telemetry attribute for routing key to be added to spans + AgentBridge.privateApi.addTracerParameter("messaging.rabbitmq.destination.routing_key", routingKey, true); if (properties.getReplyTo() != null) { AgentBridge.privateApi.addTracerParameter("message.replyTo", properties.getReplyTo()); } From 2eab2bfe20d9151a96e704c09b9daa16f25ae60f Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Wed, 5 Jun 2024 17:59:11 -0400 Subject: [PATCH 40/42] Add otel attributes for rabbitMQ queues and exchanges --- .../rabbitamqp172/RabbitAMQPMetricUtil.java | 19 ++++++++++++++++--- .../client/Consumer_Instrumentation.java | 2 +- .../rabbitamqp241/RabbitAMQPMetricUtil.java | 19 ++++++++++++++++--- .../client/Consumer_Instrumentation.java | 2 +- .../rabbitamqp250/RabbitAMQPMetricUtil.java | 19 ++++++++++++++++--- .../client/Consumer_Instrumentation.java | 2 +- .../rabbitamqp270/RabbitAMQPMetricUtil.java | 19 ++++++++++++++++--- .../client/Consumer_Instrumentation.java | 2 +- .../rabbitamqp500/RabbitAMQPMetricUtil.java | 19 ++++++++++++++++--- .../client/Consumer_Instrumentation.java | 2 +- .../service/analytics/SpanEventFactory.java | 2 ++ 11 files changed, 87 insertions(+), 20 deletions(-) diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index 0ce1533098..48757250ac 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -53,7 +53,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .instance(host, port) .build()); - addAttributes(routingKey, props); + addProduceAttributes(exchangeName, routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, @@ -68,12 +68,25 @@ public static void processGetMessage(String queueName, String routingKey, String .instance(host, port) .build()); - addConsumeAttributes(queueName, routingKey, properties); + addConsumeAttributes(exchangeName, queueName, routingKey, properties); } - public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { + public static void addConsumeAttributes(String exchangeName, String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", queueName, true); + if (exchangeName != null) { + AgentBridge.privateApi.addTracerParameter("messaging.destination_publish.name", exchangeName, true); + } + } + addAttributes(routingKey, properties); + } + + public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { + if (exchangeName != null && captureSegmentParameters) { + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); } addAttributes(routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java index 2af56138eb..4e9df6c1bf 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -27,7 +27,7 @@ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProp RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); AgentBridge.getAgent().getTransaction().provideHeaders(new InboundWrapper(properties.getHeaders())); AgentBridge.getAgent().getTransaction(false).setTransportType(TransportType.AMQP); - RabbitAMQPMetricUtil.addConsumeAttributes(null, envelope.getRoutingKey(), properties); + RabbitAMQPMetricUtil.addConsumeAttributes(null, null, envelope.getRoutingKey(), properties); Weaver.callOriginal(); } } diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java index de6822762e..658715807a 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -54,7 +54,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .instance(host, port) .build()); - addAttributes(routingKey, props); + addProduceAttributes(exchangeName, routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, @@ -69,12 +69,25 @@ public static void processGetMessage(String queueName, String routingKey, String .instance(host, port) .build()); - addConsumeAttributes(queueName, routingKey, properties); + addConsumeAttributes(exchangeName, queueName, routingKey, properties); } - public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { + public static void addConsumeAttributes(String exchangeName, String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", queueName, true); + if (exchangeName != null) { + AgentBridge.privateApi.addTracerParameter("messaging.destination_publish.name", exchangeName, true); + } + } + addAttributes(routingKey, properties); + } + + public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { + if (exchangeName != null && captureSegmentParameters) { + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); } addAttributes(routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java index f994cea4a4..078aa9d906 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -27,7 +27,7 @@ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProp RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); AgentBridge.getAgent().getTransaction().provideHeaders(new InboundWrapper(properties.getHeaders())); AgentBridge.getAgent().getTransaction(false).setTransportType(TransportType.AMQP); - RabbitAMQPMetricUtil.addConsumeAttributes(null, envelope.getRoutingKey(), properties); + RabbitAMQPMetricUtil.addConsumeAttributes(null, null, envelope.getRoutingKey(), properties); Weaver.callOriginal(); } } diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index c86a869076..551fb4063b 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -54,7 +54,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .instance(host, port) .build()); - addAttributes(routingKey, props); + addProduceAttributes(exchangeName, routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, @@ -69,12 +69,25 @@ public static void processGetMessage(String queueName, String routingKey, String .instance(host, port) .build()); - addConsumeAttributes(queueName, routingKey, properties); + addConsumeAttributes(exchangeName, queueName, routingKey, properties); } - public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { + public static void addConsumeAttributes(String exchangeName, String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", queueName, true); + if (exchangeName != null) { + AgentBridge.privateApi.addTracerParameter("messaging.destination_publish.name", exchangeName, true); + } + } + addAttributes(routingKey, properties); + } + + public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { + if (exchangeName != null && captureSegmentParameters) { + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); } addAttributes(routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java index 7d92591f52..02838e7613 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -27,7 +27,7 @@ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProp RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); AgentBridge.getAgent().getTransaction().provideHeaders(new InboundWrapper(properties.getHeaders())); AgentBridge.getAgent().getTransaction(false).setTransportType(TransportType.AMQP); - RabbitAMQPMetricUtil.addConsumeAttributes(null, envelope.getRoutingKey(), properties); + RabbitAMQPMetricUtil.addConsumeAttributes(null, null, envelope.getRoutingKey(), properties); Weaver.callOriginal(); } } diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java index db3953353a..b688f5e972 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -54,7 +54,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .instance(host, port) .build()); - addAttributes(routingKey, props); + addProduceAttributes(exchangeName, routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, @@ -69,12 +69,25 @@ public static void processGetMessage(String queueName, String routingKey, String .instance(host, port) .build()); - addConsumeAttributes(queueName, routingKey, properties); + addConsumeAttributes(exchangeName, queueName, routingKey, properties); } - public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { + public static void addConsumeAttributes(String exchangeName, String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", queueName, true); + if (exchangeName != null) { + AgentBridge.privateApi.addTracerParameter("messaging.destination_publish.name", exchangeName, true); + } + } + addAttributes(routingKey, properties); + } + + public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { + if (exchangeName != null && captureSegmentParameters) { + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); } addAttributes(routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java index 3f60e36d72..43335d6fe4 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -27,7 +27,7 @@ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProp RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); AgentBridge.getAgent().getTransaction().provideHeaders(new InboundWrapper(properties.getHeaders())); AgentBridge.getAgent().getTransaction(false).setTransportType(TransportType.AMQP); - RabbitAMQPMetricUtil.addConsumeAttributes(null, envelope.getRoutingKey(), properties); + RabbitAMQPMetricUtil.addConsumeAttributes(null, null, envelope.getRoutingKey(), properties); Weaver.callOriginal(); } } diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index da23208c70..034d34aaeb 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -55,7 +55,7 @@ public static void processSendMessage(String exchangeName, String routingKey, .instance(host, port) .build()); - addAttributes(routingKey, props); + addProduceAttributes(exchangeName, routingKey, props); } public static void processGetMessage(String queueName, String routingKey, String exchangeName, @@ -70,12 +70,25 @@ public static void processGetMessage(String queueName, String routingKey, String .instance(host, port) .build()); - addConsumeAttributes(queueName, routingKey, properties); + addConsumeAttributes(exchangeName, queueName, routingKey, properties); } - public static void addConsumeAttributes(String queueName, String routingKey, AMQP.BasicProperties properties) { + public static void addConsumeAttributes(String exchangeName, String queueName, String routingKey, AMQP.BasicProperties properties) { if (queueName != null && captureSegmentParameters) { AgentBridge.privateApi.addTracerParameter("message.queueName", queueName, true); + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", queueName, true); + if (exchangeName != null) { + AgentBridge.privateApi.addTracerParameter("messaging.destination_publish.name", exchangeName, true); + } + } + addAttributes(routingKey, properties); + } + + public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { + if (exchangeName != null && captureSegmentParameters) { + // OTel attributes + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); } addAttributes(routingKey, properties); } diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java index 1f981e1c2e..2e461b2c29 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/rabbitmq/client/Consumer_Instrumentation.java @@ -27,7 +27,7 @@ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProp RabbitAMQPMetricUtil.nameTransaction(envelope.getExchange()); AgentBridge.getAgent().getTransaction().provideHeaders(new InboundWrapper(properties.getHeaders())); AgentBridge.getAgent().getTransaction(false).setTransportType(TransportType.AMQP); - RabbitAMQPMetricUtil.addConsumeAttributes(null, envelope.getRoutingKey(), properties); + RabbitAMQPMetricUtil.addConsumeAttributes(null, null, envelope.getRoutingKey(), properties); Weaver.callOriginal(); } } 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 3bca6e403f..474c0e2655 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 @@ -412,12 +412,14 @@ public SpanEventFactory setExternalParameterAttributes(ExternalParameters parame setCloudResourceId(messageProduceParameters.getCloudResourceId()); setServerAddress(messageProduceParameters.getHost()); setServerPort(messageProduceParameters.getPort()); + setKindFromUserAttributes(); } else if (parameters instanceof MessageConsumeParameters) { MessageConsumeParameters messageConsumeParameters = (MessageConsumeParameters) parameters; setCategory(SpanCategory.generic); setCloudResourceId(messageConsumeParameters.getCloudResourceId()); setServerAddress(messageConsumeParameters.getHost()); setServerPort(messageConsumeParameters.getPort()); + setKindFromUserAttributes(); } else { setCategory(SpanCategory.generic); } From 5215be187c01bd12548b5dd5016fae125a0ff37d Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Thu, 6 Jun 2024 12:10:26 -0400 Subject: [PATCH 41/42] Set empty exchanges in "messaging.destination.name" attr to be "Default" --- .../rabbitamqp172/RabbitAMQPMetricUtil.java | 10 +++++++--- .../rabbitamqp241/RabbitAMQPMetricUtil.java | 10 +++++++--- .../rabbitamqp250/RabbitAMQPMetricUtil.java | 10 +++++++--- .../rabbitamqp270/RabbitAMQPMetricUtil.java | 10 +++++++--- .../rabbitamqp500/RabbitAMQPMetricUtil.java | 10 +++++++--- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java index 48757250ac..800ded0e23 100644 --- a/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-1.7.2/src/main/java/com/nr/agent/instrumentation/rabbitamqp172/RabbitAMQPMetricUtil.java @@ -48,7 +48,7 @@ public static void processSendMessage(String exchangeName, String routingKey, tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .outboundHeaders(new OutboundWrapper(headers)) .instance(host, port) .build()); @@ -63,7 +63,7 @@ public static void processGetMessage(String queueName, String routingKey, String tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .inboundHeaders(new InboundWrapper(properties.getHeaders())) .instance(host, port) .build()); @@ -86,11 +86,15 @@ public static void addConsumeAttributes(String exchangeName, String queueName, S public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { if (exchangeName != null && captureSegmentParameters) { // OTel attributes - AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", wrapExchange(exchangeName), true); } addAttributes(routingKey, properties); } + public static String wrapExchange(String exchangeName) { + return exchangeName.isEmpty() ? DEFAULT : exchangeName; + } + public static void queuePurge(String queue, TracedMethod tracedMethod) { tracedMethod.setMetricName(MessageFormat.format("MessageBroker/{0}/Queue/Purge/Named/{1}", RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); diff --git a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java index 658715807a..f0ed958f0d 100644 --- a/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.4.1/src/main/java/com/nr/agent/instrumentation/rabbitamqp241/RabbitAMQPMetricUtil.java @@ -49,7 +49,7 @@ public static void processSendMessage(String exchangeName, String routingKey, tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .outboundHeaders(new OutboundWrapper(headers)) .instance(host, port) .build()); @@ -64,7 +64,7 @@ public static void processGetMessage(String queueName, String routingKey, String tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .inboundHeaders(new InboundWrapper(properties.getHeaders())) .instance(host, port) .build()); @@ -87,11 +87,15 @@ public static void addConsumeAttributes(String exchangeName, String queueName, S public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { if (exchangeName != null && captureSegmentParameters) { // OTel attributes - AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", wrapExchange(exchangeName), true); } addAttributes(routingKey, properties); } + public static String wrapExchange(String exchangeName) { + return exchangeName.isEmpty() ? DEFAULT : exchangeName; + } + public static void queuePurge(String queue, TracedMethod tracedMethod) { tracedMethod.setMetricName(MessageFormat.format("MessageBroker/{0}/Queue/Purge/Named/{1}", RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); diff --git a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java index 551fb4063b..2375e1f67b 100644 --- a/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.5.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp250/RabbitAMQPMetricUtil.java @@ -49,7 +49,7 @@ public static void processSendMessage(String exchangeName, String routingKey, tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .outboundHeaders(new OutboundWrapper(headers)) .instance(host, port) .build()); @@ -64,7 +64,7 @@ public static void processGetMessage(String queueName, String routingKey, String tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .inboundHeaders(new InboundWrapper(properties.getHeaders())) .instance(host, port) .build()); @@ -87,11 +87,15 @@ public static void addConsumeAttributes(String exchangeName, String queueName, S public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { if (exchangeName != null && captureSegmentParameters) { // OTel attributes - AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", wrapExchange(exchangeName), true); } addAttributes(routingKey, properties); } + public static String wrapExchange(String exchangeName) { + return exchangeName.isEmpty() ? DEFAULT : exchangeName; + } + public static void queuePurge(String queue, TracedMethod tracedMethod) { tracedMethod.setMetricName(MessageFormat.format("MessageBroker/{0}/Queue/Purge/Named/{1}", RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); diff --git a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java index b688f5e972..c561780d4a 100644 --- a/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-2.7.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp270/RabbitAMQPMetricUtil.java @@ -49,7 +49,7 @@ public static void processSendMessage(String exchangeName, String routingKey, tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .outboundHeaders(new OutboundWrapper(headers)) .instance(host, port) .build()); @@ -64,7 +64,7 @@ public static void processGetMessage(String queueName, String routingKey, String tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .inboundHeaders(new InboundWrapper(properties.getHeaders())) .instance(host, port) .build()); @@ -87,11 +87,15 @@ public static void addConsumeAttributes(String exchangeName, String queueName, S public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { if (exchangeName != null && captureSegmentParameters) { // OTel attributes - AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", wrapExchange(exchangeName), true); } addAttributes(routingKey, properties); } + public static String wrapExchange(String exchangeName) { + return exchangeName.isEmpty() ? DEFAULT : exchangeName; + } + public static void queuePurge(String queue, TracedMethod tracedMethod) { tracedMethod.setMetricName(MessageFormat.format("MessageBroker/{0}/Queue/Purge/Named/{1}", RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); diff --git a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java index 034d34aaeb..2fa96a2b8b 100644 --- a/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java +++ b/instrumentation/rabbit-amqp-5.0.0/src/main/java/com/nr/agent/instrumentation/rabbitamqp500/RabbitAMQPMetricUtil.java @@ -50,7 +50,7 @@ public static void processSendMessage(String exchangeName, String routingKey, tracedMethod.reportAsExternal(MessageProduceParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .outboundHeaders(new OutboundWrapper(headers)) .instance(host, port) .build()); @@ -65,7 +65,7 @@ public static void processGetMessage(String queueName, String routingKey, String tracedMethod.reportAsExternal(MessageConsumeParameters .library(RABBITMQ) .destinationType(DestinationType.EXCHANGE) - .destinationName(exchangeName.isEmpty() ? DEFAULT : exchangeName) + .destinationName(wrapExchange(exchangeName)) .inboundHeaders(new InboundWrapper(properties.getHeaders())) .instance(host, port) .build()); @@ -88,11 +88,15 @@ public static void addConsumeAttributes(String exchangeName, String queueName, S public static void addProduceAttributes(String exchangeName, String routingKey, AMQP.BasicProperties properties) { if (exchangeName != null && captureSegmentParameters) { // OTel attributes - AgentBridge.privateApi.addTracerParameter("messaging.destination.name", exchangeName, true); + AgentBridge.privateApi.addTracerParameter("messaging.destination.name", wrapExchange(exchangeName), true); } addAttributes(routingKey, properties); } + public static String wrapExchange(String exchangeName) { + return exchangeName.isEmpty() ? DEFAULT : exchangeName; + } + public static void queuePurge(String queue, TracedMethod tracedMethod) { tracedMethod.setMetricName(MessageFormat.format("MessageBroker/{0}/Queue/Purge/Named/{1}", RABBITMQ, queue.isEmpty() ? DEFAULT : queue)); From cae16b69ba564c47ab8efb3a0a1d821afc8b90a3 Mon Sep 17 00:00:00 2001 From: Oren Ben-Meir Date: Mon, 17 Jun 2024 12:28:48 -0400 Subject: [PATCH 42/42] Set span.kind to be producer or consumer --- .../agent/service/analytics/SpanEventFactory.java | 9 +++++++-- .../agent/service/analytics/SpanEventFactoryTest.java | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) 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 474c0e2655..77a75df7ee 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 @@ -198,6 +198,11 @@ public SpanEventFactory setKindFromUserAttributes() { return this; } + public SpanEventFactory setKind(String kind) { + builder.spanKind(kind); + return this; + } + // http parameter public SpanEventFactory setUri(URI uri) { if (uri == null) { @@ -412,14 +417,14 @@ public SpanEventFactory setExternalParameterAttributes(ExternalParameters parame setCloudResourceId(messageProduceParameters.getCloudResourceId()); setServerAddress(messageProduceParameters.getHost()); setServerPort(messageProduceParameters.getPort()); - setKindFromUserAttributes(); + setKind("producer"); } else if (parameters instanceof MessageConsumeParameters) { MessageConsumeParameters messageConsumeParameters = (MessageConsumeParameters) parameters; setCategory(SpanCategory.generic); setCloudResourceId(messageConsumeParameters.getCloudResourceId()); setServerAddress(messageConsumeParameters.getHost()); setServerPort(messageConsumeParameters.getPort()); - setKindFromUserAttributes(); + setKind("consumer"); } else { setCategory(SpanCategory.generic); } 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 d7188c0bc8..1caef16f9e 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 @@ -281,6 +281,7 @@ public void shouldSetCloudResourceIdOnSpanFromMessageProduceParameters() { Map agentAttrs = target.getAgentAttributes(); assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); + assertEquals("producer", target.getIntrinsics().get("span.kind")); } @Test @@ -295,6 +296,7 @@ public void shouldSetCloudResourceIdOnSpanFromMessageConsumeParameters() { Map agentAttrs = target.getAgentAttributes(); assertEquals(expectedArn, agentAttrs.get("cloud.resource_id")); + assertEquals("consumer", target.getIntrinsics().get("span.kind")); } @Test @@ -313,6 +315,7 @@ public void shouldSetInstanceOnSpanFromMessageProduceParameters() { assertEquals(expectedHost, agentAttrs.get("server.address")); assertEquals(expectedHost, agentAttrs.get("peer.hostname")); assertEquals(expectedPort, agentAttrs.get("server.port")); + assertEquals("producer", target.getIntrinsics().get("span.kind")); } @Test @@ -331,6 +334,7 @@ public void shouldSetInstanceOnSpanFromMessageConsumeParameters() { assertEquals(expectedHost, agentAttrs.get("server.address")); assertEquals(expectedHost, agentAttrs.get("peer.hostname")); assertEquals(expectedPort, agentAttrs.get("server.port")); + assertEquals("consumer", target.getIntrinsics().get("span.kind")); } @Test