Skip to content

Comments

Rewrite build-time instrumentation as compilation post-processor (using Gradle lazy APIs)#9475

Merged
bric3 merged 19 commits intomasterfrom
bdu/lazy-instrument-plugin
Jan 22, 2026
Merged

Rewrite build-time instrumentation as compilation post-processor (using Gradle lazy APIs)#9475
bric3 merged 19 commits intomasterfrom
bdu/lazy-instrument-plugin

Conversation

@bric3
Copy link
Contributor

@bric3 bric3 commented Sep 5, 2025

What Does This Do

Disclaimer: This only touches the build part (i.e. Gradle).

The current InstrumentPlugin uses eager gradle API and inject a task to run after the compile task, and in doing so, exchanging their output. This approach is uncommon, create as many instrument task as they are compile tasks, and make it harder to predict compilation output.

This PR rethink the approach to a compilation post-processor. So instead of injecting a task in the gradle task graph, each compile task have a post-processor action.

Also, the plugin is renamed to dd-trace-java.build-time-instrumentation for better expressing intent. (Related configuration points are also renamed),

In doing so, it makes the intent of this plug-in easier to grasp.

On another note, the single Groovy file has been spread to distinct types.

Motivation

  • Good usage of Gradle API.
  • Make it lazy.
  • Rename better conveys what this plugins do: instrument at build time. The name dd-trace-java.build-time-instrumentation was chosen to align dd-trace-java.call-site-instrumentation. Also, searching these string will bring more focused results.

Avoid Gradle eager API, avoid messing with compile tasks avoid,

Helps going toward convention plug-ins.

Additional Notes

Related PRs

Related to

Blocked by

What does this plugin

ℹ️ italic word indicate Gradle linguo.

  • Registers an extension under the buildTimeInstrumentation name (previously instrument)
  • Registers a configuration (to declare dependencies) under the buildTimeInstrumentationPlugin name (previously instrumentPluginClasspath)
  • Applies to Java, Kotlin, Scala, and Groovy main sources
  • Appends a post-processing doLast action to each matching compile task

Currently, this build time instrumentation is applied on instrumentation modules :

  • datadog.trace.agent.tooling.muzzle.MuzzleGradlePlugin : Creates muzzle-references at compile time for classes extending InstrumenterModule. This generates metadata about instrumentation compatibility.
  • datadog.trace.agent.tooling.bytebuddy.NewTaskForGradlePlugin : Used for datadog.trace.instrumentation.java.concurrent.WrapRunnableAsNewTaskInstrumentation instrumentation, to replace datadog.trace.bootstrap.instrumentation.java.concurrent.NewTaskForPlaceholder#newTaskFor by java.util.concurrent.AbstractExecutorService#newTaskFor(java.lang.Runnable, T) (which is protected and not accessible during compilation).
  • datadog.trace.agent.tooling.bytebuddy.reqctx.RewriteRequestContextAdvicePlugin : Transforms classes annotated with @RequiresRequestContext to inject request context handling.

...and in the otel agent

  • datadog.opentelemetry.tooling.shim.OtelShimGradlePlugin: Injects Datadog's OpenTelemetry shim into specific OpenTelemetry API classes like DefaultOpenTelemetry, GlobalOpenTelemetry$ObfuscatedOpenTelemetry, ThreadLocalContextStorage, etc. This allows intercepting OpenTelemetry API calls at build-time.

Additional checks

As in #9514 I also checked the produced jars between master and this branch using my jardiff tool I already mentioned there (I crafted it for validating #9514, other tools didn't work or didn't perform what was needed).

Note

🔗 https://github.com/bric3/jardiff

Shortcomings:

  • not released anywhere, one has to build it - a possible idea would be to integrate this in a gradle plugin to make the diff easier to read.
  • needs JDK 25

Ran the following command on this branch (folder dd-trace-java-copy-2), and on master (folder dd-trace-java-copy-1).

$ ./gradlew :dd-java-agent:shadowJar

The following assumes that jardiff is actually this command java -jar jardiff/build/libs/jardiff-0.1.0-SNAPSHOT.jar, and assuming this is a Java 25. Also, while not strictly necessary the following snippets use delta to render the diff.

