-
Notifications
You must be signed in to change notification settings - Fork 291
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Map basic OpenTelemetry instrumentation types to their Datadog equiva…
…lents
- Loading branch information
Showing
9 changed files
with
315 additions
and
1 deletion.
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
39 changes: 39 additions & 0 deletions
39
...t-otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelExtensionHandler.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,39 @@ | ||
package datadog.opentelemetry.tooling; | ||
|
||
import datadog.trace.agent.tooling.ExtensionHandler; | ||
import java.net.URL; | ||
import java.net.URLConnection; | ||
import java.util.jar.JarEntry; | ||
import java.util.jar.JarFile; | ||
|
||
/** Handles OpenTelemetry instrumentations, so they can be loaded into the Datadog tracer. */ | ||
public final class OtelExtensionHandler extends ExtensionHandler { | ||
|
||
/** Handler for loading externally built OpenTelemetry extensions. */ | ||
public static final OtelExtensionHandler OPENTELEMETRY = new OtelExtensionHandler(); | ||
|
||
private static final String OPENTELEMETRY_MODULE_DESCRIPTOR = | ||
"META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule"; | ||
|
||
private static final String DATADOG_MODULE_DESCRIPTOR = | ||
"META-INF/services/datadog.trace.agent.tooling.InstrumenterModule"; | ||
|
||
@Override | ||
public JarEntry mapEntry(JarFile jar, String file) { | ||
if (DATADOG_MODULE_DESCRIPTOR.equals(file)) { | ||
// redirect request to include OpenTelemetry instrumentations | ||
return super.mapEntry(jar, OPENTELEMETRY_MODULE_DESCRIPTOR); | ||
} else { | ||
return super.mapEntry(jar, file); | ||
} | ||
} | ||
|
||
@Override | ||
public URLConnection mapContent(URL url, JarFile jar, JarEntry entry) { | ||
if (entry.getName().endsWith(".class")) { | ||
return new ClassMappingConnection(url, jar, entry, OtelInstrumentationMapper::new); | ||
} else { | ||
return new JarFileConnection(url, jar, entry); | ||
} | ||
} | ||
} |
109 changes: 109 additions & 0 deletions
109
...l/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelInstrumentationMapper.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,109 @@ | ||
package datadog.opentelemetry.tooling; | ||
|
||
import static datadog.trace.agent.tooling.ExtensionHandler.MAP_LOGGING; | ||
|
||
import datadog.trace.agent.tooling.InstrumenterModule; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import net.bytebuddy.jar.asm.ClassVisitor; | ||
import net.bytebuddy.jar.asm.MethodVisitor; | ||
import net.bytebuddy.jar.asm.commons.ClassRemapper; | ||
import net.bytebuddy.jar.asm.commons.Remapper; | ||
|
||
/** Maps OpenTelemetry instrumentations to use the Datadog {@link InstrumenterModule} API. */ | ||
public final class OtelInstrumentationMapper extends ClassRemapper { | ||
|
||
private static final Set<String> UNSUPPORTED_TYPES = | ||
new HashSet<>( | ||
Arrays.asList("io/opentelemetry/javaagent/tooling/muzzle/InstrumentationModuleMuzzle")); | ||
|
||
private static final Set<String> UNSUPPORTED_METHODS = | ||
new HashSet<>( | ||
Arrays.asList( | ||
"getMuzzleReferences", "getMuzzleHelperClassNames", "registerMuzzleVirtualFields")); | ||
|
||
public OtelInstrumentationMapper(ClassVisitor classVisitor) { | ||
super(classVisitor, Renamer.INSTANCE); | ||
} | ||
|
||
@Override | ||
public void visit( | ||
int version, | ||
int access, | ||
String name, | ||
String signature, | ||
String superName, | ||
String[] interfaces) { | ||
super.visit(version, access, name, signature, superName, removeUnsupportedTypes(interfaces)); | ||
} | ||
|
||
@Override | ||
public MethodVisitor visitMethod( | ||
int access, String name, String descriptor, String signature, String[] exceptions) { | ||
if (!UNSUPPORTED_METHODS.contains(name)) { | ||
return super.visitMethod(access, name, descriptor, signature, exceptions); | ||
} else { | ||
return null; // remove unsupported method | ||
} | ||
} | ||
|
||
private String[] removeUnsupportedTypes(String[] interfaces) { | ||
List<String> filtered = null; | ||
for (int i = interfaces.length - 1; i >= 0; i--) { | ||
if (UNSUPPORTED_TYPES.contains(interfaces[i])) { | ||
if (null == filtered) { | ||
filtered = new ArrayList<>(Arrays.asList(interfaces)); | ||
} | ||
filtered.remove(i); // remove unsupported interface | ||
} | ||
} | ||
return null != filtered ? filtered.toArray(new String[0]) : interfaces; | ||
} | ||
|
||
static final class Renamer extends Remapper { | ||
static final Renamer INSTANCE = new Renamer(); | ||
|
||
private static final String OTEL_JAVAAGENT_SHADED_PREFIX = | ||
"io/opentelemetry/javaagent/shaded/io/opentelemetry/"; | ||
|
||
/** Datadog equivalent of OpenTelemetry instrumentation classes. */ | ||
private static final Map<String, String> RENAMED_TYPES = new HashMap<>(); | ||
|
||
static { | ||
RENAMED_TYPES.put( | ||
"io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule", | ||
"datadog/opentelemetry/tooling/OtelInstrumenterModule"); | ||
RENAMED_TYPES.put( | ||
"io/opentelemetry/javaagent/extension/instrumentation/TypeInstrumentation", | ||
"datadog/opentelemetry/tooling/OtelInstrumenter"); | ||
RENAMED_TYPES.put( | ||
"io/opentelemetry/javaagent/extension/instrumentation/TypeTransformer", | ||
"datadog/opentelemetry/tooling/OtelTransformer"); | ||
RENAMED_TYPES.put( | ||
"io/opentelemetry/javaagent/bootstrap/Java8BytecodeBridge", | ||
"datadog/trace/bootstrap/otel/Java8BytecodeBridge"); | ||
RENAMED_TYPES.put( | ||
"io/opentelemetry/javaagent/extension/matcher/AgentElementMatchers", | ||
"datadog/trace/agent/tooling/bytebuddy/matcher/HierarchyMatchers"); | ||
} | ||
|
||
@Override | ||
public String map(String internalName) { | ||
String rename = RENAMED_TYPES.get(internalName); | ||
if (null != rename) { | ||
return rename; | ||
} | ||
// map OpenTelemetry's shaded API to our embedded copy | ||
if (internalName.startsWith(OTEL_JAVAAGENT_SHADED_PREFIX)) { | ||
return "datadog/trace/bootstrap/otel/" | ||
+ internalName.substring(OTEL_JAVAAGENT_SHADED_PREFIX.length()); | ||
} | ||
return MAP_LOGGING.apply(internalName); | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...agent-otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelInstrumenter.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,41 @@ | ||
package datadog.opentelemetry.tooling; | ||
|
||
import datadog.trace.agent.tooling.Instrumenter; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
/** Replaces OpenTelemetry's {@code TypeInstrumentation} callback when mapping extensions. */ | ||
public interface OtelInstrumenter | ||
extends Instrumenter.ForTypeHierarchy, | ||
Instrumenter.HasMethodAdvice, | ||
Instrumenter.HasTypeAdvice { | ||
|
||
@Override | ||
default String hierarchyMarkerType() { | ||
return null; // no hint available | ||
} | ||
|
||
@Override | ||
default ElementMatcher<TypeDescription> hierarchyMatcher() { | ||
return typeMatcher(); | ||
} | ||
|
||
@Override | ||
default void methodAdvice(MethodTransformer methodTransformer) { | ||
OtelTransformerState.capture(this).with(methodTransformer); | ||
} | ||
|
||
@Override | ||
default void typeAdvice(TypeTransformer typeTransformer) { | ||
OtelTransformerState.capture(this).with(typeTransformer); | ||
} | ||
|
||
ElementMatcher<TypeDescription> typeMatcher(); | ||
|
||
/** | ||
* Once both transformers have been captured in {@link #methodAdvice} and {@link #typeAdvice} the | ||
* {@code #transform} method will be called. This allows the extension to register method and type | ||
* advice at the same time, using the single interface expected by OpenTelemetry. | ||
*/ | ||
void transform(OtelTransformer transformer); | ||
} |
33 changes: 33 additions & 0 deletions
33
...otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelInstrumenterModule.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,33 @@ | ||
package datadog.opentelemetry.tooling; | ||
|
||
import datadog.trace.agent.tooling.InstrumenterModule; | ||
import datadog.trace.api.InstrumenterConfig; | ||
|
||
/** | ||
* Replaces OpenTelemetry's {@code InstrumentationModule} when mapping extensions. | ||
* | ||
* <p>Original instrumentation names and aliases are prefixed with {@literal "otel."}. | ||
*/ | ||
public abstract class OtelInstrumenterModule extends InstrumenterModule.Tracing { | ||
|
||
public OtelInstrumenterModule(String instrumentationName, String... additionalNames) { | ||
super(namespace(instrumentationName), namespace(additionalNames)); | ||
} | ||
|
||
@Override | ||
protected boolean defaultEnabled() { | ||
return InstrumenterConfig.get().isTraceOtelEnabled() && super.defaultEnabled(); | ||
} | ||
|
||
private static String namespace(String name) { | ||
return "otel." + name; | ||
} | ||
|
||
private static String[] namespace(String[] names) { | ||
String[] namespaced = new String[names.length]; | ||
for (int i = 0; i < names.length; i++) { | ||
namespaced[i] = namespace(names[i]); | ||
} | ||
return namespaced; | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
.../agent-otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelTransformer.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,14 @@ | ||
package datadog.opentelemetry.tooling; | ||
|
||
import net.bytebuddy.agent.builder.AgentBuilder; | ||
import net.bytebuddy.description.method.MethodDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
/** Replaces OpenTelemetry's {@code TypeTransformer} callback when mapping extensions. */ | ||
public interface OtelTransformer { | ||
|
||
void applyAdviceToMethod( | ||
ElementMatcher<? super MethodDescription> methodMatcher, String adviceClassName); | ||
|
||
void applyTransformer(AgentBuilder.Transformer transformer); | ||
} |
73 changes: 73 additions & 0 deletions
73
...t-otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelTransformerState.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,73 @@ | ||
package datadog.opentelemetry.tooling; | ||
|
||
import datadog.trace.agent.tooling.Instrumenter; | ||
import net.bytebuddy.agent.builder.AgentBuilder; | ||
import net.bytebuddy.description.method.MethodDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
/** | ||
* {@link OtelTransformer} state captured when processing OpenTelemetry extensions with {@code | ||
* CombiningTransformerBuilder}. Assumes that the builder is single threaded. | ||
* | ||
* <p>OpenTelemetry has a single transformer callback with methods to register method advice and | ||
* type transformations at the same time, whereas the Datadog tracer has separate {@link | ||
* Instrumenter.MethodTransformer} and {@link Instrumenter.TypeTransformer} callbacks. | ||
* | ||
* <p>To map between the two we capture the Datadog method and type transformers here, from calls to | ||
* {@link OtelInstrumenter}. Once we have captured both transformers we trigger the single transform | ||
* request through the mapped OpenTelemetry callback. | ||
*/ | ||
final class OtelTransformerState implements OtelTransformer { | ||
private static final OtelTransformerState CURRENT = new OtelTransformerState(); | ||
|
||
private OtelInstrumenter instrumenter; | ||
private Instrumenter.MethodTransformer methodTransformer; | ||
private Instrumenter.TypeTransformer typeTransformer; | ||
|
||
static OtelTransformerState capture(OtelInstrumenter instrumenter) { | ||
if (instrumenter != CURRENT.instrumenter) { | ||
CURRENT.reset(); | ||
CURRENT.instrumenter = instrumenter; | ||
} | ||
return CURRENT; | ||
} | ||
|
||
void with(Instrumenter.MethodTransformer methodTransformer) { | ||
this.methodTransformer = methodTransformer; | ||
if (null != this.typeTransformer) { | ||
triggerTransform(); | ||
} | ||
} | ||
|
||
void with(Instrumenter.TypeTransformer typeTransformer) { | ||
this.typeTransformer = typeTransformer; | ||
if (null != this.methodTransformer) { | ||
triggerTransform(); | ||
} | ||
} | ||
|
||
private void triggerTransform() { | ||
try { | ||
instrumenter.transform(this); | ||
} finally { | ||
reset(); | ||
} | ||
} | ||
|
||
private void reset() { | ||
this.instrumenter = null; | ||
this.methodTransformer = null; | ||
this.typeTransformer = null; | ||
} | ||
|
||
@Override | ||
public void applyAdviceToMethod( | ||
ElementMatcher<? super MethodDescription> methodMatcher, String adviceClassName) { | ||
methodTransformer.applyAdvice(methodMatcher, adviceClassName); | ||
} | ||
|
||
@Override | ||
public void applyTransformer(AgentBuilder.Transformer transformer) { | ||
typeTransformer.applyAdvice(transformer::transform); | ||
} | ||
} |