diff --git a/dd-java-agent/agent-otel/otel-bootstrap/build.gradle b/dd-java-agent/agent-otel/otel-bootstrap/build.gradle index c57766ed84f..a781f141754 100644 --- a/dd-java-agent/agent-otel/otel-bootstrap/build.gradle +++ b/dd-java-agent/agent-otel/otel-bootstrap/build.gradle @@ -2,6 +2,9 @@ plugins { id "com.github.johnrengelman.shadow" } +def otelApiVersion = '1.38.0' +def otelInstrumentationApiVersion = '2.4.0' + apply from: "$rootDir/gradle/java.gradle" apply plugin: 'instrument' @@ -25,6 +28,7 @@ minimumInstructionCoverage = 0.0 minimumBranchCoverage = 0.0 forbiddenApis { + failOnMissingClasses = false ignoreFailures = true } spotbugs { @@ -33,8 +37,14 @@ spotbugs { 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' + embeddedClasspath group: 'io.opentelemetry', name: 'opentelemetry-api', version: otelApiVersion + embeddedClasspath group: 'io.opentelemetry.instrumentation', name: 'opentelemetry-instrumentation-api', version: otelInstrumentationApiVersion + embeddedClasspath group: 'io.opentelemetry.javaagent', name: 'opentelemetry-javaagent-extension-api', version: "$otelInstrumentationApiVersion-alpha" + + // selected bootstrap types shared across multiple OpenTelemetry instrumentations + embeddedClasspath group: 'io.opentelemetry.javaagent.instrumentation', name: 'opentelemetry-javaagent-servlet-common-bootstrap', version: "$otelInstrumentationApiVersion-alpha" + compileOnly project(':dd-java-agent:agent-bootstrap') implementation project(':dd-java-agent:agent-otel:otel-shim') instrumentPluginClasspath project(path: ':dd-java-agent:agent-otel:otel-tooling', configuration: 'instrumentPluginClasspath') @@ -44,6 +54,8 @@ dependencies { tasks.register('unpackJars', Copy) { dependsOn configurations.embeddedClasspath exclude 'META-INF/' + exclude '**/module-info.class' + exclude '**/package-info.class' from { configurations.embeddedClasspath.collect { zipTree(it) } } @@ -56,9 +68,33 @@ tasks.named('compileJava') { shadowJar { dependencies deps.excludeShared + include '*.jar' + include 'io/opentelemetry/api/**' + exclude 'io/opentelemetry/api/incubator/**' + include 'io/opentelemetry/context/**' exclude 'io/opentelemetry/context/internal/shaded/**' + include 'io/opentelemetry/semconv/**' + include 'io/opentelemetry/instrumentation/api/**' + exclude 'io/opentelemetry/instrumentation/api/incubator/**' + exclude 'io/opentelemetry/instrumentation/api/internal/cache/**' + exclude 'io/opentelemetry/instrumentation/api/internal/RuntimeVirtualFieldSupplier*' + exclude 'io/opentelemetry/instrumentation/api/util/VirtualField*' + include 'io/opentelemetry/javaagent/bootstrap/**' + exclude 'io/opentelemetry/javaagent/bootstrap/CallDepth*' + exclude 'io/opentelemetry/javaagent/bootstrap/internal/ClassLoaderMatcherCacheHolder*' + exclude 'io/opentelemetry/javaagent/bootstrap/internal/ConfiguredResourceAttributesHolder*' + exclude 'io/opentelemetry/javaagent/bootstrap/internal/DeprecatedConfigProperties*' + exclude 'io/opentelemetry/javaagent/bootstrap/internal/EmptyInstrumentationConfig*' + exclude 'io/opentelemetry/javaagent/bootstrap/internal/InClassLoaderMatcher*' + exclude 'io/opentelemetry/javaagent/bootstrap/internal/InstrumentationConfig*' + include 'datadog/opentelemetry/shim/**' + include 'datadog/trace/bootstrap/otel/**' - relocate 'io.opentelemetry', 'datadog.trace.bootstrap.otel' + relocate 'io.opentelemetry.api', 'datadog.trace.bootstrap.otel.api' + relocate 'io.opentelemetry.context', 'datadog.trace.bootstrap.otel.context' + relocate 'io.opentelemetry.semconv', 'datadog.trace.bootstrap.otel.semconv' + relocate 'io.opentelemetry.instrumentation.api', 'datadog.trace.bootstrap.otel.instrumentation.api' + relocate 'io.opentelemetry.javaagent.bootstrap', 'datadog.trace.bootstrap.otel.instrumentation' relocate 'datadog.opentelemetry.shim', 'datadog.trace.bootstrap.otel.shim' } diff --git a/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/Java8BytecodeBridge.java b/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/Java8BytecodeBridge.java deleted file mode 100644 index ffc369acd08..00000000000 --- a/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/Java8BytecodeBridge.java +++ /dev/null @@ -1,33 +0,0 @@ -package datadog.trace.bootstrap.otel; - -import io.opentelemetry.api.baggage.Baggage; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; - -/** Support references to OpenTelemetry's Java8BytecodeBridge in external extensions. */ -public final class Java8BytecodeBridge { - - // Static helpers that will redirect to our embedded copy of the OpenTelemetry API - - public static Context currentContext() { - return Context.current(); - } - - public static Context rootContext() { - return Context.root(); - } - - public static Span currentSpan() { - return Span.current(); - } - - public static Span spanFromContext(Context context) { - return Span.fromContext(context); - } - - public static Baggage baggageFromContext(Context context) { - return Baggage.fromContext(context); - } - - private Java8BytecodeBridge() {} -} diff --git a/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/instrumentation/CallDepth.java b/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/instrumentation/CallDepth.java new file mode 100644 index 00000000000..d0376ff2f4c --- /dev/null +++ b/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/instrumentation/CallDepth.java @@ -0,0 +1,24 @@ +package datadog.trace.bootstrap.otel.instrumentation; + +import datadog.trace.bootstrap.CallDepthThreadLocalMap; + +/** Redirects requests to our own {@link CallDepthThreadLocalMap}. */ +public final class CallDepth { + private final Class clazz; + + private CallDepth(final Class clazz) { + this.clazz = clazz; + } + + public static CallDepth forClass(Class clazz) { + return new CallDepth(clazz); + } + + public int getAndIncrement() { + return CallDepthThreadLocalMap.incrementCallDepth(clazz); + } + + public int decrementAndGet() { + return CallDepthThreadLocalMap.decrementCallDepth(clazz); + } +} diff --git a/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/instrumentation/internal/InstrumentationConfig.java b/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/instrumentation/internal/InstrumentationConfig.java new file mode 100644 index 00000000000..78df63fde73 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/instrumentation/internal/InstrumentationConfig.java @@ -0,0 +1,93 @@ +package datadog.trace.bootstrap.otel.instrumentation.internal; + +import datadog.trace.bootstrap.config.provider.ConfigProvider; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** Redirects requests to our own {@link ConfigProvider}. */ +public final class InstrumentationConfig { + private static final InstrumentationConfig INSTANCE = new InstrumentationConfig(); + + private static final Pattern DURATION_PATTERN = Pattern.compile("(\\d+)(ms|[DdHhMmSs]?)"); + + private static final ConfigProvider delegate = ConfigProvider.getInstance(); + + public static InstrumentationConfig get() { + return INSTANCE; + } + + public String getString(String name) { + return delegate.getString(name); + } + + public String getString(String name, String defaultValue) { + return delegate.getString(name, defaultValue); + } + + public boolean getBoolean(String name, boolean defaultValue) { + return delegate.getBoolean(name, defaultValue); + } + + public int getInt(String name, int defaultValue) { + return delegate.getInteger(name, defaultValue); + } + + public long getLong(String name, long defaultValue) { + return delegate.getLong(name, defaultValue); + } + + public double getDouble(String name, double defaultValue) { + return delegate.getDouble(name, defaultValue); + } + + public Duration getDuration(String name, Duration defaultValue) { + String durationString = delegate.getString(name); + if (null == durationString) { + return defaultValue; + } + Matcher matcher = DURATION_PATTERN.matcher(durationString); + if (matcher.matches()) { + long value = Integer.parseInt(matcher.group(1)); + String unit = matcher.group(2); + if ("D".equalsIgnoreCase(unit)) { + return Duration.ofDays(value); + } else if ("H".equalsIgnoreCase(unit)) { + return Duration.ofHours(value); + } else if ("M".equalsIgnoreCase(unit)) { + return Duration.ofMinutes(value); + } else if ("S".equalsIgnoreCase(unit)) { + return Duration.ofSeconds(value); + } else { + return Duration.ofMillis(value); // already in ms + } + } else { + throw new IllegalArgumentException( + "Invalid duration property " + name + "=" + durationString); + } + } + + public List getList(String name) { + return getList(name, Collections.emptyList()); + } + + public List getList(String name, List defaultValue) { + return delegate.getList(name, defaultValue); + } + + public Set getSet(String name, Set defaultValue) { + return delegate.getSet(name, defaultValue); + } + + public Map getMap(String name, Map defaultValue) { + Map map = delegate.getMergedMap(name); + if (map.isEmpty()) { + map = defaultValue; + } + return map; + } +} diff --git a/dd-java-agent/agent-otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelInstrumentationMapper.java b/dd-java-agent/agent-otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelInstrumentationMapper.java index ae4638d0af5..7bdc3d2d754 100644 --- a/dd-java-agent/agent-otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelInstrumentationMapper.java +++ b/dd-java-agent/agent-otel/otel-tooling/src/main/java/datadog/opentelemetry/tooling/OtelInstrumentationMapper.java @@ -60,11 +60,6 @@ private String[] removeUnsupportedTypes(String[] 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/"; - - private static final String ASM_PREFIX = "org/objectweb/asm/"; - /** Datadog equivalent of OpenTelemetry instrumentation classes. */ private static final Map RENAMED_TYPES = new HashMap<>(); @@ -111,9 +106,25 @@ static final class Renamer extends Remapper { RENAMED_TYPES.put( "io/opentelemetry/javaagent/tooling/muzzle/references/Source", Type.getInternalName(OtelMuzzleRefBuilder.Source.class)); - RENAMED_TYPES.put( - "io/opentelemetry/javaagent/bootstrap/Java8BytecodeBridge", - "datadog/trace/bootstrap/otel/Java8BytecodeBridge"); + } + + /** OpenTelemetry and related packages shaded inside the tracer. */ + private static final Map RENAMED_PACKAGES = new HashMap<>(); + + static { + RENAMED_PACKAGES.put( + "io/opentelemetry/javaagent/shaded/io/opentelemetry/", "datadog/trace/bootstrap/otel/"); + + RENAMED_PACKAGES.put("io/opentelemetry/api/", "datadog/trace/bootstrap/otel/api/"); + RENAMED_PACKAGES.put("io/opentelemetry/context/", "datadog/trace/bootstrap/otel/context/"); + RENAMED_PACKAGES.put("io/opentelemetry/semconv/", "datadog/trace/bootstrap/otel/semconv/"); + + RENAMED_PACKAGES.put( + "io/opentelemetry/instrumentation/", "datadog/trace/bootstrap/otel/instrumentation/"); + RENAMED_PACKAGES.put( + "io/opentelemetry/javaagent/bootstrap/", "datadog/trace/bootstrap/otel/instrumentation/"); + + RENAMED_PACKAGES.put("org/objectweb/asm/", "net/bytebuddy/jar/asm/"); } @Override @@ -122,14 +133,10 @@ public String map(String 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()); - } - // map unshaded ASM types to the shaded copy in byte-buddy - if (internalName.startsWith(ASM_PREFIX)) { - return "net/bytebuddy/jar/asm/" + internalName.substring(ASM_PREFIX.length()); + for (Map.Entry mapping : RENAMED_PACKAGES.entrySet()) { + if (internalName.startsWith(mapping.getKey())) { + return mapping.getValue() + internalName.substring(mapping.getKey().length()); + } } return MAP_LOGGING.apply(internalName); } diff --git a/internal-api/src/main/java/datadog/trace/bootstrap/config/provider/OtelEnvironmentConfigSource.java b/internal-api/src/main/java/datadog/trace/bootstrap/config/provider/OtelEnvironmentConfigSource.java index 6c490d7716b..0c6bc71b222 100644 --- a/internal-api/src/main/java/datadog/trace/bootstrap/config/provider/OtelEnvironmentConfigSource.java +++ b/internal-api/src/main/java/datadog/trace/bootstrap/config/provider/OtelEnvironmentConfigSource.java @@ -36,6 +36,8 @@ final class OtelEnvironmentConfigSource extends ConfigProvider.Source { private static final Logger log = LoggerFactory.getLogger(OtelEnvironmentConfigSource.class); + private final boolean enabled; + private final Map otelEnvironment = new HashMap<>(); private final Properties otelConfigFile = loadOtelConfigFile(); @@ -44,7 +46,15 @@ final class OtelEnvironmentConfigSource extends ConfigProvider.Source { @Override protected String get(String key) { - return otelEnvironment.get(key); + if (!enabled) { + return null; + } + + String value = otelEnvironment.get(key); + if (null == value && key.startsWith("otel.")) { + value = getOtelProperty(key); + } + return value; } @Override @@ -57,9 +67,10 @@ public ConfigOrigin origin() { } OtelEnvironmentConfigSource(Properties datadogConfigFile) { + this.enabled = traceOtelEnabled(); this.datadogConfigFile = datadogConfigFile; - if (traceOtelEnabled()) { + if (enabled) { setupOteEnvironment(); } }