The following difference have been checked:

  • dd-java-agent

    jardiff ../dd-trace-java-copy-{1,2}/dd-java-agent/build/libs/dd-java-agent-1.58.0-SNAPSHOT.jar \
      -c classdata \
      --exclude "**/*.yaml,**/*.MF,**/*.version,*.version,*.txt" \
      | delta --syntax-theme=GitHub --light 

    No differences:

    image

@bric3 bric3 requested review from a team as code owners September 5, 2025 12:50
@bric3 bric3 requested a review from amarziali September 5, 2025 12:50
@github-actions
Copy link
Contributor

github-actions bot commented Sep 5, 2025

Hi! 👋 Thanks for your pull request! 🎉

To help us review it, please make sure to:

  • Add at least one type, and one component or instrumentation label to the pull request

If you need help, please check our contributing guidelines.

@bric3 bric3 marked this pull request as draft September 5, 2025 12:50
@bric3 bric3 added tag: no release notes Changes to exclude from release notes comp: tooling Build & Tooling labels Sep 5, 2025
Copy link
Contributor

@AlexeyKuznetsov-DD AlexeyKuznetsov-DD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, what would be the benefit? Faster build?

@bric3 bric3 force-pushed the bdu/lazy-instrument-plugin branch from 248b4eb to 3094d50 Compare September 5, 2025 17:02
@bric3
Copy link
Contributor Author

bric3 commented Sep 10, 2025

Status report

The new approach of the instrument post-processing works in almost all simple projects, e.g.

Important

The diff was made by this custom tool https://github.com/bric3/jardiff

:dd-java-agent:instrumentation:iast-instrumenter
$ mise exec java@corretto-24 -- jardiff /Users/brice.dutheil/go/src/github.com/DataDog/dd-trace-java{-copy-1,}/dd-java-agent/instrumentation/iast-instrumenter/build/classes/java/main                      17:01:29
Comparing:
* /Users/brice.dutheil/go/src/github.com/DataDog/dd-trace-java-copy-1/dd-java-agent/instrumentation/iast-instrumenter/build/classes/java/main
* /Users/brice.dutheil/go/src/github.com/DataDog/dd-trace-java/dd-java-agent/instrumentation/iast-instrumenter/build/classes/java/main

✔️ META-INF/services/datadog.trace.agent.tooling.InstrumenterModule
✔️ datadog/trace/instrumentation/iastinstrumenter/IastExclusionTrie.class
✔️ datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation$IastMatchers.class
✔️ datadog/trace/instrumentation/iastinstrumenter/IastHardcodedSecretListener$ReportSecretConsumer.class
✔️ datadog/trace/instrumentation/iastinstrumenter/SourceMapperImpl.class
✔️ datadog/trace/instrumentation/iastinstrumenter/StratumListener.class
✔️ datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation.class
✔️ datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation$Muzzle.class
✔️ datadog/trace/instrumentation/iastinstrumenter/IastHardcodedSecretListener.class
✔️ datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation$IastCallSiteSupplier.class
✔️ datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation$IastMatchers$1.class
✔️ datadog/trace/instrumentation/iastinstrumenter/telemetry/TelemetryCallSiteSupplier$IteratorAdapter.class
✔️ datadog/trace/instrumentation/iastinstrumenter/telemetry/TelemetryCallSiteSupplier$1.class
✔️ datadog/trace/instrumentation/iastinstrumenter/telemetry/TelemetryCallSiteSupplier.class
✔️ datadog/trace/instrumentation/iastinstrumenter/service/CallSitesLoader.class
:dd-java-agent:instrumentation:mule-4
$  mise exec java@corretto-24 -- jardiff /Users/brice.dutheil/go/src/github.com/DataDog/dd-trace-java{-copy-1,}/dd-java-agent/instrumentation/mule-4/build/classes/java/main                                 17:26:08
Comparing:
* /Users/brice.dutheil/go/src/github.com/DataDog/dd-trace-java-copy-1/dd-java-agent/instrumentation/mule-4/build/classes/java/main
* /Users/brice.dutheil/go/src/github.com/DataDog/dd-trace-java/dd-java-agent/instrumentation/mule-4/build/classes/java/main

