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

Add span operation naming convention #6104

Merged
merged 2 commits into from
Nov 10, 2023
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 @@ -64,6 +64,8 @@ public String[] helperClassNames() {
packageName + ".context.propagation.AgentTextMapPropagator",
packageName + ".context.propagation.OtelContextPropagators",
packageName + ".trace.OtelExtractedContext",
packageName + ".trace.OtelConventions",
packageName + ".trace.OtelConventions$1",
packageName + ".trace.OtelSpan",
packageName + ".trace.OtelSpan$1",
packageName + ".trace.OtelSpan$NoopSpan",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public String[] helperClassNames() {
packageName + ".OtelContext",
packageName + ".OtelScope",
ROOT_PACKAGE_NAME + ".trace.OtelExtractedContext",
ROOT_PACKAGE_NAME + ".trace.OtelConventions",
ROOT_PACKAGE_NAME + ".trace.OtelConventions$1",
ROOT_PACKAGE_NAME + ".trace.OtelSpan",
ROOT_PACKAGE_NAME + ".trace.OtelSpan$1",
ROOT_PACKAGE_NAME + ".trace.OtelSpan$NoopSpan",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public String[] helperClassNames() {
packageName + ".OtelContext",
packageName + ".OtelScope",
ROOT_PACKAGE_NAME + ".trace.OtelExtractedContext",
ROOT_PACKAGE_NAME + ".trace.OtelConventions",
ROOT_PACKAGE_NAME + ".trace.OtelConventions$1",
ROOT_PACKAGE_NAME + ".trace.OtelSpan",
ROOT_PACKAGE_NAME + ".trace.OtelSpan$1",
ROOT_PACKAGE_NAME + ".trace.OtelSpan$NoopSpan",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package datadog.trace.instrumentation.opentelemetry14.trace;

import static datadog.trace.api.DDTags.ANALYTICS_SAMPLE_RATE;
import static datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND_CLIENT;
import static datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND_CONSUMER;
import static datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND_PRODUCER;
import static datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND_SERVER;
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
import static io.opentelemetry.api.trace.SpanKind.CONSUMER;
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
import static io.opentelemetry.api.trace.SpanKind.PRODUCER;
import static io.opentelemetry.api.trace.SpanKind.SERVER;
import static java.util.Locale.ROOT;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import io.opentelemetry.api.trace.SpanKind;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class OtelConventions {
/** The Datadog span default operation name. */
public static final String DEFAULT_OPERATION_NAME = "otel_unknown";

private static final Logger LOGGER = LoggerFactory.getLogger(OtelConventions.class);
private static final String OPERATION_NAME_SPECIFIC_ATTRIBUTE = "operation.name";
private static final String SERVICE_NAME_SPECIFIC_ATTRIBUTE = "service.name";
private static final String RESOURCE_NAME_SPECIFIC_ATTRIBUTE = "resource.name";
private static final String SPAN_TYPE_SPECIFIC_ATTRIBUTES = "span.type";
private static final String ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES = "analytics.event";
private static final String SPAN_KIND_INTERNAL = "internal";

private OtelConventions() {}

/**
* Convert OpenTelemetry {@link SpanKind} to Datadog span type.
*
* @param spanKind The OpenTelemetry span kind to convert.
* @return The related Datadog span type.
*/
public static String toSpanType(SpanKind spanKind) {
switch (spanKind) {
case CLIENT:
return SPAN_KIND_CLIENT;
case SERVER:
return SPAN_KIND_SERVER;
case PRODUCER:
return SPAN_KIND_PRODUCER;
case CONSUMER:
return SPAN_KIND_CONSUMER;
case INTERNAL:
return SPAN_KIND_INTERNAL;
default:
return spanKind.toString().toLowerCase(ROOT);
PerfectSlayer marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
* Convert Datadog span type to OpenTelemetry {@link SpanKind}.
*
* @param spanType The span type to convert.
* @return The related OpenTelemetry {@link SpanKind}.
*/
public static SpanKind toSpanKind(String spanType) {
if (spanType == null) {
return INTERNAL;
}
switch (spanType) {
case SPAN_KIND_CLIENT:
return CLIENT;
case SPAN_KIND_SERVER:
return SERVER;
case SPAN_KIND_PRODUCER:
return PRODUCER;
case SPAN_KIND_CONSUMER:
return CONSUMER;
default:
return INTERNAL;
}
}

public static void applyConventions(AgentSpan span) {
String serviceName = getStringAttribute(span, SERVICE_NAME_SPECIFIC_ATTRIBUTE);
if (serviceName != null) {
span.setServiceName(serviceName);
}
String resourceName = getStringAttribute(span, RESOURCE_NAME_SPECIFIC_ATTRIBUTE);
if (resourceName != null) {
span.setResourceName(resourceName);
}
String spanType = getStringAttribute(span, SPAN_TYPE_SPECIFIC_ATTRIBUTES);
if (spanType != null) {
span.setSpanType(spanType);
}
Boolean analyticsEvent = getBooleanAttribute(span, ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES);
if (analyticsEvent != null) {
span.setMetric(ANALYTICS_SAMPLE_RATE, analyticsEvent ? 1 : 0);
}
applyOperationName(span);
}

private static void applyOperationName(AgentSpan span) {
String operationName = getStringAttribute(span, OPERATION_NAME_SPECIFIC_ATTRIBUTE);
if (operationName == null) {
operationName = computeOperationName(span);
}
span.setOperationName(operationName.toLowerCase(ROOT));
}

private static String computeOperationName(AgentSpan span) {
SpanKind spanKind = toSpanKind(span.getSpanType());
/*
* HTTP convention: https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/http/
*/
String httpRequestMethod = getStringAttribute(span, "http.request.method");
if (spanKind == SERVER && httpRequestMethod != null) {
return "http.server.request";
}
if (spanKind == CLIENT && httpRequestMethod != null) {
return "http.client.request";
}
/*
* Database convention: https://opentelemetry.io/docs/specs/semconv/database/database-spans/
*/
String dbSystem = getStringAttribute(span, "db.system");
if (spanKind == CLIENT && dbSystem != null) {
return dbSystem + ".query";
}
/*
* Messaging: https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/
*/
String messagingSystem = getStringAttribute(span, "messaging.system");
String messagingOperation = getStringAttribute(span, "messaging.operation");
if ((spanKind == CONSUMER || spanKind == PRODUCER || spanKind == CLIENT || spanKind == SERVER)
&& messagingSystem != null
&& messagingOperation != null) {
return messagingSystem + "." + messagingOperation;
}
/*
* AWS: https://opentelemetry.io/docs/specs/semconv/cloud-providers/aws-sdk/
*/
String rpcSystem = getStringAttribute(span, "rpc.system");
if (spanKind == CLIENT && "aws-api".equals(rpcSystem)) {
String service = getStringAttribute(span, "rpc.service");
if (service == null) {
return "aws.client.request";
} else {
return "aws." + service + ".request";
}
}
/*
* RPC: https://opentelemetry.io/docs/specs/semconv/rpc/rpc-spans/
*/
if (spanKind == CLIENT && rpcSystem != null) {
return rpcSystem + ".client.request";
}
if (spanKind == SERVER && rpcSystem != null) {
return rpcSystem + ".server.request";
}
/*
* FaaS:
* https://opentelemetry.io/docs/specs/semconv/faas/faas-spans/#incoming-faas-span-attributes
* https://opentelemetry.io/docs/specs/semconv/faas/faas-spans/#outgoing-invocations
*/
String faasInvokedProvider = getStringAttribute(span, "faas.invoked_provider");
String faasInvokedName = getStringAttribute(span, "faas.invoked_name");
if (spanKind == CLIENT && faasInvokedProvider != null && faasInvokedName != null) {
return faasInvokedProvider + "." + faasInvokedName + ".invoke";
}
String faasTrigger = getStringAttribute(span, "faas.trigger");
if (spanKind == SERVER && faasTrigger != null) {
return faasTrigger + ".invoke";
}
/*
* GraphQL: https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/instrumentation/graphql/
*/
String graphqlOperationType = getStringAttribute(span, "graphql.operation.type");
if (graphqlOperationType != null) {
return "graphql.server.request";
}
/*
* Generic server / client: https://opentelemetry.io/docs/specs/semconv/http/http-spans/
*/
String networkProtocolName = getStringAttribute(span, "network.protocol.name");
if (spanKind == SERVER) {
return networkProtocolName == null
? "server.request"
: networkProtocolName + ".server.request";
}
if (spanKind == CLIENT) {
return networkProtocolName == null
? "client.request"
: networkProtocolName + ".client.request";
}
// Fallback if no convention match
if (span.getSpanType() != null) {
return spanKind.name();
} else {
return DEFAULT_OPERATION_NAME;
}
}

@Nullable
private static String getStringAttribute(AgentSpan span, String key) {
Object tag = span.getTag(key);
if (tag == null) {
return null;
} else if (!(tag instanceof String)) {
LOGGER.debug("Span attributes {} is not a string", key);
return key;
}
return (String) tag;
}

@Nullable
private static Boolean getBooleanAttribute(AgentSpan span, String key) {
Object tag = span.getTag(key);
if (tag == null) {
return null;
}
if (tag instanceof Boolean) {
return (Boolean) tag;
} else if (tag instanceof String) {
return Boolean.parseBoolean((String) tag);
} else {
LOGGER.debug("Span attributes {} is not a boolean", key);
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package datadog.trace.instrumentation.opentelemetry14.trace;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.applyConventions;
import static io.opentelemetry.api.trace.StatusCode.ERROR;
import static io.opentelemetry.api.trace.StatusCode.OK;
import static io.opentelemetry.api.trace.StatusCode.UNSET;
Expand Down Expand Up @@ -106,20 +107,22 @@ public Span recordException(Throwable exception, Attributes additionalAttributes
@Override
public Span updateName(String name) {
if (this.recording) {
this.delegate.setOperationName(name);
this.delegate.setResourceName(name);
PerfectSlayer marked this conversation as resolved.
Show resolved Hide resolved
}
return this;
}

@Override
public void end() {
this.recording = false;
applyConventions(this.delegate);
this.delegate.finish();
}

@Override
public void end(long timestamp, TimeUnit unit) {
this.recording = false;
applyConventions(this.delegate);
this.delegate.finish(unit.toMicros(timestamp));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package datadog.trace.instrumentation.opentelemetry14.trace;

import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.toSpanType;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelExtractedContext.extract;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import datadog.trace.bootstrap.instrumentation.api.Tags;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
Expand All @@ -13,7 +13,6 @@
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import javax.annotation.ParametersAreNonnullByDefault;

Expand Down Expand Up @@ -110,22 +109,6 @@ public SpanBuilder setSpanKind(SpanKind spanKind) {
return this;
}

private static String toSpanType(SpanKind spanKind) {
switch (spanKind) {
case CLIENT:
return Tags.SPAN_KIND_CLIENT;
case SERVER:
return Tags.SPAN_KIND_SERVER;
case PRODUCER:
return Tags.SPAN_KIND_PRODUCER;
case CONSUMER:
return Tags.SPAN_KIND_CONSUMER;
default:
case INTERNAL:
return spanKind.toString().toLowerCase(Locale.ROOT);
}
}

@Override
public SpanBuilder setStartTimestamp(long startTimestamp, TimeUnit unit) {
this.delegate.withStartTimestamp(unit.toMicros(startTimestamp));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package datadog.trace.instrumentation.opentelemetry14.trace;

import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.DEFAULT_OPERATION_NAME;

import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.Tracer;
Expand All @@ -19,7 +21,9 @@ public OtelTracer(String instrumentationScopeName) {
@Override
public SpanBuilder spanBuilder(String spanName) {
AgentTracer.SpanBuilder delegate =
this.tracer.buildSpan(INSTRUMENTATION_NAME, spanName).withResourceName(spanName);
this.tracer
.buildSpan(INSTRUMENTATION_NAME, DEFAULT_OPERATION_NAME)
.withResourceName(spanName);
return new OtelSpanBuilder(delegate);
}
}
Loading
Loading