Enhance AWS APM metrics mapping implementation (#906)
This commit is contained in:
parent
763ddea48a
commit
047bd89f39
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue