Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DynamoDB: populate cloud.resource_id #2113

Merged
merged 3 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -69,145 +70,145 @@ public AmazonDynamoDBClient_Instrumentation(ClientConfiguration clientConfigurat
final CreateTableResult executeCreateTable(CreateTableRequest createTableRequest) {
linkAndExpire(createTableRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "createTable",
createTableRequest.getTableName(), endpoint);
createTableRequest.getTableName(), endpoint, this);
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, this);
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, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final DeleteItemResult executeDeleteItem(DeleteItemRequest deleteItemRequest) {
linkAndExpire(deleteItemRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteItem",
deleteItemRequest.getTableName(), endpoint);
deleteItemRequest.getTableName(), endpoint, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final DeleteTableResult executeDeleteTable(DeleteTableRequest deleteTableRequest) {
linkAndExpire(deleteTableRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "deleteTable",
deleteTableRequest.getTableName(), endpoint);
deleteTableRequest.getTableName(), endpoint, this);
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, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final DescribeTableResult executeDescribeTable(DescribeTableRequest describeTableRequest) {
linkAndExpire(describeTableRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTable",
describeTableRequest.getTableName(), endpoint);
describeTableRequest.getTableName(), endpoint, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final DescribeTimeToLiveResult executeDescribeTimeToLive(DescribeTimeToLiveRequest describeTimeToLiveRequest) {
linkAndExpire(describeTimeToLiveRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "describeTimeToLive",
describeTimeToLiveRequest.getTableName(), endpoint);
describeTimeToLiveRequest.getTableName(), endpoint, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final GetItemResult executeGetItem(GetItemRequest getItemRequest) {
linkAndExpire(getItemRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "getItem", getItemRequest.getTableName(),
endpoint);
endpoint, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final ListTablesResult executeListTables(ListTablesRequest listTablesRequest) {
linkAndExpire(listTablesRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "listTables",
listTablesRequest.getExclusiveStartTableName(), endpoint);
listTablesRequest.getExclusiveStartTableName(), endpoint, this);
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, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final PutItemResult executePutItem(PutItemRequest putItemRequest) {
linkAndExpire(putItemRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "putItem", putItemRequest.getTableName(),
endpoint);
endpoint, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final QueryResult executeQuery(QueryRequest queryRequest) {
linkAndExpire(queryRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "query", queryRequest.getTableName(),
endpoint);
endpoint, this);
return Weaver.callOriginal();

}

@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, this);
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, this);
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, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final UpdateItemResult executeUpdateItem(UpdateItemRequest updateItemRequest) {
linkAndExpire(updateItemRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateItem",
updateItemRequest.getTableName(), endpoint);
updateItemRequest.getTableName(), endpoint, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final UpdateTableResult executeUpdateTable(UpdateTableRequest updateTableRequest) {
linkAndExpire(updateTableRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTable",
updateTableRequest.getTableName(), endpoint);
updateTableRequest.getTableName(), endpoint, this);
return Weaver.callOriginal();
}

@Trace(async = true, leaf = true)
final UpdateTimeToLiveResult executeUpdateTimeToLive(UpdateTimeToLiveRequest updateTimeToLiveRequest) {
linkAndExpire(updateTimeToLiveRequest);
DynamoDBMetricUtil.metrics(NewRelic.getAgent().getTracedMethod(), "updateTimeToLive",
updateTimeToLiveRequest.getTableName(), endpoint);
updateTimeToLiveRequest.getTableName(), endpoint, this);
return Weaver.callOriginal();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@

package com.nr.instrumentation.dynamodb_1_11_106;

import com.amazonaws.util.AwsHostNameUtils;
import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.agent.bridge.datastore.DatastoreVendor;
import com.newrelic.api.agent.CloudAccountInfo;
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;

/**
* This uses {@link DatastoreParameters} to create external metrics for all DynamoDB calls in
Expand All @@ -21,24 +26,58 @@ 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";

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, Object sdkClient) {
String host = INSTANCE_HOST;
String arn = null;
Integer port = null;
if (endpoint != null) {
host = endpoint.getHost();
port = getPort(endpoint);
arn = getArn(collection, sdkClient, host);
}
DatastoreParameters params = DatastoreParameters
.product(PRODUCT)
.collection(collection)
.operation(operation)
.instance(host, port)
.noDatabaseName()
.cloudResourceId(arn)
.build();

tracedMethod.reportAsExternal(params);
}

private static int getPort(URI endpoint) {
// visible for testing
static String getArn(String tableName, Object sdkClient, String host) {
if (host == null) {
NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Host is null.");
return null;
}

String accountId = AgentBridge.cloud.getAccountInfo(sdkClient, CloudAccountInfo.AWS_ACCOUNT_ID);
if (accountId == null) {
NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. No account information provided.");
return null;
}

if (tableName == null) {
NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Unable to determine table.");
return null;
}

String region = AwsHostNameUtils.parseRegion(host, "dynamodb");
if (region == null) {
NewRelic.getAgent().getLogger().log(Level.FINEST, "Unable to assemble ARN. Unable to determine region.");
return null;
}

// arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}
return "arn:aws:dynamodb:" + region + ":" + accountId + ":table/" + tableName;
}

private static Integer getPort(URI endpoint) {
if (endpoint.getPort() > 0) {
return endpoint.getPort();
}
Expand All @@ -49,7 +88,7 @@ private static int getPort(URI endpoint) {
} else if ("https".equalsIgnoreCase(scheme)) {
return 443;
}
return -1;
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
*
* * Copyright 2024 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.nr.instrumentation.dynamodb_1_11_106;

import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.agent.bridge.CloudApi;
import com.newrelic.api.agent.CloudAccountInfo;
import com.newrelic.api.agent.DatastoreParameters;
import com.newrelic.api.agent.TracedMethod;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;

import java.net.URI;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class DynamoDBMetricUtilTest {

private CloudApi previousCloudApi;

@Before
public void setup() {
previousCloudApi = AgentBridge.cloud;
AgentBridge.cloud = mock(CloudApi.class);
}

@After
public void tearDown() {
AgentBridge.cloud = previousCloudApi;
}

@Test
public void testGetArn() {
Object sdkClient = new Object();
when(AgentBridge.cloud.getAccountInfo(eq(sdkClient), eq(CloudAccountInfo.AWS_ACCOUNT_ID)))
.thenReturn("123456789");
String table = "test";
String host = "dynamodb.us-east-2.amazonaws.com";

String arn = DynamoDBMetricUtil.getArn(table, sdkClient, host);
assertEquals("arn:aws:dynamodb:us-east-2:123456789:table/test", arn);
}

@Test
public void testGetArn_withoutAccountId() {
Object sdkClient = new Object();
String table = "test";
String host = "dynamodb.us-east-2.amazonaws.com";

String arn = DynamoDBMetricUtil.getArn(table, sdkClient, host);
assertNull(arn);
}

@Test
public void testGetArn_withoutTable() {
Object sdkClient = new Object();
when(AgentBridge.cloud.getAccountInfo(eq(sdkClient), eq(CloudAccountInfo.AWS_ACCOUNT_ID)))
.thenReturn("123456789");
String host = "dynamodb.us-east-2.amazonaws.com";

String arn = DynamoDBMetricUtil.getArn(null, sdkClient, host);
assertNull(arn);
}

@Test
public void testGetArn_withoutHost() {
Object sdkClient = new Object();
when(AgentBridge.cloud.getAccountInfo(eq(sdkClient), eq(CloudAccountInfo.AWS_ACCOUNT_ID)))
.thenReturn("123456789");
String table = "test";

String arn = DynamoDBMetricUtil.getArn(table, sdkClient, null);
assertNull(arn);
}

@Test
public void testMetrics() {
Object sdkClient = new Object();
when(AgentBridge.cloud.getAccountInfo(eq(sdkClient), eq(CloudAccountInfo.AWS_ACCOUNT_ID)))
.thenReturn("123456789");
String table = "test";
TracedMethod tracedMethod = mock(TracedMethod.class);
String operation = "getItem";
URI endpoint = URI.create("https://dynamodb.us-east-2.amazonaws.com");

DynamoDBMetricUtil.metrics(tracedMethod, operation, table, endpoint, sdkClient);

ArgumentCaptor<DatastoreParameters> externalParamsCaptor = ArgumentCaptor.forClass(DatastoreParameters.class);
verify(tracedMethod).reportAsExternal(externalParamsCaptor.capture());
DatastoreParameters params = externalParamsCaptor.getValue();
assertEquals("DynamoDB", params.getProduct());
assertEquals("test", params.getCollection());
assertEquals("getItem", params.getOperation());
assertEquals("dynamodb.us-east-2.amazonaws.com", params.getHost());
assertEquals(Integer.valueOf(443), params.getPort());
assertNull(params.getPathOrId());
assertEquals("arn:aws:dynamodb:us-east-2:123456789:table/test", params.getCloudResourceId());
assertNull(params.getDatabaseName());
}

}
3 changes: 2 additions & 1 deletion instrumentation/aws-java-sdk-dynamodb-2.15.34/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading
Loading