Refactor javaagent buildscript
MODIFY
instrumentation/build.gradle.kts
instrumentation/build.gradle.kts
-import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-
-// this project will run in isolation under the agent's classloader
plugins {
- id("io.opentelemetry.instrumentation.javaagent-shadowing")
id("otel.java-conventions")
}
val instrumentationProjectTest = tasks.named("test")
-val instrumentationProjectDependencies = dependencies
subprojects {
val subProj = this
@@ -16,48 +11,9 @@ subprojects {
dependsOn(subProj.tasks.named("test"))
}
}
-
- plugins.withId("otel.javaagent-instrumentation") {
- instrumentationProjectDependencies.run {
- implementation(project(subProj.path))
- }
- }
-}
-
-dependencies {
- compileOnly(project(":instrumentation-api"))
- compileOnly(project(":javaagent-instrumentation-api"))
- implementation(project(":javaagent-tooling"))
- implementation(project(":javaagent-extension-api"))
-
- // this only exists to make Intellij happy since it doesn't (currently at least) understand our
- // inclusion of this artifact inside of :instrumentation-api
- compileOnly(project(":instrumentation-api-caching"))
-}
-
-configurations {
- // exclude bootstrap dependencies from shadowJar
- implementation {
- exclude("org.slf4j")
- exclude("io.opentelemetry", "opentelemetry-api")
- exclude("io.opentelemetry", "opentelemetry-api-metrics")
- exclude("io.opentelemetry", "opentelemetry-semconv")
- }
}
tasks {
- named<ShadowJar>("shadowJar") {
- duplicatesStrategy = DuplicatesStrategy.FAIL
-
- dependencies {
- //These classes are added to bootstrap classloader by javaagent module
- exclude(project(":javaagent-bootstrap"))
- exclude(project(":instrumentation-api"))
- exclude(project(":instrumentation-api-annotation-support"))
- exclude(project(":javaagent-instrumentation-api"))
- }
- }
-
register("listInstrumentations") {
group = "Help"
description = "List all available instrumentation modules"

MODIFY
javaagent-exporters/build.gradle.kts
javaagent-exporters/build.gradle.kts
plugins {
id("otel.java-conventions")
- id("io.opentelemetry.instrumentation.javaagent-shadowing")
}
dependencies {
@@ -19,11 +18,3 @@ dependencies {
// TODO(anuraaga): Move version to dependency management
implementation("io.grpc:grpc-netty-shaded:1.38.0")
}
-
-configurations {
- implementation {
- exclude("io.opentelemetry", "opentelemetry-api")
- exclude("io.opentelemetry", "opentelemetry-api-metrics")
- exclude("io.opentelemetry", "opentelemetry-semconv")
- }
-}

MODIFY
javaagent/build.gradle.kts
javaagent/build.gradle.kts
@@ -11,13 +11,33 @@ plugins {
}
description = "OpenTelemetry Javaagent"
-
group = "io.opentelemetry.javaagent"
+// this configuration collects libs that will be placed in the bootstrap classloader
val bootstrapLibs by configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
}
+// this configuration collects libs that will be placed in the agent classloader, isolated from the instrumented application code
+val javaagentLibs by configurations.creating {
+ isCanBeResolved = true
+ isCanBeConsumed = false
+}
+// this configuration collects just exporter libs (also placed in the agent classloader & isolated from the instrumented application)
+val exporterLibs by configurations.creating {
+ isCanBeResolved = true
+ isCanBeConsumed = false
+}
+
+// exclude dependencies that are to be placed in bootstrap from agent libs - they won't be added to inst/
+listOf(javaagentLibs, exporterLibs).forEach {
+ it.run {
+ exclude("org.slf4j")
+ exclude("io.opentelemetry", "opentelemetry-api")
+ exclude("io.opentelemetry", "opentelemetry-api-metrics")
+ exclude("io.opentelemetry", "opentelemetry-semconv")
+ }
+}
val licenseReportDependencies by configurations.creating {
extendsFrom(bootstrapLibs)
@@ -30,7 +50,13 @@ dependencies {
bootstrapLibs(project(":javaagent-instrumentation-api"))
bootstrapLibs("org.slf4j:slf4j-simple")
- // We only have compileOnly dependencies on these to make sure they don"t leak into POMs.
+ javaagentLibs(project(":javaagent-extension-api"))
+ javaagentLibs(project(":javaagent-tooling"))
+ javaagentLibs(project(":muzzle"))
+
+ exporterLibs(project(":javaagent-exporters"))
+
+ // We only have compileOnly dependencies on these to make sure they don't leak into POMs.
licenseReportDependencies("com.github.ben-manes.caffeine:caffeine") {
isTransitive = false
}
@@ -50,26 +76,20 @@ dependencies {
val javaagentDependencies = dependencies
-// collect all bootstrap instrumentation dependencies
+// collect all bootstrap and javaagent instrumentation dependencies
project(":instrumentation").subprojects {
val subProj = this
- plugins.withId("java") {
- if (subProj.name == "bootstrap") {
+ plugins.withId("otel.javaagent-bootstrap") {
javaagentDependencies.run {
add(bootstrapLibs.name, project(subProj.path))
}
}
- }
-}
-fun isolateSpec(projectsWithShadowJar: Collection<Project>): CopySpec = copySpec {
- from(projectsWithShadowJar.map { zipTree(it.tasks.getByName<ShadowJar>("shadowJar").archiveFile) }) {
- // important to keep prefix "inst" short, as it is prefixed to lots of strings in runtime mem
- into("inst")
- 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")
+ plugins.withId("otel.javaagent-instrumentation") {
+ javaagentDependencies.run {
+ add(javaagentLibs.name, project(subProj.path))
+ }
}
}
@@ -80,28 +100,42 @@ tasks {
}
}
- //Includes everything needed for OOTB experience
- val shadowJar by existing(ShadowJar::class) {
- archiveClassifier.set("all")
- val projectsWithShadowJar = listOf(project(":instrumentation"), project(":javaagent-exporters"))
- projectsWithShadowJar.forEach {
- dependsOn("${it.path}:shadowJar")
+ // lightShadow is the default classifier we publish so disable the default jar.
+ jar {
+ enabled = false
+ }
+
+ val relocateJavaagentLibs by registering(ShadowJar::class) {
+ configurations = listOf(javaagentLibs)
+
+ duplicatesStrategy = DuplicatesStrategy.FAIL
+
+ archiveFileName.set("javaagentLibs-relocated.jar")
+
+ // exclude bootstrap projects from javaagent libs - they won't be added to inst/
+ dependencies {
+ exclude(project(":instrumentation-api"))
+ exclude(project(":instrumentation-api-annotation-support"))
+ exclude(project(":javaagent-bootstrap"))
+ exclude(project(":javaagent-instrumentation-api"))
}
- with(isolateSpec(projectsWithShadowJar))
- duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
- //Includes instrumentations, but not exporters
- val lightShadow by registering(ShadowJar::class) {
- archiveClassifier.set("")
- dependsOn(":instrumentation:shadowJar")
- val projectsWithShadowJar = listOf(project(":instrumentation"))
- with(isolateSpec(projectsWithShadowJar))
+ val relocateExporterLibs by registering(ShadowJar::class) {
+ configurations = listOf(exporterLibs)
+
+ archiveFileName.set("exporterLibs-relocated.jar")
}
- withType<ShadowJar>().configureEach {
+ //Includes instrumentations, but not exporters
+ val lightShadow by registering(ShadowJar::class) {
configurations = listOf(bootstrapLibs)
+ dependsOn(relocateJavaagentLibs)
+ isolateClasses(relocateJavaagentLibs.get().outputs.files)
+
+ archiveClassifier.set("")
+
manifest {
attributes(
"Main-Class" to "io.opentelemetry.javaagent.OpenTelemetryAgent",
@@ -113,31 +147,41 @@ tasks {
}
}
- // lightShadow is the default classifier we publish so disable the default jar.
- jar {
- enabled = false
+ //Includes everything needed for OOTB experience
+ val shadowJar by existing(ShadowJar::class) {
+ configurations = listOf(bootstrapLibs)
+
+ dependsOn(relocateJavaagentLibs, relocateExporterLibs)
+ isolateClasses(relocateJavaagentLibs.get().outputs.files)
+ isolateClasses(relocateExporterLibs.get().outputs.files)
+
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+
+ archiveClassifier.set("all")
+
+ manifest {
+ attributes(lightShadow.get().manifest.attributes)
+ }
+ }
+
+ assemble {
+ dependsOn(lightShadow, shadowJar)
}
withType<Test>().configureEach {
+ dependsOn(shadowJar)
inputs.file(shadowJar.get().archiveFile)
jvmArgs("-Dotel.javaagent.debug=true")
doFirst {
// Defining here to allow jacoco to be first on the command line.
- jvmArgs("-javaagent:${shadowJar.get().archivePath}")
+ jvmArgs("-javaagent:${shadowJar.get().archiveFile.get().asFile}")
}
testLogging {
events("started")
}
-
- dependsOn(shadowJar)
- }
-
- named("assemble") {
- dependsOn(lightShadow)
- dependsOn(shadowJar)
}
val cleanLicenses by registering(Delete::class) {
@@ -171,3 +215,15 @@ licenseReport {
filters = arrayOf(LicenseBundleNormalizer("$projectDir/license-normalizer-bundle.json", true))
}
+
+fun CopySpec.isolateClasses(jars: Iterable<File>) {
+ jars.forEach {
+ from(zipTree(it)) {
+ // important to keep prefix "inst" short, as it is prefixed to lots of strings in runtime mem
+ into("inst")
+ 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")
+ }
+ }
+}

MODIFY
testing/agent-for-testing/build.gradle.kts
testing/agent-for-testing/build.gradle.kts
@@ -10,10 +10,12 @@ plugins {
description = "OpenTelemetry Javaagent for testing"
group = "io.opentelemetry.javaagent"
+// this configuration collects libs that will be placed in the bootstrap classloader
val bootstrapLibs by configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
}
+// this configuration collects libs that will be placed in the agent classloader, isolated from the instrumented application code
val javaagentLibs by configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
@@ -35,6 +37,7 @@ dependencies {
javaagentLibs(project(":testing:agent-exporter"))
javaagentLibs(project(":javaagent-extension-api"))
javaagentLibs(project(":javaagent-tooling"))
+ javaagentLibs(project(":muzzle"))
// Include instrumentations instrumenting core JDK classes tp ensure interoperability with other instrumentation
javaagentLibs(project(":instrumentation:executors:javaagent"))
@@ -59,14 +62,12 @@ val javaagentDependencies = dependencies
project(":instrumentation").subprojects {
val subProj = this
- plugins.withId("java") {
- if (subProj.name == "bootstrap") {
+ plugins.withId("otel.javaagent-bootstrap") {
javaagentDependencies.run {
add(bootstrapLibs.name, project(subProj.path))
}
}
}
-}
tasks {
jar {
@@ -87,25 +88,11 @@ tasks {
}
}
- fun isolateAgentClasses (jars: Iterable<File>): CopySpec {
- return copySpec {
- jars.forEach {
- from(zipTree(it)) {
- // important to keep prefix "inst" short, as it is prefixed to lots of strings in runtime mem
- into("inst")
- 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")
- }
- }
- }
- }
-
val shadowJar by existing(ShadowJar::class) {
- dependsOn(relocateJavaagentLibs)
-
configurations = listOf(bootstrapLibs)
- with(isolateAgentClasses(relocateJavaagentLibs.get().outputs.files))
+
+ dependsOn(relocateJavaagentLibs)
+ isolateClasses(relocateJavaagentLibs.get().outputs.files)
archiveClassifier.set("")
@@ -122,12 +109,11 @@ tasks {
afterEvaluate {
withType<Test>().configureEach {
+ dependsOn(shadowJar)
inputs.file(shadowJar.get().archiveFile)
jvmArgs("-Dotel.javaagent.debug=true")
jvmArgs("-javaagent:${shadowJar.get().archiveFile.get().asFile.absolutePath}")
-
- dependsOn(shadowJar)
}
}
@@ -140,3 +126,15 @@ tasks {
}
}
}
+
+fun CopySpec.isolateClasses(jars: Iterable<File>) {
+ jars.forEach {
+ from(zipTree(it)) {
+ // important to keep prefix "inst" short, as it is prefixed to lots of strings in runtime mem
+ into("inst")
+ 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")
+ }
+ }
+}

Refactor javaagent buildscript (#3682)