From 6d302eda66db58c6859a3c64c887d00e8f28ce1c Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Thu, 9 Oct 2025 15:39:47 +0200 Subject: [PATCH 1/9] chore: Refactor instrumentation index tasks to avoid overlapping inputs * Emit index in proper generated source location and use the location as source dir. * Deduplicate code. --- .../agent/tooling/InstrumenterIndex.java | 6 +- .../trace/agent/tooling/KnownTypesIndex.java | 6 +- dd-java-agent/instrumentation/build.gradle | 74 +++++++++---------- 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterIndex.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterIndex.java index 357c7118add..29537852213 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterIndex.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterIndex.java @@ -334,10 +334,10 @@ public void writeIndex(Path indexFile) throws IOException { */ public static void main(String[] args) throws IOException { if (args.length < 1) { - throw new IllegalArgumentException("Expected: resources-dir"); + throw new IllegalArgumentException("Expected: index-dir"); } - Path resourcesDir = Paths.get(args[0]).toAbsolutePath(); + Path indexDir = Paths.get(args[0]).toAbsolutePath(); // satisfy some instrumenters that cache matchers in initializers HierarchyMatchers.registerIfAbsent(HierarchyMatchers.simpleChecks()); @@ -345,7 +345,7 @@ public static void main(String[] args) throws IOException { IndexGenerator indexGenerator = new IndexGenerator(); indexGenerator.buildIndex(); - indexGenerator.writeIndex(resourcesDir.resolve(INSTRUMENTER_INDEX_NAME)); + indexGenerator.writeIndex(indexDir.resolve(INSTRUMENTER_INDEX_NAME)); } } } diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/KnownTypesIndex.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/KnownTypesIndex.java index d1edbf54af3..9f517b93cb3 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/KnownTypesIndex.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/KnownTypesIndex.java @@ -154,10 +154,10 @@ public void writeIndex(Path indexFile) throws IOException { */ public static void main(String[] args) throws IOException { if (args.length < 1) { - throw new IllegalArgumentException("Expected: resources-dir"); + throw new IllegalArgumentException("Expected: index-dir"); } - Path resourcesDir = Paths.get(args[0]).toAbsolutePath(); + Path indexDir = Paths.get(args[0]).toAbsolutePath(); // satisfy some instrumenters that cache matchers in initializers HierarchyMatchers.registerIfAbsent(HierarchyMatchers.simpleChecks()); @@ -165,7 +165,7 @@ public static void main(String[] args) throws IOException { IndexGenerator indexGenerator = new IndexGenerator(); indexGenerator.buildIndex(); - indexGenerator.writeIndex(resourcesDir.resolve(KNOWN_TYPES_INDEX_NAME)); + indexGenerator.writeIndex(indexDir.resolve(KNOWN_TYPES_INDEX_NAME)); } } } diff --git a/dd-java-agent/instrumentation/build.gradle b/dd-java-agent/instrumentation/build.gradle index f9cba65eb2b..ee120011815 100644 --- a/dd-java-agent/instrumentation/build.gradle +++ b/dd-java-agent/instrumentation/build.gradle @@ -128,8 +128,7 @@ if (project.gradle.startParameter.taskNames.any { it.endsWith("generateMuzzleRep } } - -tasks.named('shadowJar') { +tasks.named('shadowJar', ShadowJar) { duplicatesStrategy = DuplicatesStrategy.FAIL dependencies { // the tracer is now in a separate shadow jar @@ -140,44 +139,43 @@ tasks.named('shadowJar') { dependencies deps.excludeShared } -tasks.register('generateInstrumenterIndex', JavaExec) { - // temporary config to add slf4j-simple so we get logging from instrumenters while indexing - def slf4jSimple = project.configurations.maybeCreate('slf4j-simple') - project.dependencies.add('slf4j-simple', "org.slf4j:slf4j-simple:${libs.versions.slf4j.get()}") - - def resourcesDir = "${sourceSets.main.output.resourcesDir}" - def indexFile = "${resourcesDir}/instrumenter.index" - - it.group = 'Build' - it.description = "Generate instrumenter.index" - it.mainClass = 'datadog.trace.agent.tooling.InstrumenterIndex$IndexGenerator' - it.classpath = project.configurations.runtimeClasspath + slf4jSimple - it.inputs.files(it.classpath) - it.outputs.files(indexFile) - it.args = [resourcesDir] - - dependsOn 'processResources' +// temporary config to add slf4j-simple so we get logging from instrumenters while indexing +project.configurations.register('slf4j-simple') { + it.dependencies.add(project.dependencyFactory.create("org.slf4j:slf4j-simple:${libs.versions.slf4j.get()}")) } -tasks.register('generateKnownTypesIndex', JavaExec) { - // temporary config to add slf4j-simple so we get logging from instrumenters while indexing - def slf4jSimple = project.configurations.maybeCreate('slf4j-simple') - project.dependencies.add('slf4j-simple', "org.slf4j:slf4j-simple:${libs.versions.slf4j.get()}") - - def resourcesDir = "${sourceSets.main.output.resourcesDir}" - def indexFile = "${resourcesDir}/known-types.index" +TaskProvider registerIndexTask(String indexTaskName, String indexer, String description ) { + def indexTask = tasks.register(indexTaskName, JavaExec) { + def destinationDir = project.layout.buildDirectory.dir("generated/${it.name}") - it.group = 'Build' - it.description = "Generate known-types.index" - it.mainClass = 'datadog.trace.agent.tooling.KnownTypesIndex$IndexGenerator' - it.classpath = project.configurations.runtimeClasspath + slf4jSimple - it.inputs.files(it.classpath) - it.outputs.files(indexFile) - it.args = [resourcesDir] - - dependsOn 'processResources' + it.group = LifecycleBasePlugin.BUILD_GROUP + it.description = description + it.mainClass = indexer + it.classpath = objects.fileCollection().tap { + it.from(project.configurations.named("runtimeClasspath")) + it.from(project.configurations.named('slf4j-simple')) + } + it.inputs.files(it.classpath) + it.outputs.dir(destinationDir) + it.argumentProviders.add(new CommandLineArgumentProvider() { + @Override + Iterable asArguments() { + return destinationDir.map { [it.asFile.path] }.get() + } + }) + } + sourceSets.main.resources.srcDir(indexTask) + return indexTask } -tasks.named("shadowJar", ShadowJar) { - dependsOn('generateInstrumenterIndex', 'generateKnownTypesIndex') -} +registerIndexTask( + 'generateInstrumenterIndex', + 'datadog.trace.agent.tooling.InstrumenterIndex$IndexGenerator', + 'Generate instrumenter.index' + ) + +registerIndexTask( + 'generateKnownTypesIndex', + 'datadog.trace.agent.tooling.KnownTypesIndex$IndexGenerator', + 'Generate known-types.index' + ) From ba69ea6c5a3c1d3a293cd18a137b1f78360e5465 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Thu, 9 Oct 2025 21:43:25 +0200 Subject: [PATCH 2/9] chore: Refactor dd-java-agent index tasks and included agent to avoid overlapping output * Expand the included agent jars in another location buildDir/included, this location contributes to the resources. * Emit index in separate location, modified index to receive input folder and output folder, this location contributes to the resources. * Fix indexer logging. --- .../trace/bootstrap/AgentJarIndex.java | 28 +++-- dd-java-agent/build.gradle | 103 +++++++++++------- 2 files changed, 83 insertions(+), 48 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java index 168c916d994..fa10b0ff8bf 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java @@ -13,11 +13,7 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import org.slf4j.Logger; @@ -102,6 +98,8 @@ static class IndexGenerator extends SimpleFileVisitor { private Path prefixRoot; private int prefixId; + private Map prefixMappings = new HashMap<>(); + IndexGenerator(Path resourcesDir) { this.resourcesDir = resourcesDir; @@ -125,6 +123,7 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { prefixRoot = dir; prefixes.add(dir.getFileName() + "/"); prefixId = prefixes.size(); + prefixMappings.put(prefixId, dir.getFileName().toString()); } return FileVisitResult.CONTINUE; } @@ -145,8 +144,13 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { int existingPrefixId = prefixTrie.apply(entryKey); if (-1 != existingPrefixId && prefixId != existingPrefixId) { log.warn( - "Detected duplicate content under '{}'. Ensure your content is under a distinct directory.", - entryKey); + "Detected duplicate content '{}' under '{}', already seen in {}. Ensure your content is under a distinct directory.", + entryKey, + resourcesDir.relativize(file).getName(0), // prefix + existingPrefixId == 0 + ? "" + : prefixMappings.get(existingPrefixId) // previous prefix + ); } prefixTrie.put(entryKey, prefixId); if (entryKey.endsWith("*")) { @@ -185,10 +189,18 @@ private static String computeEntryKey(Path path) { } public static void main(String[] args) throws IOException { + if (args.length < 1) { + throw new IllegalArgumentException("Expected: resources-dir"); + } + Path resourcesDir = Paths.get(args[0]).toAbsolutePath(); + Path indexDir = resourcesDir; + if (args.length == 2) { + indexDir = Paths.get(args[1]).toAbsolutePath(); + } IndexGenerator indexGenerator = new IndexGenerator(resourcesDir); Files.walkFileTree(resourcesDir, indexGenerator); - indexGenerator.writeIndex(resourcesDir.resolve(AGENT_INDEX_FILE_NAME)); + indexGenerator.writeIndex(indexDir.resolve(AGENT_INDEX_FILE_NAME)); } } } diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index c68e8ec254c..ea629fbfdc3 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -17,12 +17,22 @@ configurations { traceShadowInclude } +def includedAgentDir = project.layout.buildDirectory.dir("generated/included") +def includedJarFileTree = fileTree(includedAgentDir) { + builtBy(project.tasks.withType(Copy).matching { it.name.startsWith("expandAgentShadowJar") }) +} +tasks.named("processResources") { + dependsOn(includedJarFileTree) +} + // The special pre-check should be compiled with Java 6 to detect unsupported Java versions // and prevent issues for users that still using them. sourceSets { "main_java6" { java.srcDirs "${project.projectDir}/src/main/java6" + } + main.resources.srcDir(includedAgentDir) } def java6CompileTask = tasks.named("compileMain_java6Java") { @@ -52,9 +62,6 @@ def generalShadowJarConfig(ShadowJar shadowJarTask) { duplicatesStrategy = DuplicatesStrategy.FAIL - // Include AgentPreCheck compiled with Java 6. - from sourceSets.main_java6.output - // Remove some cruft from the final jar. // These patterns should NOT include **/META-INF/maven/**/pom.properties, which is // used to report our own dependencies, but we should remove the top-level metadata @@ -114,10 +121,14 @@ def generalShadowJarConfig(ShadowJar shadowJarTask) { } } -def includeShadowJar(TaskProvider includedShadowJarTask, String destinationDir) { - def opentracingFound = new AtomicBoolean() - project.processResources { - doFirst { +def includeShadowJar(TaskProvider includedShadowJarTask, Provider includedAgentDir, String agentDir) { + project.tasks.register("expandAgentShadowJar${agentDir.capitalize()}", Copy) { + + group = LifecycleBasePlugin.BUILD_GROUP + description = "Expand the included shadow jar into the agent jar under ${agentDir}" + + def opentracingFound = new AtomicBoolean() + doFirst("detect-open-tracing") { eachFile { // We seem unlikely to use this name somewhere else. if (it.path.contains("opentracing") && it.name.contains("Format\$Builtin")) { @@ -125,18 +136,18 @@ def includeShadowJar(TaskProvider includedShadowJarTask, String desti } } } - doLast { + doLast("fail-on-detected-opentracing") { if (opentracingFound.get()) { throw new GradleException("OpenTracing direct dependency found!") } } + into includedAgentDir.map { it.dir(agentDir) } from(zipTree(includedShadowJarTask.map { it.archiveFile })) { - into destinationDir rename '(^.*)\\.class$', '$1.classdata' // Rename LICENSE file since it clashes with license dir on non-case sensitive FSs (i.e. Mac) rename '^LICENSE$', 'LICENSE.renamed' - if (destinationDir == 'inst') { + if (agentDir == 'inst') { // byte-buddy now ships classes optimized for Java8+ under META-INF/versions/9 // since we target Java8+ we can promote these classes over the pre-Java8 ones duplicatesStrategy = DuplicatesStrategy.EXCLUDE @@ -147,9 +158,7 @@ def includeShadowJar(TaskProvider includedShadowJarTask, String desti } } } - } - project.tasks.named("processResources") { dependsOn includedShadowJarTask } @@ -158,22 +167,22 @@ def includeShadowJar(TaskProvider includedShadowJarTask, String desti } } -def includeSubprojShadowJar(Project includedProjectJar, String destinationDir) { +def includeSubprojShadowJar(Project includedProjectJar, Provider includedAgentDir, String destinationDir) { evaluationDependsOn(includedProjectJar.path) - includeShadowJar(includedProjectJar.tasks.named("shadowJar", ShadowJar), destinationDir) + includeShadowJar(includedProjectJar.tasks.named("shadowJar", ShadowJar), includedAgentDir, destinationDir) } -includeSubprojShadowJar(project(':dd-java-agent:instrumentation'), 'inst') -includeSubprojShadowJar(project(':dd-java-agent:agent-jmxfetch'), 'metrics') -includeSubprojShadowJar(project(':dd-java-agent:agent-profiling'), 'profiling') -includeSubprojShadowJar(project(':dd-java-agent:appsec'), 'appsec') -includeSubprojShadowJar(project(':dd-java-agent:agent-aiguard'), 'aiguard') -includeSubprojShadowJar(project(':dd-java-agent:agent-iast'), 'iast') -includeSubprojShadowJar(project(':dd-java-agent:agent-debugger'), 'debugger') -includeSubprojShadowJar(project(':dd-java-agent:agent-ci-visibility'), 'ci-visibility') -includeSubprojShadowJar(project(':dd-java-agent:agent-llmobs'), 'llm-obs') -includeSubprojShadowJar(project(':dd-java-agent:agent-logs-intake'), 'logs-intake') -includeSubprojShadowJar(project(':dd-java-agent:cws-tls'), 'cws-tls') +includeSubprojShadowJar(project(':dd-java-agent:instrumentation'), includedAgentDir, 'inst') +includeSubprojShadowJar(project(':dd-java-agent:agent-jmxfetch'), includedAgentDir, 'metrics') +includeSubprojShadowJar(project(':dd-java-agent:agent-profiling'), includedAgentDir, 'profiling') +includeSubprojShadowJar(project(':dd-java-agent:appsec'), includedAgentDir, 'appsec') +includeSubprojShadowJar(project(':dd-java-agent:agent-aiguard'), includedAgentDir, 'aiguard') +includeSubprojShadowJar(project(':dd-java-agent:agent-iast'), includedAgentDir, 'iast') +includeSubprojShadowJar(project(':dd-java-agent:agent-debugger'), includedAgentDir, 'debugger') +includeSubprojShadowJar(project(':dd-java-agent:agent-ci-visibility'), includedAgentDir, 'ci-visibility') +includeSubprojShadowJar(project(':dd-java-agent:agent-llmobs'), includedAgentDir, 'llm-obs') +includeSubprojShadowJar(project(':dd-java-agent:agent-logs-intake'), includedAgentDir, 'logs-intake') +includeSubprojShadowJar(project(':dd-java-agent:cws-tls'), includedAgentDir, 'cws-tls') def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) { configurations = [project.configurations.sharedShadowInclude] @@ -194,7 +203,7 @@ def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) { exclude(dependency('org.slf4j::')) } } -includeShadowJar(sharedShadowJar, 'shared') +includeShadowJar(sharedShadowJar, includedAgentDir, 'shared') // place the tracer in its own shadow jar separate to instrumentation def traceShadowJar = tasks.register('traceShadowJar', ShadowJar) { @@ -203,9 +212,12 @@ def traceShadowJar = tasks.register('traceShadowJar', ShadowJar) { archiveClassifier = 'trace' it.dependencies deps.excludeShared } -includeShadowJar(traceShadowJar, 'trace') +includeShadowJar(traceShadowJar, includedAgentDir, 'trace') tasks.named("shadowJar", ShadowJar) { + // Include AgentPreCheck compiled with Java 6. + from sourceSets.main_java6.output + generalShadowJarConfig(it) configurations = [project.configurations.shadowInclude] @@ -223,24 +235,35 @@ tasks.named("shadowJar", ShadowJar) { } } -tasks.register('generateAgentJarIndex', JavaExec) { - def indexName = 'dd-java-agent.index' - def contentDir = "${sourceSets.main.output.resourcesDir}" - def indexFile = "${contentDir}/${indexName}" +// temporary config to add slf4j-simple so we get logging while indexing +project.configurations.register('slf4j-simple') { + it.dependencies.add(project.dependencyFactory.create("org.slf4j:slf4j-simple:${libs.versions.slf4j.get()}")) +} + +def generateAgentJarIndex = tasks.register('generateAgentJarIndex', JavaExec) { + def destinationDir = project.layout.buildDirectory.dir("generated/${it.name}") - it.group = 'Build' + it.group = LifecycleBasePlugin.BUILD_GROUP it.description = "Generate dd-java-agent.index" - it.inputs.files(fileTree(contentDir).exclude(indexName)) - it.outputs.files(indexFile) it.mainClass = 'datadog.trace.bootstrap.AgentJarIndex$IndexGenerator' - it.classpath = project.configurations.shadowInclude - it.args = [contentDir] - dependsOn 'processResources' - dependsOn 'writeVersionNumberFile' + it.inputs.files(includedJarFileTree) + it.inputs.files(it.classpath) + it.outputs.dir(destinationDir) + it.classpath = objects.fileCollection().tap { + it.from(project.configurations.named("shadowInclude")) + it.from(project.configurations.named('slf4j-simple')) + } + // debuggable within gradle using: + // it.jvmArgs("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005") + it.argumentProviders.add(new CommandLineArgumentProvider() { + @Override + Iterable asArguments() { + return [includedAgentDir.get().asFile.path, destinationDir.get().asFile.path,] + } + }) } - -compileJava.dependsOn 'generateAgentJarIndex' +sourceSets.main.resources.srcDir(generateAgentJarIndex) subprojects { Project subProj -> // Don't need javadoc task run for internal projects. From 1876ddd6513c8ab088c98031ee16a11d3c3d43d1 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Thu, 9 Oct 2025 23:59:38 +0200 Subject: [PATCH 3/9] style: Access properties via `it` in Groovy DSL --- dd-java-agent/build.gradle | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index ea629fbfdc3..48459e1f685 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -121,7 +121,7 @@ def generalShadowJarConfig(ShadowJar shadowJarTask) { } } -def includeShadowJar(TaskProvider includedShadowJarTask, Provider includedAgentDir, String agentDir) { +def includeShadowJar(TaskProvider includedShadowJarTask, String agentDir, Provider includedAgentDir) { project.tasks.register("expandAgentShadowJar${agentDir.capitalize()}", Copy) { group = LifecycleBasePlugin.BUILD_GROUP @@ -167,31 +167,31 @@ def includeShadowJar(TaskProvider includedShadowJarTask, Provider includedAgentDir, String destinationDir) { +def includeSubprojShadowJar(Project includedProjectJar, String destinationDir, Provider includedAgentDir) { evaluationDependsOn(includedProjectJar.path) - includeShadowJar(includedProjectJar.tasks.named("shadowJar", ShadowJar), includedAgentDir, destinationDir) + includeShadowJar(includedProjectJar.tasks.named("shadowJar", ShadowJar), destinationDir, includedAgentDir) } -includeSubprojShadowJar(project(':dd-java-agent:instrumentation'), includedAgentDir, 'inst') -includeSubprojShadowJar(project(':dd-java-agent:agent-jmxfetch'), includedAgentDir, 'metrics') -includeSubprojShadowJar(project(':dd-java-agent:agent-profiling'), includedAgentDir, 'profiling') -includeSubprojShadowJar(project(':dd-java-agent:appsec'), includedAgentDir, 'appsec') -includeSubprojShadowJar(project(':dd-java-agent:agent-aiguard'), includedAgentDir, 'aiguard') -includeSubprojShadowJar(project(':dd-java-agent:agent-iast'), includedAgentDir, 'iast') -includeSubprojShadowJar(project(':dd-java-agent:agent-debugger'), includedAgentDir, 'debugger') -includeSubprojShadowJar(project(':dd-java-agent:agent-ci-visibility'), includedAgentDir, 'ci-visibility') -includeSubprojShadowJar(project(':dd-java-agent:agent-llmobs'), includedAgentDir, 'llm-obs') -includeSubprojShadowJar(project(':dd-java-agent:agent-logs-intake'), includedAgentDir, 'logs-intake') -includeSubprojShadowJar(project(':dd-java-agent:cws-tls'), includedAgentDir, 'cws-tls') +includeSubprojShadowJar(project(':dd-java-agent:instrumentation'), 'inst', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:agent-jmxfetch'), 'metrics', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:agent-profiling'), 'profiling', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:appsec'), 'appsec', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:agent-aiguard'), 'aiguard', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:agent-iast'), 'iast', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:agent-debugger'), 'debugger', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:agent-ci-visibility'), 'ci-visibility', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:agent-llmobs'), 'llm-obs', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:agent-logs-intake'), 'logs-intake', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:cws-tls'), 'cws-tls', includedAgentDir) def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) { - configurations = [project.configurations.sharedShadowInclude] + it.configurations = [project.configurations.sharedShadowInclude] // Put the jar in a different directory so we don't overwrite the normal shadow jar and // break caching, and also to not interfere with CI scripts that copy everything in the // libs directory it.destinationDirectory.set(project.layout.buildDirectory.dir("shared-lib")) // Add a classifier so we don't confuse the jar file with the normal shadow jar - archiveClassifier = 'shared' + it.archiveClassifier = 'shared' it.dependencies { exclude(project(':dd-java-agent:agent-bootstrap')) exclude(project(':dd-java-agent:agent-logging')) @@ -203,16 +203,16 @@ def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) { exclude(dependency('org.slf4j::')) } } -includeShadowJar(sharedShadowJar, includedAgentDir, 'shared') +includeShadowJar(sharedShadowJar, 'shared', includedAgentDir) // place the tracer in its own shadow jar separate to instrumentation def traceShadowJar = tasks.register('traceShadowJar', ShadowJar) { - configurations = [project.configurations.traceShadowInclude] + it.configurations = [project.configurations.traceShadowInclude] it.destinationDirectory.set(project.layout.buildDirectory.dir("trace-lib")) - archiveClassifier = 'trace' + it.archiveClassifier = 'trace' it.dependencies deps.excludeShared } -includeShadowJar(traceShadowJar, includedAgentDir, 'trace') +includeShadowJar(traceShadowJar, 'trace', includedAgentDir) tasks.named("shadowJar", ShadowJar) { // Include AgentPreCheck compiled with Java 6. From 3e19ebd5f53866cacf286e5be78e208ff09a49d9 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Mon, 13 Oct 2025 17:30:08 +0200 Subject: [PATCH 4/9] chore: Fix indexer warning logs For logs like ``` [main] WARN datadog.trace.bootstrap.AgentJarIndex - Detected duplicate content 'datadog.trace.civisibility.writer.ddintake.*' under 'trace', already seen in . Ensure your content is under a distinct directory. ``` --- .../java/datadog/trace/bootstrap/AgentJarIndex.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java index fa10b0ff8bf..f2e97007a2d 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java @@ -97,7 +97,6 @@ static class IndexGenerator extends SimpleFileVisitor { private Path prefixRoot; private int prefixId; - private Map prefixMappings = new HashMap<>(); IndexGenerator(Path resourcesDir) { @@ -142,14 +141,15 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { String entryKey = computeEntryKey(prefixRoot.relativize(file)); if (null != entryKey) { int existingPrefixId = prefixTrie.apply(entryKey); - if (-1 != existingPrefixId && prefixId != existingPrefixId) { + // warn if two subsections contain content under the same package prefix + // because we're then unable to redirect requests to the right submodule + // (ignore the two 'datadog.compiler' packages which allow duplication) + if (existingPrefixId > 0 && prefixId != existingPrefixId) { log.warn( "Detected duplicate content '{}' under '{}', already seen in {}. Ensure your content is under a distinct directory.", entryKey, resourcesDir.relativize(file).getName(0), // prefix - existingPrefixId == 0 - ? "" - : prefixMappings.get(existingPrefixId) // previous prefix + prefixMappings.get(existingPrefixId) // previous prefix ); } prefixTrie.put(entryKey, prefixId); From 20839fd2c128686a32ba9d17781775d3c520735d Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Mon, 13 Oct 2025 17:30:53 +0200 Subject: [PATCH 5/9] chore: Fix indexer warning log, due to duplicate license files For logs like ``` Detected duplicate content under 'META-INF.AL2.0'. Ensure your content is under a distinct directory. Detected duplicate content under 'META-INF.LGPL2.1'. Ensure your content is under a distinct directory. ``` --- dd-java-agent/cws-tls/build.gradle | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/cws-tls/build.gradle b/dd-java-agent/cws-tls/build.gradle index 68fcbd9dc6b..df9efdd2608 100644 --- a/dd-java-agent/cws-tls/build.gradle +++ b/dd-java-agent/cws-tls/build.gradle @@ -1,4 +1,5 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.apache.maven.model.License plugins { id 'com.gradleup.shadow' @@ -22,6 +23,10 @@ dependencies { tasks.named("shadowJar", ShadowJar) { dependencies deps.excludeShared - // exclude this since it's available in the instrumentation jar - exclude 'com/sun/jna/**/*' + + // exclude 'jna' this since it's available in the instrumentation jar + dependencies { + exclude(dependency("net.java.dev.jna:jna")) + exclude(dependency("net.java.dev.jna:jna-platform")) + } } From bdcaeeb2131c3722abf5bace25411c03d2be2f3d Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Mon, 13 Oct 2025 17:31:46 +0200 Subject: [PATCH 6/9] chore: Fix indexer warning log, due to duplicate dd-javac-plugin-client files For logs like ``` [main] WARN datadog.trace.bootstrap.AgentJarIndex - Detected duplicate content under 'datadog.compiler.utils.*'. Ensure your content is under a distinct directory. [main] WARN datadog.trace.bootstrap.AgentJarIndex - Detected duplicate content under 'datadog.compiler.annotations.*'. Ensure your content is under a distinct ``` --- gradle/dependencies.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index f4c8bd35b08..abae8406a78 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -45,6 +45,9 @@ final class CachedData { exclude(dependency('com.github.jnr::')) exclude(dependency('org.ow2.asm::')) + // javac plugin client + exclude(dependency('com.datadoghq:dd-javac-plugin-client')) + // moshi and its transitives exclude(dependency('com.squareup.moshi::')) From 9b7972ff913042e48ff84f4c475b3a252190597a Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Mon, 13 Oct 2025 17:34:00 +0200 Subject: [PATCH 7/9] fix: Use Sync task rather than Copy, which may leave deleted files in place --- dd-java-agent/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index 6335d9b5d60..1c83634896d 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -122,7 +122,7 @@ def generalShadowJarConfig(ShadowJar shadowJarTask) { } def includeShadowJar(TaskProvider includedShadowJarTask, String agentDir, Provider includedAgentDir) { - project.tasks.register("expandAgentShadowJar${agentDir.capitalize()}", Copy) { + project.tasks.register("expandAgentShadowJar${agentDir.capitalize()}", Sync) { group = LifecycleBasePlugin.BUILD_GROUP description = "Expand the included shadow jar into the agent jar under ${agentDir}" From 5dfca97c4c711ef74a4d7030c6a1e07a63814c94 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Mon, 13 Oct 2025 17:35:26 +0200 Subject: [PATCH 8/9] fix: Replace weak configuration using tasks.matching {} by proper filetree.builtBy call --- dd-java-agent/build.gradle | 55 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index 1c83634896d..6e9b2daf5e0 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -18,9 +18,8 @@ configurations { } def includedAgentDir = project.layout.buildDirectory.dir("generated/included") -def includedJarFileTree = fileTree(includedAgentDir) { - builtBy(project.tasks.withType(Copy).matching { it.name.startsWith("expandAgentShadowJar") }) -} +def includedJarFileTree = fileTree(includedAgentDir) + tasks.named("processResources") { dependsOn(includedJarFileTree) } @@ -121,13 +120,13 @@ def generalShadowJarConfig(ShadowJar shadowJarTask) { } } -def includeShadowJar(TaskProvider includedShadowJarTask, String agentDir, Provider includedAgentDir) { - project.tasks.register("expandAgentShadowJar${agentDir.capitalize()}", Sync) { - group = LifecycleBasePlugin.BUILD_GROUP - description = "Expand the included shadow jar into the agent jar under ${agentDir}" +def includeShadowJar(TaskProvider includedShadowJarTask, String agentDir, FileTree includedJarFileTree) { + def expandTask = project.tasks.register("expandAgentShadowJar${agentDir.capitalize()}", Sync) { + it.group = LifecycleBasePlugin.BUILD_GROUP + it.description = "Expand the included shadow jar into the agent jar under ${agentDir}" def opentracingFound = new AtomicBoolean() - doFirst("detect-open-tracing") { + it.doFirst("detect-open-tracing") { eachFile { // We seem unlikely to use this name somewhere else. if (it.path.contains("opentracing") && it.name.contains("Format\$Builtin")) { @@ -135,14 +134,14 @@ def includeShadowJar(TaskProvider includedShadowJarTask, String agent } } } - doLast("fail-on-detected-opentracing") { + it.doLast("fail-on-detected-opentracing") { if (opentracingFound.get()) { throw new GradleException("OpenTracing direct dependency found!") } } - into includedAgentDir.map { it.dir(agentDir) } - from(zipTree(includedShadowJarTask.map { it.archiveFile })) { + it.into providers.provider { new File(includedJarFileTree.dir, agentDir) } + it.from(zipTree(includedShadowJarTask.map { it.archiveFile })) { rename '(^.*)\\.class$', '$1.classdata' // Rename LICENSE file since it clashes with license dir on non-case sensitive FSs (i.e. Mac) rename '^LICENSE$', 'LICENSE.renamed' @@ -158,30 +157,32 @@ def includeShadowJar(TaskProvider includedShadowJarTask, String agent } } - dependsOn includedShadowJarTask + it.dependsOn includedShadowJarTask } + includedJarFileTree.builtBy(expandTask) + includedShadowJarTask.configure { generalShadowJarConfig(it as ShadowJar) } } -def includeSubprojShadowJar(Project includedProjectJar, String destinationDir, Provider includedAgentDir) { +def includeSubprojShadowJar(Project includedProjectJar, String destinationDir, FileTree includedJarFileTree) { evaluationDependsOn(includedProjectJar.path) - includeShadowJar(includedProjectJar.tasks.named("shadowJar", ShadowJar), destinationDir, includedAgentDir) + includeShadowJar(includedProjectJar.tasks.named("shadowJar", ShadowJar), destinationDir, includedJarFileTree) } -includeSubprojShadowJar(project(':dd-java-agent:instrumentation'), 'inst', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:agent-jmxfetch'), 'metrics', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:agent-profiling'), 'profiling', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:appsec'), 'appsec', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:agent-aiguard'), 'aiguard', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:agent-iast'), 'iast', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:agent-debugger'), 'debugger', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:agent-ci-visibility'), 'ci-visibility', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:agent-llmobs'), 'llm-obs', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:agent-logs-intake'), 'logs-intake', includedAgentDir) -includeSubprojShadowJar(project(':dd-java-agent:cws-tls'), 'cws-tls', includedAgentDir) +includeSubprojShadowJar(project(':dd-java-agent:instrumentation'), 'inst', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:agent-jmxfetch'), 'metrics', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:agent-profiling'), 'profiling', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:appsec'), 'appsec', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:agent-aiguard'), 'aiguard', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:agent-iast'), 'iast', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:agent-debugger'), 'debugger', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:agent-ci-visibility'), 'ci-visibility', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:agent-llmobs'), 'llm-obs', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:agent-logs-intake'), 'logs-intake', includedJarFileTree) +includeSubprojShadowJar(project(':dd-java-agent:cws-tls'), 'cws-tls', includedJarFileTree) def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) { it.configurations = [project.configurations.sharedShadowInclude] @@ -202,7 +203,7 @@ def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) { exclude(dependency('org.slf4j::')) } } -includeShadowJar(sharedShadowJar, 'shared', includedAgentDir) +includeShadowJar(sharedShadowJar, 'shared', includedJarFileTree) // place the tracer in its own shadow jar separate to instrumentation def traceShadowJar = tasks.register('traceShadowJar', ShadowJar) { @@ -211,7 +212,7 @@ def traceShadowJar = tasks.register('traceShadowJar', ShadowJar) { it.archiveClassifier = 'trace' it.dependencies deps.excludeShared } -includeShadowJar(traceShadowJar, 'trace', includedAgentDir) +includeShadowJar(traceShadowJar, 'trace', includedJarFileTree) tasks.named("shadowJar", ShadowJar) { // Include AgentPreCheck compiled with Java 6. From b59e05e66d03837d8222dcac522d2e8ea061665c Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Tue, 14 Oct 2025 11:05:23 +0200 Subject: [PATCH 9/9] style: Fix star import --- .../main/java/datadog/trace/bootstrap/AgentJarIndex.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java index f2e97007a2d..cedc773bbfb 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java @@ -13,7 +13,13 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.util.*; +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 java.util.jar.JarFile; import java.util.zip.ZipEntry; import org.slf4j.Logger;