Enhance AWS APM metrics mapping implementation (#906)

This commit is contained in:
Min Xia 2023-06-16 09:22:08 -07:00 committed by GitHub
parent 763ddea48a
commit 047bd89f39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 354 additions and 38 deletions

View File

@ -24,4 +24,16 @@ final class AwsAttributeKeys {
static final AttributeKey<String> AWS_REMOTE_OPERATION =
AttributeKey.stringKey("aws.remote.operation");
static final AttributeKey<String> AWS_REMOTE_TARGET = AttributeKey.stringKey("aws.remote.target");
// use the same AWS Resource attribute name defined by OTel java auto-instr for aws_sdk_v_1_1
// TODO: all AWS specific attributes should be defined in semconv package and reused cross all
// otel packages. Related sim -
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8710
static final AttributeKey<String> AWS_BUCKET_NAME = AttributeKey.stringKey("aws.bucket.name");
static final AttributeKey<String> AWS_QUEUE_NAME = AttributeKey.stringKey("aws.queue.name");
static final AttributeKey<String> AWS_STREAM_NAME = AttributeKey.stringKey("aws.stream.name");
static final AttributeKey<String> AWS_TABLE_NAME = AttributeKey.stringKey("aws.table.name");
}

View File

@ -5,19 +5,31 @@
package io.opentelemetry.contrib.awsxray;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_BUCKET_NAME;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_LOCAL_OPERATION;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_LOCAL_SERVICE;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_QUEUE_NAME;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_REMOTE_OPERATION;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_REMOTE_SERVICE;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_REMOTE_TARGET;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_SPAN_KIND;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_STREAM_NAME;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_TABLE_NAME;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_SYSTEM;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_INVOKED_NAME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_INVOKED_PROVIDER;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_TRIGGER;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.GRAPHQL_OPERATION_TYPE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_METHOD;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_TARGET;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_URL;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_OPERATION;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_SYSTEM;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_NAME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_PORT;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_SOCK_PEER_ADDR;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_SOCK_PEER_PORT;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.PEER_SERVICE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.RPC_METHOD;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.RPC_SERVICE;
@ -30,6 +42,9 @@ import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -71,6 +86,7 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
setService(resource, span, builder);
setEgressOperation(span, builder);
setRemoteServiceAndOperation(span, builder);
setRemoteTarget(span, builder);
setSpanKind(span, builder);
break;
default:
@ -79,6 +95,30 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
return builder.build();
}
private static void setRemoteTarget(SpanData span, AttributesBuilder builder) {
Optional<String> remoteTarget = getRemoteTarget(span);
remoteTarget.ifPresent(s -> builder.put(AWS_REMOTE_TARGET, s));
}
/**
* RemoteTarget attribute {@link AwsAttributeKeys#AWS_REMOTE_TARGET} is used to store the resource
* name of the remote invokes, such as S3 bucket name, mysql table name, etc. TODO: currently only
* support AWS resource name, will be extended to support the general remote targets, such as
* ActiveMQ name, etc.
*/
private static Optional<String> getRemoteTarget(SpanData span) {
if (isKeyPresent(span, AWS_BUCKET_NAME)) {
return Optional.ofNullable(span.getAttributes().get(AWS_BUCKET_NAME));
} else if (isKeyPresent(span, AWS_QUEUE_NAME)) {
return Optional.ofNullable(span.getAttributes().get(AWS_QUEUE_NAME));
} else if (isKeyPresent(span, AWS_STREAM_NAME)) {
return Optional.ofNullable(span.getAttributes().get(AWS_STREAM_NAME));
} else if (isKeyPresent(span, AWS_TABLE_NAME)) {
return Optional.ofNullable(span.getAttributes().get(AWS_TABLE_NAME));
}
return Optional.empty();
}
/** Service is always derived from {@link ResourceAttributes#SERVICE_NAME} */
private static void setService(Resource resource, SpanData span, AttributesBuilder builder) {
String service = resource.getAttribute(SERVICE_NAME);
@ -90,18 +130,39 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
}
/**
* Ingress operation (i.e. operation for Server and Consumer spans) is always derived from span
* name.
* Ingress operation (i.e. operation for Server and Consumer spans) will be generated from
* "http.method + http.target/with the first API path parameter" if the default span name equals
* null, UnknownOperation or http.method value.
*/
private static void setIngressOperation(SpanData span, AttributesBuilder builder) {
String operation = span.getName();
if (operation == null) {
String operation;
if (!isValidOperation(span)) {
operation = generateIngressOperation(span);
} else {
operation = span.getName();
}
if (operation.equals(UNKNOWN_OPERATION)) {
logUnknownAttribute(AWS_LOCAL_OPERATION, span);
operation = UNKNOWN_OPERATION;
}
builder.put(AWS_LOCAL_OPERATION, operation);
}
/**
* When Span name is null, UnknownOperation or HttpMethod value, it will be treated as invalid
* local operation value that needs to be further processed
*/
private static boolean isValidOperation(SpanData span) {
String operation = span.getName();
if (operation == null || operation.equals(UNKNOWN_OPERATION)) {
return false;
}
if (isKeyPresent(span, HTTP_METHOD)) {
String httpMethod = span.getAttributes().get(HTTP_METHOD);
return !operation.equals(httpMethod);
}
return true;
}
/**
* Egress operation (i.e. operation for Client and Producer spans) is always derived from a
* special span attribute, {@link AwsAttributeKeys#AWS_LOCAL_OPERATION}. This attribute is
@ -148,39 +209,138 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
* <li>Attributes are confirmed to have low-cardinality values, based on code analysis.
* </ul>
*
* TODO: This specific logic may change in future. Specifically, we are still deciding which HTTP
* and RPC attributes to use here, but this is a sufficient starting point.
* if the selected attributes are still producing the UnknownRemoteService or
* UnknownRemoteOperation, `net.peer.name`, `net.peer.port`, `net.peer.sock.addr` and
* `net.peer.sock.port` will be used to derive the RemoteService. And `http.method` and `http.url`
* will be used to derive the RemoteOperation.
*/
private static void setRemoteServiceAndOperation(SpanData span, AttributesBuilder builder) {
String remoteService = UNKNOWN_REMOTE_SERVICE;
String remoteOperation = UNKNOWN_REMOTE_OPERATION;
if (isKeyPresent(span, AWS_REMOTE_SERVICE) || isKeyPresent(span, AWS_REMOTE_OPERATION)) {
setRemoteService(span, builder, AWS_REMOTE_SERVICE);
setRemoteOperation(span, builder, AWS_REMOTE_OPERATION);
remoteService = getRemoteService(span, AWS_REMOTE_SERVICE);
remoteOperation = getRemoteOperation(span, AWS_REMOTE_OPERATION);
} else if (isKeyPresent(span, RPC_SERVICE) || isKeyPresent(span, RPC_METHOD)) {
setRemoteService(span, builder, RPC_SERVICE);
setRemoteOperation(span, builder, RPC_METHOD);
remoteService = getRemoteService(span, RPC_SERVICE);
remoteOperation = getRemoteOperation(span, RPC_METHOD);
} else if (isKeyPresent(span, DB_SYSTEM) || isKeyPresent(span, DB_OPERATION)) {
setRemoteService(span, builder, DB_SYSTEM);
setRemoteOperation(span, builder, DB_OPERATION);
} else if (isKeyPresent(span, FAAS_INVOKED_PROVIDER) || isKeyPresent(span, FAAS_INVOKED_NAME)) {
setRemoteService(span, builder, FAAS_INVOKED_PROVIDER);
setRemoteOperation(span, builder, FAAS_INVOKED_NAME);
remoteService = getRemoteService(span, DB_SYSTEM);
remoteOperation = getRemoteOperation(span, DB_OPERATION);
} else if (isKeyPresent(span, FAAS_INVOKED_NAME) || isKeyPresent(span, FAAS_TRIGGER)) {
remoteService = getRemoteService(span, FAAS_INVOKED_NAME);
remoteOperation = getRemoteOperation(span, FAAS_TRIGGER);
} else if (isKeyPresent(span, MESSAGING_SYSTEM) || isKeyPresent(span, MESSAGING_OPERATION)) {
setRemoteService(span, builder, MESSAGING_SYSTEM);
setRemoteOperation(span, builder, MESSAGING_OPERATION);
remoteService = getRemoteService(span, MESSAGING_SYSTEM);
remoteOperation = getRemoteOperation(span, MESSAGING_OPERATION);
} else if (isKeyPresent(span, GRAPHQL_OPERATION_TYPE)) {
builder.put(AWS_REMOTE_SERVICE, GRAPHQL);
setRemoteOperation(span, builder, GRAPHQL_OPERATION_TYPE);
} else {
logUnknownAttribute(AWS_REMOTE_SERVICE, span);
builder.put(AWS_REMOTE_SERVICE, UNKNOWN_REMOTE_SERVICE);
logUnknownAttribute(AWS_REMOTE_OPERATION, span);
builder.put(AWS_REMOTE_OPERATION, UNKNOWN_REMOTE_OPERATION);
remoteService = GRAPHQL;
remoteOperation = getRemoteOperation(span, GRAPHQL_OPERATION_TYPE);
}
// Peer service takes priority as RemoteService over everything but AWS Remote.
if (isKeyPresent(span, PEER_SERVICE) && !isKeyPresent(span, AWS_REMOTE_SERVICE)) {
setRemoteService(span, builder, PEER_SERVICE);
remoteService = getRemoteService(span, PEER_SERVICE);
}
// try to derive RemoteService and RemoteOperation from the other related attributes
if (remoteService.equals(UNKNOWN_REMOTE_SERVICE)) {
remoteService = generateRemoteService(span);
}
if (remoteOperation.equals(UNKNOWN_REMOTE_OPERATION)) {
remoteOperation = generateRemoteOperation(span);
}
builder.put(AWS_REMOTE_SERVICE, remoteService);
builder.put(AWS_REMOTE_OPERATION, remoteOperation);
}
/**
* When span name is not meaningful(null, unknown or http_method value) as operation name for http
* use cases. Will try to extract the operation name from http target string
*/
private static String generateIngressOperation(SpanData span) {
String operation = UNKNOWN_OPERATION;
if (isKeyPresent(span, HTTP_TARGET)) {
String httpTarget = span.getAttributes().get(HTTP_TARGET);
// get the first part from API path string as operation value
// the more levels/parts we get from API path the higher chance for getting high cardinality
// data
if (httpTarget != null) {
operation = extractAPIPathValue(httpTarget);
if (isKeyPresent(span, HTTP_METHOD)) {
String httpMethod = span.getAttributes().get(HTTP_METHOD);
if (httpMethod != null) {
operation = httpMethod + " " + operation;
}
}
}
}
return operation;
}
/**
* When the remote call operation is undetermined for http use cases, will try to extract the
* remote operation name from http url string
*/
private static String generateRemoteOperation(SpanData span) {
String remoteOperation = UNKNOWN_REMOTE_OPERATION;
if (isKeyPresent(span, HTTP_URL)) {
String httpUrl = span.getAttributes().get(HTTP_URL);
try {
URL url;
if (httpUrl != null) {
url = new URL(httpUrl);
remoteOperation = extractAPIPathValue(url.getPath());
}
} catch (MalformedURLException e) {
logger.log(Level.FINEST, "invalid http.url attribute: ", httpUrl);
}
}
if (isKeyPresent(span, HTTP_METHOD)) {
String httpMethod = span.getAttributes().get(HTTP_METHOD);
remoteOperation = httpMethod + " " + remoteOperation;
}
if (remoteOperation.equals(UNKNOWN_REMOTE_OPERATION)) {
logUnknownAttribute(AWS_REMOTE_OPERATION, span);
}
return remoteOperation;
}
/**
* Extract the first part from API http target if it exists
*
* @param httpTarget http request target string value. Eg, /payment/1234
* @return the first part from the http target. Eg, /payment
*/
private static String extractAPIPathValue(String httpTarget) {
if (httpTarget == null || httpTarget.isEmpty()) {
return "/";
}
String[] paths = httpTarget.split("/");
if (paths.length > 1) {
return "/" + paths[1];
}
return "/";
}
private static String generateRemoteService(SpanData span) {
String remoteService = UNKNOWN_REMOTE_SERVICE;
if (isKeyPresent(span, NET_PEER_NAME)) {
remoteService = getRemoteService(span, NET_PEER_NAME);
if (isKeyPresent(span, NET_PEER_PORT)) {
Long port = span.getAttributes().get(NET_PEER_PORT);
remoteService += ":" + port;
}
} else if (isKeyPresent(span, NET_SOCK_PEER_ADDR)) {
remoteService = getRemoteService(span, NET_SOCK_PEER_ADDR);
if (isKeyPresent(span, NET_SOCK_PEER_PORT)) {
Long port = span.getAttributes().get(NET_SOCK_PEER_PORT);
remoteService += ":" + port;
}
} else {
logUnknownAttribute(AWS_REMOTE_SERVICE, span);
}
return remoteService;
}
/** Span kind is needed for differentiating metrics in the EMF exporter */
@ -189,28 +349,24 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
builder.put(AWS_SPAN_KIND, spanKind);
}
private static boolean isKeyPresent(SpanData span, AttributeKey<String> key) {
private static boolean isKeyPresent(SpanData span, AttributeKey<?> key) {
return span.getAttributes().get(key) != null;
}
private static void setRemoteService(
SpanData span, AttributesBuilder builder, AttributeKey<String> remoteServiceKey) {
private static String getRemoteService(SpanData span, AttributeKey<String> remoteServiceKey) {
String remoteService = span.getAttributes().get(remoteServiceKey);
if (remoteService == null) {
logUnknownAttribute(AWS_REMOTE_SERVICE, span);
remoteService = UNKNOWN_REMOTE_SERVICE;
}
builder.put(AWS_REMOTE_SERVICE, remoteService);
return remoteService;
}
private static void setRemoteOperation(
SpanData span, AttributesBuilder builder, AttributeKey<String> remoteOperationKey) {
private static String getRemoteOperation(SpanData span, AttributeKey<String> remoteOperationKey) {
String remoteOperation = span.getAttributes().get(remoteOperationKey);
if (remoteOperation == null) {
logUnknownAttribute(AWS_REMOTE_OPERATION, span);
remoteOperation = UNKNOWN_REMOTE_OPERATION;
}
builder.put(AWS_REMOTE_OPERATION, remoteOperation);
return remoteOperation;
}
private static void logUnknownAttribute(AttributeKey<String> attributeKey, SpanData span) {

View File

@ -5,19 +5,32 @@
package io.opentelemetry.contrib.awsxray;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_BUCKET_NAME;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_LOCAL_OPERATION;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_LOCAL_SERVICE;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_QUEUE_NAME;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_REMOTE_OPERATION;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_REMOTE_SERVICE;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_REMOTE_TARGET;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_SPAN_KIND;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_STREAM_NAME;
import static io.opentelemetry.contrib.awsxray.AwsAttributeKeys.AWS_TABLE_NAME;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_SYSTEM;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_INVOKED_NAME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_INVOKED_PROVIDER;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_TRIGGER;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.GRAPHQL_OPERATION_TYPE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_METHOD;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_TARGET;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_URL;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_OPERATION;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_SYSTEM;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_NAME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_PORT;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_SOCK_PEER_ADDR;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_SOCK_PEER_PORT;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.PEER_SERVICE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.RPC_METHOD;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.RPC_SERVICE;
@ -140,6 +153,54 @@ class AwsMetricAttributeGeneratorTest {
validateAttributesProducedForSpanOfKind(expectedAttributes, SpanKind.SERVER);
}
@Test
public void testServerSpanWithNullSpanName() {
updateResourceWithServiceName();
when(spanDataMock.getName()).thenReturn(null);
Attributes expectedAttributes =
Attributes.of(
AWS_SPAN_KIND, SpanKind.SERVER.name(),
AWS_LOCAL_SERVICE, SERVICE_NAME_VALUE,
AWS_LOCAL_OPERATION, UNKNOWN_OPERATION);
validateAttributesProducedForSpanOfKind(expectedAttributes, SpanKind.SERVER);
}
@Test
public void testServerSpanWithSpanNameAsHttpMethod() {
updateResourceWithServiceName();
when(spanDataMock.getName()).thenReturn("GET");
mockAttribute(HTTP_METHOD, "GET");
Attributes expectedAttributes =
Attributes.of(
AWS_SPAN_KIND, SpanKind.SERVER.name(),
AWS_LOCAL_SERVICE, SERVICE_NAME_VALUE,
AWS_LOCAL_OPERATION, UNKNOWN_OPERATION);
validateAttributesProducedForSpanOfKind(expectedAttributes, SpanKind.SERVER);
mockAttribute(HTTP_METHOD, null);
}
@Test
public void testServerSpanWithSpanNameWithHttpTarget() {
updateResourceWithServiceName();
when(spanDataMock.getName()).thenReturn("POST");
mockAttribute(HTTP_METHOD, "POST");
mockAttribute(HTTP_TARGET, "/payment/123");
Attributes expectedAttributes =
Attributes.of(
AWS_SPAN_KIND,
SpanKind.SERVER.name(),
AWS_LOCAL_SERVICE,
SERVICE_NAME_VALUE,
AWS_LOCAL_OPERATION,
"POST /payment");
validateAttributesProducedForSpanOfKind(expectedAttributes, SpanKind.SERVER);
mockAttribute(HTTP_METHOD, null);
mockAttribute(HTTP_TARGET, null);
}
@Test
public void testProducerSpanWithAttributes() {
updateResourceWithServiceName();
@ -210,7 +271,7 @@ class AwsMetricAttributeGeneratorTest {
// Validate behaviour of various combinations of FAAS attributes, then remove them.
validateAndRemoveRemoteAttributes(
FAAS_INVOKED_PROVIDER, "FAAS invoked provider", FAAS_INVOKED_NAME, "FAAS invoked name");
FAAS_INVOKED_NAME, "FAAS invoked name", FAAS_TRIGGER, "FAAS trigger name");
// Validate behaviour of various combinations of Messaging attributes, then remove them.
validateAndRemoveRemoteAttributes(
@ -221,6 +282,46 @@ class AwsMetricAttributeGeneratorTest {
validateExpectedRemoteAttributes("graphql", "GraphQL operation type");
mockAttribute(GRAPHQL_OPERATION_TYPE, null);
// Validate behaviour of extracting Remote Service from net.peer.name
mockAttribute(NET_PEER_NAME, "www.example.com");
validateExpectedRemoteAttributes("www.example.com", UNKNOWN_REMOTE_OPERATION);
mockAttribute(NET_PEER_NAME, null);
// Validate behaviour of extracting Remote Service from net.peer.name and net.peer.port
mockAttribute(NET_PEER_NAME, "192.168.0.0");
mockAttribute(NET_PEER_PORT, 8081L);
validateExpectedRemoteAttributes("192.168.0.0:8081", UNKNOWN_REMOTE_OPERATION);
mockAttribute(NET_PEER_NAME, null);
mockAttribute(NET_PEER_PORT, null);
// Validate behaviour of extracting Remote Service from net.peer.socket.addr
mockAttribute(NET_SOCK_PEER_ADDR, "www.example.com");
validateExpectedRemoteAttributes("www.example.com", UNKNOWN_REMOTE_OPERATION);
mockAttribute(NET_SOCK_PEER_ADDR, null);
// Validate behaviour of extracting Remote Service from net.peer.socket.addr and
// net.sock.peer.port
mockAttribute(NET_SOCK_PEER_ADDR, "192.168.0.0");
mockAttribute(NET_SOCK_PEER_PORT, 8081L);
validateExpectedRemoteAttributes("192.168.0.0:8081", UNKNOWN_REMOTE_OPERATION);
mockAttribute(NET_SOCK_PEER_ADDR, null);
mockAttribute(NET_SOCK_PEER_PORT, null);
// Validate behavior of Remote Operation from HttpTarget - with 1st api part, then remove it
mockAttribute(HTTP_URL, "http://www.example.com/payment/123");
validateExpectedRemoteAttributes(UNKNOWN_REMOTE_SERVICE, "/payment");
mockAttribute(HTTP_URL, null);
// Validate behavior of Remote Operation from HttpTarget - without 1st api part, then remove it
mockAttribute(HTTP_URL, "http://www.example.com");
validateExpectedRemoteAttributes(UNKNOWN_REMOTE_SERVICE, "/");
mockAttribute(HTTP_URL, null);
// Validate behavior of Remote Operation from HttpTarget - invalid url, then remove it
mockAttribute(HTTP_URL, "abc");
validateExpectedRemoteAttributes(UNKNOWN_REMOTE_SERVICE, UNKNOWN_REMOTE_OPERATION);
mockAttribute(HTTP_URL, null);
// Validate behaviour of Peer service attribute, then remove it.
mockAttribute(PEER_SERVICE, "Peer service");
validateExpectedRemoteAttributes("Peer service", UNKNOWN_REMOTE_OPERATION);
@ -237,6 +338,8 @@ class AwsMetricAttributeGeneratorTest {
validatePeerServiceDoesOverride(FAAS_INVOKED_PROVIDER);
validatePeerServiceDoesOverride(MESSAGING_SYSTEM);
validatePeerServiceDoesOverride(GRAPHQL_OPERATION_TYPE);
validatePeerServiceDoesOverride(NET_PEER_NAME);
validatePeerServiceDoesOverride(NET_SOCK_PEER_ADDR);
// Actually testing that peer service overrides "UnknownRemoteService".
validatePeerServiceDoesOverride(AttributeKey.stringKey("unknown.service.key"));
}
@ -252,7 +355,30 @@ class AwsMetricAttributeGeneratorTest {
assertThat(actualAttributes.get(AWS_REMOTE_SERVICE)).isEqualTo("TestString");
}
private void mockAttribute(AttributeKey<String> key, String value) {
@Test
public void testClientSpanWithRemoteTargetAttributes() {
// Validate behaviour of aws bucket name attribute, then remove it.
mockAttribute(AWS_BUCKET_NAME, "aws_s3_bucket_name");
validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "aws_s3_bucket_name");
mockAttribute(AWS_BUCKET_NAME, null);
// Validate behaviour of AWS_QUEUE_NAME attribute, then remove it.
mockAttribute(AWS_QUEUE_NAME, "aws_queue_name");
validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "aws_queue_name");
mockAttribute(AWS_QUEUE_NAME, null);
// Validate behaviour of AWS_STREAM_NAME attribute, then remove it.
mockAttribute(AWS_STREAM_NAME, "aws_stream_name");
validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "aws_stream_name");
mockAttribute(AWS_STREAM_NAME, null);
// Validate behaviour of AWS_TABLE_NAME attribute, then remove it.
mockAttribute(AWS_TABLE_NAME, "aws_table_name");
validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "aws_table_name");
mockAttribute(AWS_TABLE_NAME, null);
}
private <T> void mockAttribute(AttributeKey<T> key, T value) {
when(attributesMock.get(key)).thenReturn(value);
}
@ -316,4 +442,26 @@ class AwsMetricAttributeGeneratorTest {
mockAttribute(remoteServiceKey, null);
mockAttribute(PEER_SERVICE, null);
}
private void validateRemoteTargetAttributes(
AttributeKey<String> remoteTargetKey, String remoteTarget) {
// Client and Producer spans should generate the expected RemoteTarget attribute
when(spanDataMock.getKind()).thenReturn(SpanKind.CLIENT);
Attributes actualAttributes =
GENERATOR.generateMetricAttributesFromSpan(spanDataMock, resource);
assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(remoteTarget);
when(spanDataMock.getKind()).thenReturn(SpanKind.PRODUCER);
actualAttributes = GENERATOR.generateMetricAttributesFromSpan(spanDataMock, resource);
assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(remoteTarget);
// Server and Consumer span should not generate RemoteTarget attribute
when(spanDataMock.getKind()).thenReturn(SpanKind.SERVER);
actualAttributes = GENERATOR.generateMetricAttributesFromSpan(spanDataMock, resource);
assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(null);
when(spanDataMock.getKind()).thenReturn(SpanKind.CONSUMER);
actualAttributes = GENERATOR.generateMetricAttributesFromSpan(spanDataMock, resource);
assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(null);
}
}