✔️ META-INF/services/datadog.trace.agent.tooling.InstrumenterModule
✔️ datadog/trace/instrumentation/mule4/EventTracerInstrumentation.class
✔️ datadog/trace/instrumentation/mule4/ComponentMessageProcessorInstrumentation$ProcessAdvice.class
✔️ datadog/trace/instrumentation/mule4/AbstractMuleInstrumentation.class
✔️ datadog/trace/instrumentation/mule4/EventTracerInstrumentation$Muzzle.class
✔️ datadog/trace/instrumentation/mule4/SpanState.class
✔️ datadog/trace/instrumentation/mule4/ComponentMessageProcessorInstrumentation$Muzzle.class
✔️ datadog/trace/instrumentation/mule4/JpmsMuleInstrumentation$Muzzle.class
✔️ datadog/trace/instrumentation/mule4/ExecutionInitialSpanInfoInstrumentation$StoreComponentAdvice.class
✔️ datadog/trace/instrumentation/mule4/EventContextInstrumentation$Muzzle.class
✔️ datadog/trace/instrumentation/mule4/ComponentMessageProcessorInstrumentation.class
✔️ datadog/trace/instrumentation/mule4/EventContextCreationAdvice.class
✔️ datadog/trace/instrumentation/mule4/NoopMuleSpan.class
✔️ datadog/trace/instrumentation/mule4/DDEventTracer.class
✔️ datadog/trace/instrumentation/mule4/ExecutionInitialSpanInfoInstrumentation.class
✔️ datadog/trace/instrumentation/mule4/MuleDecorator.class
✔️ datadog/trace/instrumentation/mule4/EventTracerInstrumentation$SwapCoreTracerAdvice.class
✔️ datadog/trace/instrumentation/mule4/JpmsMuleInstrumentation.class
✔️ datadog/trace/instrumentation/mule4/ExecutionInitialSpanInfoInstrumentation$Muzzle.class
✔️ datadog/trace/instrumentation/mule4/EventContextInstrumentation.class
:dd-java-agent:instrumentation:play.2.4
:dd-java-agent:agent-otel:otel-bootstrap

jardiff on otel-bootstrap

Etc.

However, :dd-java-agent:instrumentation:jetty-9 is oddly configured and must be

mise exec java@corretto-24 -- jardiff /Users/brice.dutheil/go/src/github.com/DataDog/dd-trace-java{-copy-1,}/dd-java-agent/instrumentation/jetty-9/build/classes/java/main

And as such not all classes are available when post processing kicks in. Possibly this project needs a heavier setup refactoring.

Few other projects with non-standard configurations to fix

  • :dd-java-agent:instrumentation:play.2.4
  • :dd-java-agent:instrumentation:play.2.6

Executing a simple ./gradlew help :

~5K less created tasks during configuration
image

https://scans.gradle.com/s/jqasddzbs75ve/performance/configuration#summary-tasks-created-during-configuration

Note

~40k tasks created is still way too much and impacts the build performance considerably

versus master, with ~45k task created during configuration
image

https://scans.gradle.com/s/vg7nn7oitpx5k/performance/configuration#summary-tasks-created-during-configuration

@AlexeyKuznetsov-DD
Copy link
Contributor

AlexeyKuznetsov-DD commented Dec 17, 2025

Just a minor comment. Probably it make sense to refactor this plugin to Kotlin.
I did some naive 1-1 migration here
It is not working properly, so I will close my PR as you are working on it and have more context.

The current implementation was using bad practices in various ways,
`project.afterEvaluate`, task creation, explicit `dependsOn`

# Conflicts:
#	buildSrc/src/main/groovy/InstrumentPlugin.groovy
#	buildSrc/src/main/groovy/MuzzlePlugin.groovy
#	dd-java-agent/instrumentation/jetty-9/build.gradle

