This module provides two Gradle plugins for the BTrace extension ecosystem:
- BTrace Extension Plugin (
io.btrace.extension) - Build and package BTrace extensions - BTrace Fat Agent Plugin (
io.btrace.fat-agent) - Build self-contained fat agent JARs with embedded extensions
Build and package BTrace extensions with sane defaults.
- Scans implementation bytecode to infer minimal required permissions
- Writes extension metadata into the API JAR manifest
- Produces three artifacts: API JAR, Impl JAR (shadowed), and distributable ZIP
- Uses a single authored source tree while preserving the API/impl runtime artifact split
- Auto-registers the
@ExternalTypeannotation processor on the main source set (generates typed, lazy-resolution adapters for application types — seedocs/architecture/provided-style-extensions.md)
plugins {
id 'io.btrace.extension' version "${btraceVersion}"
}
repositories {
mavenCentral()
mavenLocal() // if you published locally for testing
}src/main/java,src/main/resources: Single authored source tree- The plugin derives the exported API closure from declared services and keeps the same runtime artifact split (
*-api.jar+*-impl.jar)
btraceExtension {
id = "com.example.myext" // required: globally unique extension ID
name = "My Extension" // optional
description = "Does things" // optional
// Optional: omit to auto-detect from @ServiceDescriptor annotations
services = [
"com.example.myext.api.MyService"
]
additionalExports = [
// optional extra API types to include in the exported API set
]
excludedExports = [
// optional exclusions from the computed API export set
]
requiresExtensions = [
// other extension IDs if you depend on them
]
shadedPackages = [
// from : to (relocations applied to impl JAR)
"com.example.dep" : "com.example.myext.shaded.dep"
]
// Permissions
scanPermissions = true // default: scan impl JAR + classpath
requiredPermissions = [
// "NETWORK", "FILE_READ", "FILE_WRITE", "THREADS", "EXEC", "NATIVE",
// "REFLECTION", "CLASSLOADER", "SYSTEM_PROPS", "THREAD_INFO", "MEMORY_INFO", "JFR_EVENTS"
]
// Lints
apiCtorSeverity = 'error' // 'off' | 'warn' | 'error' (default: 'error')
}- API JAR:
build/libs/<name>-<version>-api.jarwith manifest entries:BTrace-Extension-Id,BTrace-Extension-Services,BTrace-Extension-Permissions, etc.
- Impl JAR:
build/libs/<name>-<version>-impl.jar(shadowed, minimized) - Distribution ZIP:
build/distributions/<name>-<version>-extension.zip
buildApiJar: Builds the API JAR and writes extension metadata into the manifestshadowJar: Builds the impl JAR from the implementation portion of the extension with relocationspackageExtension: Bundles API + Impl into a ZIP
Build self-contained fat agent JARs with embedded extensions for single-JAR deployment scenarios (Spark, Hadoop, Kubernetes, etc.).
- Embeds extensions directly into the agent JAR
- Auto-discovers extension projects in multi-project builds
- Supports project references, Maven coordinates, and local files
- API classes as
.classfiles (bootstrap), impl as.classdata(runtime loaded) - Integrates with ShadowJar for package relocation
plugins {
id 'io.btrace.fat-agent' version "${btraceVersion}"
}btraceFatAgent {
// Output configuration
baseName = 'btrace-agent-fat' // default
outputDir = file('build/libs') // default
// Extension sources (multiple can be combined)
embedExtensions {
// Project references (requires multi-project build)
project(':btrace-spark')
projects(':btrace-metrics', ':btrace-utils')
// Maven coordinates
maven('io.btrace:btrace-kafka-extension:2.3.0')
maven(group: 'io.btrace', name: 'btrace-flink-extension', version: '2.3.0')
// Local extension ZIPs or directories
file('/path/to/extension.zip')
files('/path/to/extensions/*.zip')
}
// Bundled probes (optional)
bundledProbes {
from 'src/probes/compiled' // pre-compiled .class files
fromSource 'src/probes/java' // Java sources (compiled by plugin)
include 'SparkJobTracer', 'SparkStageTracer' // specific probes only
}
// Manifest customization
manifest {
attributes(['Custom-Attr': 'value'])
}
// Package relocations (requires ShadowJar on classpath)
relocate 'org.jctools', 'io.btrace.libs.agent.org.jctools'
relocate 'org.objectweb.asm', 'io.btrace.libs.org.objectweb.asm'
// Auto-discovery options
autoDiscover = true // find extensions with btrace.extension plugin
filterProperty = 'embedExtensions' // use -PembedExtensions=ext1,ext2 to filter
}Extensions can declare an ExtensionConfigurator class that the agent calls at
startup to decide which bundled probes to activate automatically — without the
operator passing probes= on the command line.
Extension extension.properties:
id=btrace-spark
probes=SparkJobTracer,SparkStageTracer,SparkExecutorTracer
configurator=org.example.spark.SparkConfiguratorConfigurator class (in the extension's impl artifact):
public final class SparkConfigurator implements ExtensionConfigurator {
@Override
public ProbeConfiguration configure(RuntimeEnvironment env, Map<String, String> args) {
ProbeConfiguration config = new ProbeConfiguration();
if (env.hasClass("org.apache.spark.SparkContext")) {
config.enable("SparkJobTracer", "SparkStageTracer");
} else if (env.hasClass("org.apache.spark.executor.Executor")) {
config.enable("SparkExecutorTracer");
}
config.setOutput(args.getOrDefault("output", "jfr"));
return config;
}
}Operator usage — attach the fat agent; no probes= needed:
java -javaagent:my-btrace-agent-fat.jar MyAppIf probes= is supplied by the operator it takes priority and the configurator
is skipped entirely:
java -javaagent:my-btrace-agent-fat.jar=probes=SparkJobTracer MyAppSee BTraceExtensionDevelopmentGuide.md for the full configurator API reference.
When autoDiscover = true, the plugin automatically finds all subprojects with the io.btrace.extension plugin applied:
btraceFatAgent {
autoDiscover = true
filterProperty = 'embedExtensions'
}Build with specific extensions:
# Embed all discovered extensions
./gradlew fatAgentJar
# Embed only specific extensions
./gradlew fatAgentJar -PembedExtensions=btrace-metrics,btrace-statsdFor projects outside the BTrace monorepo:
plugins {
id 'io.btrace.fat-agent' version '2.3.0'
}
btraceFatAgent {
baseName = 'my-custom-agent'
// Reference base BTrace JAR (required for standalone builds)
agentJarTask = 'btraceJar' // or provide path
embedExtensions {
maven('io.btrace:btrace-metrics:2.3.0')
maven('io.btrace:btrace-statsd:2.3.0')
file('libs/my-custom-extension.zip')
}
}stageExtensions: Resolves and stages extension content (API as.class, impl as.classdata)stageProbes: Stages bundled probes toMETA-INF/btrace-probes/fatAgentJar: Creates the fat agent JAR with all embedded content
The fat agent JAR contains:
btrace-agent-fat.jar
├── META-INF/
│ ├── MANIFEST.MF
│ │ ├── Premain-Class: io.btrace.agent.Main
│ │ ├── Agent-Class: io.btrace.agent.Main
│ │ ├── Boot-Class-Path: btrace-agent-fat.jar
│ │ └── BTrace-Embedded-Extensions: ext1,ext2,ext3
│ └── btrace-extensions/
│ ├── ext1/
│ │ └── extension.properties
│ ├── ext2/
│ │ └── extension.properties
│ └── ext3/
│ └── extension.properties
├── org/openjdk/btrace/... # Agent + boot classes
├── org/example/ext/api/... # Extension API classes (.class)
└── org/example/ext/impl/... # Extension impl classes (.classdata)
# Attach to running JVM
java -javaagent:/path/to/btrace-agent-fat.jar <your-app>
# With options
java -javaagent:/path/to/btrace-agent-fat.jar=debug=true,port=2021 <your-app>The embedded extensions are automatically discovered and loaded at agent startup.
./gradlew :btrace-gradle-plugin:publishToMavenLocalThen in your project, add mavenLocal() to repositories or pluginManagement.
# Build fat agent with all extensions
./gradlew :btrace-dist:fatAgentJar
# Build with specific extensions only
./gradlew :btrace-dist:fatAgentJar -PembedExtensions=btrace-metrics
# Verify output
jar -tf btrace-dist/build/resources/main/v*/libs/btrace-agent-fat.jar | grep btrace-extensions- Keep API small and stable; only types used by BTrace scripts belong in API
- Put all runtime dependencies into
impland shade them - If permission scanning is too conservative, add
requiredPermissionsexplicitly
- Use auto-discovery for monorepo setups
- Use Maven coordinates for external/published extensions
- Test the fat agent in isolation before production deployment
- Package relocations help avoid classpath conflicts in target JVMs
For Maven users, a Maven plugin is also available to build fat agent JARs.
<build>
<plugins>
<plugin>
<groupId>io.btrace</groupId>
<artifactId>btrace-maven-plugin</artifactId>
<version>${btrace.version}</version>
<executions>
<execution>
<goals>
<goal>fat-agent</goal>
</goals>
</execution>
</executions>
<configuration>
<outputName>my-btrace-agent</outputName>
<extensions>
<extension>io.btrace:btrace-metrics:${btrace.version}</extension>
<extension>io.btrace:btrace-statsd:${btrace.version}</extension>
</extensions>
</configuration>
</plugin>
</plugins>
</build>| Parameter | Default | Description |
|---|---|---|
btraceVersion |
Plugin version | BTrace version to use for base agent/boot JARs |
extensions |
(none) | List of extension coordinates (groupId:artifactId:version) |
outputName |
btrace-agent-fat |
Output file name (without .jar) |
outputDirectory |
${project.build.directory} |
Output directory |
skip |
false |
Skip execution |
mvn packageThe fat agent JAR is created at target/${outputName}.jar.
See the main README and Getting Started Guide for usage instructions.