-
Notifications
You must be signed in to change notification settings - Fork 290
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add span operation naming convention
- Loading branch information
1 parent
85efb35
commit 3e46c17
Showing
10 changed files
with
418 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
319 changes: 319 additions & 0 deletions
319
....4/src/main/java/datadog/trace/instrumentation/opentelemetry14/trace/OtelConventions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,319 @@ | ||
package datadog.trace.instrumentation.opentelemetry14.trace; | ||
|
||
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 datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.AWS_CLIENT; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.DATABASE; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.FAAS_CLIENT; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.FAAS_SERVER; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.GENERIC_CLIENT; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.GENERIC_SERVER; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.GRAPHQL_SERVER; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.HTTP_CLIENT; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.HTTP_SERVER; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.MESSAGE_CONSUMER; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.MESSAGE_PRODUCER; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.RPC_CLIENT; | ||
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.Convention.RPC_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; | ||
|
||
public final class OtelConventions { | ||
/** The Datadog span default operation name. */ | ||
public static final String DEFAULT_OPERATION_NAME = "otel_unknown"; | ||
|
||
/** Span naming conventions, by priority order. */ | ||
private static final Convention[] SPAN_NAMING_CONVENTIONS = | ||
new Convention[] { | ||
HTTP_SERVER, | ||
HTTP_CLIENT, | ||
DATABASE, | ||
MESSAGE_CONSUMER, | ||
MESSAGE_PRODUCER, | ||
AWS_CLIENT, | ||
RPC_CLIENT, | ||
RPC_SERVER, | ||
FAAS_SERVER, | ||
FAAS_CLIENT, | ||
GRAPHQL_SERVER, | ||
GENERIC_SERVER, | ||
GENERIC_CLIENT, | ||
}; | ||
|
||
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; | ||
default: | ||
case INTERNAL: | ||
return spanKind.toString().toLowerCase(ROOT); | ||
} | ||
} | ||
|
||
/** | ||
* 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) { | ||
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 applyOperationName(AgentSpan span) { | ||
String operationName = computeOperationName(span).toLowerCase(ROOT); | ||
span.setOperationName(operationName); | ||
} | ||
|
||
public static String computeOperationName(AgentSpan span) { | ||
for (Convention convention : SPAN_NAMING_CONVENTIONS) { | ||
if (convention.appliesFor(span)) { | ||
return convention.defaultOperationName(span); | ||
} | ||
} | ||
// Fallback if no convention match | ||
if (span.getSpanType() != null) { | ||
return toSpanKind(span.getSpanType()).name(); | ||
} else { | ||
return DEFAULT_OPERATION_NAME; | ||
} | ||
} | ||
|
||
public enum Convention { | ||
// HTTP conventions: https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/http/ | ||
HTTP_SERVER() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, SERVER) && hasAttribute(span, "http.request.method"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return "http.server.request"; | ||
} | ||
}, | ||
HTTP_CLIENT() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, CLIENT) && hasAttribute(span, "http.request.method"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return "http.client.request"; | ||
} | ||
}, | ||
|
||
// https://opentelemetry.io/docs/specs/semconv/database/database-spans/ | ||
DATABASE() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, CLIENT) && hasAttribute(span, "db.system"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return getAttribute(span, "db.system") + ".query"; | ||
} | ||
}, | ||
|
||
// https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/ | ||
MESSAGE_PRODUCER() { // TODO Merge both producer and consumer? | ||
|
||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, PRODUCER) | ||
&& hasAttribute(span, "messaging.system") | ||
&& hasAttribute(span, "messaging.operation"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return getAttribute(span, "messaging.system").toLowerCase(ROOT) | ||
+ "." | ||
+ getAttribute(span, "messaging.operation"); | ||
} | ||
}, | ||
MESSAGE_CONSUMER() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return (isKind(span, CLIENT) || isKind(span, CONSUMER)) | ||
&& hasAttribute(span, "messaging.system") | ||
&& hasAttribute(span, "messaging.operation"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return getAttribute(span, "messaging.system").toLowerCase(ROOT) | ||
+ "." | ||
+ getAttribute(span, "messaging.operation"); | ||
} | ||
}, | ||
|
||
// https://opentelemetry.io/docs/specs/semconv/cloud-providers/aws-sdk/ | ||
AWS_CLIENT() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, CLIENT) | ||
&& hasAttribute(span, "rpc.system") | ||
&& "aws-api".equals(getAttribute(span, "rpc.system")); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
String service = getOptionalAttribute(span, "rpc.service"); | ||
if (service == null) { | ||
return "aws.request"; | ||
} else { | ||
return "aws." + service.toLowerCase(ROOT) + ".request"; | ||
} | ||
} | ||
}, | ||
// https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/rpc/ | ||
RPC_SERVER() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, SERVER) && hasAttribute(span, "rpc.system"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return getAttribute(span, "rpc.system") + ".server.request"; | ||
} | ||
}, | ||
RPC_CLIENT() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, CLIENT) && hasAttribute(span, "rpc.system"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return getAttribute(span, "rpc.system") + ".client.request"; | ||
} | ||
}, | ||
// https://opentelemetry.io/docs/specs/semconv/faas/faas-spans/#incoming-faas-span-attributes | ||
FAAS_SERVER() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, SERVER) && hasAttribute(span, "faas.trigger"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return getAttribute(span, "faas.trigger") + ".invoke"; | ||
} | ||
}, | ||
// https://opentelemetry.io/docs/specs/semconv/faas/faas-spans/#outgoing-invocations | ||
FAAS_CLIENT() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, CLIENT) | ||
&& hasAttribute(span, "faas.invoked_provider") | ||
&& hasAttribute(span, "faas.invoked_name"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return getAttribute(span, "faas.invoked_provider") | ||
+ "." | ||
+ getAttribute(span, "faas.invoked_name") | ||
+ ".invoke"; | ||
} | ||
}, | ||
// https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/instrumentation/graphql/ | ||
GRAPHQL_SERVER() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return hasAttribute(span, "graphql.operation.type"); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
return "graphql.server.request"; | ||
} | ||
}, | ||
GENERIC_SERVER() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, SERVER); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
String protocol = getOptionalAttribute(span, "network.protocol.name"); | ||
return protocol == null ? "server.request" : protocol + "server.request"; | ||
} | ||
}, | ||
GENERIC_CLIENT() { | ||
@Override | ||
public boolean appliesFor(AgentSpan span) { | ||
return isKind(span, CLIENT); | ||
} | ||
|
||
@Override | ||
public String defaultOperationName(AgentSpan span) { | ||
String protocol = getOptionalAttribute(span, "network.protocol.name"); | ||
return protocol == null ? "client.request" : protocol + "client.request"; | ||
} | ||
}; | ||
|
||
public abstract boolean appliesFor(AgentSpan span); | ||
|
||
public abstract String defaultOperationName(AgentSpan span); | ||
|
||
public static boolean hasAttribute(AgentSpan span, String key) { | ||
Object tag = span.getTag(key); | ||
return tag instanceof String; | ||
} | ||
|
||
public static String getAttribute(AgentSpan span, String key) { | ||
Object tag = span.getTag(key); | ||
return (String) tag; // TODO Handle type error, missing value, add logging, etc... | ||
} | ||
|
||
public static @Nullable String getOptionalAttribute(AgentSpan span, String key) { | ||
Object tag = span.getTag(key); | ||
return tag instanceof String ? (String) tag : null; | ||
} | ||
|
||
public static boolean isKind(AgentSpan span, SpanKind kind) { | ||
return toSpanType(kind).equals(span.getSpanType()); | ||
} | ||
} | ||
} |
Oops, something went wrong.