# Conflicts:
#	buildSrc/src/main/groovy/InstrumentPlugin.groovy
#	buildSrc/src/test/groovy/InstrumentPluginTest.groovy
#	dd-java-agent/instrumentation/play/play-2.4/build.gradle
#	dd-java-agent/instrumentation/play/play-2.6/build.gradle
@bric3 bric3 changed the title Rewrite InstrumentPlugin as compilation post-processor and lazy Rewrite build-time instrumentation as compilation post-processor (using Gradle lazy APIs) Jan 6, 2026
@bric3 bric3 force-pushed the bdu/lazy-instrument-plugin branch from 83aa2a4 to 5311fd3 Compare January 6, 2026 12:53
@bric3 bric3 marked this pull request as ready for review January 6, 2026 15:43
@bric3 bric3 requested review from a team as code owners January 6, 2026 15:43
@bric3 bric3 requested review from AlexeyKuznetsov-DD, claponcet, manuel-alvarez-alvarez and mcculls and removed request for a team January 6, 2026 15:43
Copy link
Contributor

@AlexeyKuznetsov-DD AlexeyKuznetsov-DD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, left minor notes and questions.

it.dependencies.add(subProj.dependencies.project(path: ':dd-java-agent:agent-tooling', configuration: 'instrumentPluginClasspath'))
it.dependencies.add(subProj.dependencies.project(
path: ':dd-java-agent:agent-tooling',
configuration: 'buildTimeInstrumentationPlugin'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: Maybe I'll rename this configuration to avoid confusion with the instrumentation plugin.

Something like

        configuration: 'buildTimeInstrumentationPlugin'
        configuration: 'buildTimeInstrumentationToolingPlugins'

import org.gradle.api.artifacts.Configuration
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.compile.AbstractCompile
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see these new imports used in the code below...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right thanks, it's something that I missed during the multiple rebase. I started this during the summer 2025.

Copy link
Contributor

@mcculls mcculls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of clarifying questions - otherwise looks good

@bric3 bric3 removed the tag: needs investigation Issues needing investigations label Jan 21, 2026
…gins

Also, use proper configuration factory methods for these configs
@bric3 bric3 force-pushed the bdu/lazy-instrument-plugin branch from 13e2583 to 8abc7a0 Compare January 21, 2026 20:38
@bric3 bric3 merged commit ea99945 into master Jan 22, 2026
568 checks passed
@bric3 bric3 deleted the bdu/lazy-instrument-plugin branch January 22, 2026 06:50
@github-actions github-actions bot added this to the 1.59.0 milestone Jan 22, 2026
jandro996 pushed a commit that referenced this pull request Jan 22, 2026
…ng Gradle lazy APIs) (#9475)

* chore(build): Rewrite InstrumentPlugin as compile post processing action

The current implementation was using bad practices in various ways,
`project.afterEvaluate`, task creation, explicit `dependsOn`

# Conflicts:
#	buildSrc/src/main/groovy/InstrumentPlugin.groovy
#	buildSrc/src/main/groovy/MuzzlePlugin.groovy
#	dd-java-agent/instrumentation/jetty-9/build.gradle

# Conflicts:
#	buildSrc/src/main/groovy/InstrumentPlugin.groovy
#	buildSrc/src/test/groovy/InstrumentPluginTest.groovy
#	dd-java-agent/instrumentation/play/play-2.4/build.gradle
#	dd-java-agent/instrumentation/play/play-2.6/build.gradle

* chore(build): replace dependency on instrument task to source cet output

* chore: Properly react on instrumentPluginClasspath configuration

* chore: Rework additional classpath and instrumenter classpath contributions to instrument plugin

* fix: Remove eager whenObjectAdded in InstrumentPlugin

* fix: Fix bad java version comparison

* chore: Rework INSTRUMENT_PLUGIN_CLASSPATH_CONFIGURATION registration and configuration

* chore: Remove CallSiteInstrumentationPlugin dependencies on instrumentJava

Since instrument plugin do post-processing within compileTask,
it not anymore required to depends on instrumentation tasks
(as they don't exist anymore)

* chore: Removes useless afterEvaluate for forbiddenApi tasks

* chore: Split single plugin file to separate types

* chore: Now instrumentPluginClasspath configuration is already registered

* fix: Incorrect comparison

* style: Make spotless happy

* chore: Renames plugin to BuildTimeInstrumentationPlugin

* chore: PR Review

* chore: Rename build-time-instrumentation configuration of tooling plugins

Also, use proper configuration factory methods for these configs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: tooling Build & Tooling tag: no release notes Changes to exclude from release notes type: enhancement Enhancements and improvements type: refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants