Skip to content

Commit

Permalink
Move the OpenTelemetry shim to its own module so we can re-use it for…
Browse files Browse the repository at this point in the history
… drop-in support (#7072)

* Move the OpenTelemetry shim to its own module so we can re-use it for drop-in support.

  We now embed a shaded copy of the OpenTelemetry API which has been pre-instrumented
  at build time with our shim. This shaded API will be on the bootstrap class-path so it
  can be used by drop-in advice without having to continually inject it everywhere the
  drop-in advice is injected.

  The same shim is used at runtime when instrumenting use of the OpenTelemetry API on
  the application class-path.

* Ensure shared plugin class-loader is rebuilt when processing different instrument setups
  • Loading branch information
mcculls authored May 28, 2024
1 parent f306e48 commit 4cdcb05
Show file tree
Hide file tree
Showing 30 changed files with 407 additions and 135 deletions.
15 changes: 11 additions & 4 deletions buildSrc/src/main/groovy/InstrumentPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -178,17 +178,20 @@ interface InstrumentWorkParameters extends WorkParameters {
abstract class InstrumentAction implements WorkAction<InstrumentWorkParameters> {
private static final Object lock = new Object()
private static ClassLoader pluginCL
private static volatile long lastBuildStamp
private static String cachedPluginPath
private static volatile long cachedBuildStamp

@Override
void execute() {
// reset shared class-loaders each time a new build starts
long buildStamp = parameters.buildStartedTime.get()
if (lastBuildStamp < buildStamp || !pluginCL) {
String pluginPath = parameters.pluginClassPath.join(':')
if (rebuildSharedClassLoader(buildStamp, pluginPath)) {
synchronized (lock) {
if (lastBuildStamp < buildStamp || !pluginCL) {
if (rebuildSharedClassLoader(buildStamp, pluginPath)) {
pluginCL = createClassLoader(parameters.pluginClassPath)
lastBuildStamp = buildStamp
cachedPluginPath = pluginPath
cachedBuildStamp = buildStamp
}
}
}
Expand All @@ -199,6 +202,10 @@ abstract class InstrumentAction implements WorkAction<InstrumentWorkParameters>
InstrumentingPlugin.instrumentClasses(plugins, instrumentingCL, sourceDirectory, targetDirectory)
}

static boolean rebuildSharedClassLoader(long buildStamp, String pluginPath) {
return cachedBuildStamp < buildStamp || cachedPluginPath != pluginPath
}

static ClassLoader createClassLoader(cp, parent = InstrumentAction.classLoader) {
return new URLClassLoader(cp*.toURI()*.toURL() as URL[], parent as ClassLoader)
}
Expand Down
66 changes: 66 additions & 0 deletions dd-java-agent/agent-otel/otel-bootstrap/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
plugins {
id "com.github.johnrengelman.shadow"
}

apply from: "$rootDir/gradle/java.gradle"
apply plugin: 'instrument'

configurations {
embeddedClasspath {
visible = false
canBeConsumed = false
canBeResolved = true
}
instrumentPluginClasspath {
visible = false
canBeConsumed = false
canBeResolved = true
}
}

instrument.plugins = ['datadog.opentelemetry.tooling.OtelShimGradlePlugin']

minimumInstructionCoverage = 0.0
minimumBranchCoverage = 0.0

forbiddenApis {
ignoreFailures = true
}
spotbugs {
onlyAnalyze = ['none']
}

dependencies {
// latest OpenTelemetry API for drop-in support; instrumented at build-time with our shim
embeddedClasspath group: 'io.opentelemetry', name: 'opentelemetry-api', version: '1.38.0'

implementation project(':dd-java-agent:agent-otel:otel-shim')

instrumentPluginClasspath project(':dd-java-agent:agent-otel:otel-tooling')
}

// unpack embeddedClasspath to same path as compiled classes so it can get instrumented
tasks.register('unpackJars', Copy) {
dependsOn configurations.embeddedClasspath
exclude 'META-INF/'
from {
configurations.embeddedClasspath.collect { zipTree(it) }
}
into compileJava.destinationDirectory
}
tasks.named('compileJava') {
dependsOn 'unpackJars'
}

shadowJar {
dependencies deps.excludeShared

exclude 'io/opentelemetry/context/internal/shaded/**'

relocate 'io.opentelemetry', 'datadog.trace.bootstrap.otel'
relocate 'datadog.opentelemetry.shim', 'datadog.trace.bootstrap.otel.shim'
}

jar {
archiveClassifier = 'unbundled'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// placeholder to activate the compiler task
11 changes: 11 additions & 0 deletions dd-java-agent/agent-otel/otel-shim/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apply from: "$rootDir/gradle/java.gradle"

minimumInstructionCoverage = 0.0
minimumBranchCoverage = 0.0

dependencies {
// minimum OpenTelemetry API version this shim is compatible with
compileOnly group: 'io.opentelemetry', name: 'opentelemetry-api', version: '1.4.0'

implementation project(':internal-api')
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package datadog.trace.instrumentation.opentelemetry14.context;
package datadog.opentelemetry.shim.context;

import datadog.opentelemetry.shim.trace.OtelSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.instrumentation.opentelemetry14.trace.OtelSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import datadog.trace.bootstrap.instrumentation.api.AttachableWrapper;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
Expand Down Expand Up @@ -56,6 +59,38 @@ public Scope makeCurrent() {
return scope;
}

public static Context current() {
// Check empty context
AgentSpan agentCurrentSpan = AgentTracer.activeSpan();
if (null == agentCurrentSpan) {
return OtelContext.ROOT;
}
// Get OTel current span
Span otelCurrentSpan = null;
if (agentCurrentSpan instanceof AttachableWrapper) {
Object wrapper = ((AttachableWrapper) agentCurrentSpan).getWrapper();
if (wrapper instanceof OtelSpan) {
otelCurrentSpan = (OtelSpan) wrapper;
}
}
if (otelCurrentSpan == null) {
otelCurrentSpan = new OtelSpan(agentCurrentSpan);
}
// Get OTel root span
Span otelRootSpan = null;
AgentSpan agentRootSpan = agentCurrentSpan.getLocalRootSpan();
if (agentRootSpan instanceof AttachableWrapper) {
Object wrapper = ((AttachableWrapper) agentRootSpan).getWrapper();
if (wrapper instanceof OtelSpan) {
otelRootSpan = (OtelSpan) wrapper;
}
}
if (otelRootSpan == null) {
otelRootSpan = new OtelSpan(agentRootSpan);
}
return new OtelContext(otelCurrentSpan, otelRootSpan);
}

@Override
public String toString() {
return "OtelContext{"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.opentelemetry14.context;
package datadog.opentelemetry.shim.context;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AttachableWrapper;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package datadog.trace.instrumentation.opentelemetry14.context.propagation;
package datadog.opentelemetry.shim.context.propagation;

import static datadog.opentelemetry.shim.trace.OtelSpanContext.fromRemote;
import static datadog.trace.api.TracePropagationStyle.TRACECONTEXT;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelSpanContext.fromRemote;

import datadog.opentelemetry.shim.context.OtelContext;
import datadog.opentelemetry.shim.trace.OtelExtractedContext;
import datadog.opentelemetry.shim.trace.OtelSpan;
import datadog.trace.api.TracePropagationStyle;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan.Context.Extracted;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import datadog.trace.bootstrap.instrumentation.api.TagContext;
import datadog.trace.instrumentation.opentelemetry14.context.OtelContext;
import datadog.trace.instrumentation.opentelemetry14.trace.OtelExtractedContext;
import datadog.trace.instrumentation.opentelemetry14.trace.OtelSpan;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.TraceState;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.opentelemetry14.context.propagation;
package datadog.opentelemetry.shim.context.propagation;

import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.opentelemetry14.context.propagation;
package datadog.opentelemetry.shim.context.propagation;

import io.opentelemetry.api.trace.TraceState;
import io.opentelemetry.api.trace.TraceStateBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.opentelemetry14.trace;
package datadog.opentelemetry.shim.trace;

import static datadog.trace.api.DDTags.ANALYTICS_SAMPLE_RATE;
import static datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.opentelemetry14.trace;
package datadog.opentelemetry.shim.trace;

import datadog.trace.api.DDSpanId;
import datadog.trace.api.DDTraceId;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package datadog.trace.instrumentation.opentelemetry14.trace;
package datadog.opentelemetry.shim.trace;

import static datadog.opentelemetry.shim.trace.OtelConventions.applyNamingConvention;
import static datadog.opentelemetry.shim.trace.OtelConventions.applyReservedAttribute;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.applyNamingConvention;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.applyReservedAttribute;
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
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package datadog.trace.instrumentation.opentelemetry14.trace;
package datadog.opentelemetry.shim.trace;

import static datadog.opentelemetry.shim.trace.OtelConventions.ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES;
import static datadog.opentelemetry.shim.trace.OtelConventions.OPERATION_NAME_SPECIFIC_ATTRIBUTE;
import static datadog.opentelemetry.shim.trace.OtelConventions.toSpanKindTagValue;
import static datadog.opentelemetry.shim.trace.OtelExtractedContext.extract;
import static datadog.trace.api.DDTags.ANALYTICS_SAMPLE_RATE;
import static datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.OPERATION_NAME_SPECIFIC_ATTRIBUTE;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.toSpanKindTagValue;
import static datadog.trace.instrumentation.opentelemetry14.trace.OtelExtractedContext.extract;
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
import static java.lang.Boolean.parseBoolean;
import static java.util.Locale.ROOT;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.opentelemetry14.trace;
package datadog.opentelemetry.shim.trace;

import datadog.trace.api.DDSpanId;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package datadog.trace.instrumentation.opentelemetry14.trace;
package datadog.opentelemetry.shim.trace;

import datadog.opentelemetry.shim.context.propagation.TraceStateHelper;
import datadog.trace.api.DDSpanId;
import datadog.trace.api.DDTraceId;
import datadog.trace.bootstrap.instrumentation.api.SpanLink;
import datadog.trace.bootstrap.instrumentation.api.SpanLinkAttributes;
import datadog.trace.instrumentation.opentelemetry14.context.propagation.TraceStateHelper;
import io.opentelemetry.api.trace.SpanContext;
import java.util.List;

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

import static datadog.trace.instrumentation.opentelemetry14.trace.OtelConventions.SPAN_KIND_INTERNAL;
import static datadog.opentelemetry.shim.trace.OtelConventions.SPAN_KIND_INTERNAL;

import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import io.opentelemetry.api.trace.SpanBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.opentelemetry14.trace;
package datadog.opentelemetry.shim.trace;

import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.opentelemetry14.trace;
package datadog.opentelemetry.shim.trace;

import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
Expand Down
9 changes: 9 additions & 0 deletions dd-java-agent/agent-otel/otel-tooling/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apply from: "$rootDir/gradle/java.gradle"

minimumInstructionCoverage = 0.0
minimumBranchCoverage = 0.0

dependencies {
api deps.bytebuddy
api deps.bytebuddyagent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package datadog.opentelemetry.tooling;

import static datadog.opentelemetry.tooling.OtelShimInjector.OTEL_CONTEXT_CLASSES;
import static datadog.opentelemetry.tooling.OtelShimInjector.OTEL_CONTEXT_STORAGE_CLASSES;
import static datadog.opentelemetry.tooling.OtelShimInjector.OTEL_ENTRYPOINT_CLASSES;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;

import de.thetaphi.forbiddenapis.SuppressForbidden;
import java.io.File;
import java.io.IOException;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;

/**
* Bytebuddy gradle plugin which injects our OpenTelemetry shim into the target API.
*
* @see "buildSrc/src/main/groovy/InstrumentPlugin.groovy"
*/
public class OtelShimGradlePlugin extends Plugin.ForElementMatcher {
private final File targetDir;

@SuppressForbidden
public OtelShimGradlePlugin(File targetDir) {
super(
namedOneOf(OTEL_ENTRYPOINT_CLASSES)
.or(namedOneOf(OTEL_CONTEXT_STORAGE_CLASSES))
.or(namedOneOf(OTEL_CONTEXT_CLASSES)));
this.targetDir = targetDir;
}

@Override
public DynamicType.Builder<?> apply(
final DynamicType.Builder<?> builder,
final TypeDescription typeDescription,
final ClassFileLocator classFileLocator) {
return builder.visit(OtelShimInjector.INSTANCE);
}

@Override
public void close() throws IOException {}
}
Loading

0 comments on commit 4cdcb05

Please sign in to comment.