Improving debuggability by requestingName
This change adds a requestingName field to help in diagnosing the source of any injection failures.
Usually, the requestingName is the simple class name of the Instrumenter; however, this change doesn't propagate the Instumeneter named through FieldBackedProvider.
MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/HelperInjector.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/HelperInjector.java
@@ -33,6 +33,8 @@ public class HelperInjector implements Transformer {
private static final ClassLoader BOOTSTRAP_CLASSLOADER_PLACEHOLDER =
new SecureClassLoader(null) {};
+ private final String requestingName;
+
private final Set<String> helperClassNames;
private final Map<String, byte[]> dynamicTypeMap = new LinkedHashMap<>();
@@ -48,21 +50,26 @@ public class HelperInjector implements Transformer {
* provided. This is important if there is interdependency between helper classes that
* requires them to be injected in a specific order.
*/
- public HelperInjector(final String... helperClassNames) {
+ public HelperInjector(final String requestingName, final String... helperClassNames) {
+ this.requestingName = requestingName;
+
this.helperClassNames = new LinkedHashSet<>(Arrays.asList(helperClassNames));
}
- public HelperInjector(final Map<String, byte[]> helperMap) {
+ public HelperInjector(final String requestingName, final Map<String, byte[]> helperMap) {
+ this.requestingName = requestingName;
+
helperClassNames = helperMap.keySet();
dynamicTypeMap.putAll(helperMap);
}
- public static HelperInjector forDynamicTypes(final Collection<DynamicType.Unloaded<?>> helpers) {
+ public static HelperInjector forDynamicTypes(
+ final String requestingName, final Collection<DynamicType.Unloaded<?>> helpers) {
final Map<String, byte[]> bytes = new HashMap<>(helpers.size());
for (final DynamicType.Unloaded<?> helper : helpers) {
bytes.put(helper.getTypeDescription().getName(), helper.getBytes());
}
- return new HelperInjector(bytes);
+ return new HelperInjector(requestingName, bytes);
}
private Map<String, byte[]> getHelperMap() throws IOException {
@@ -125,8 +132,9 @@ public class HelperInjector implements Transformer {
: classLoader.getClass().getName();
log.error(
- "Error preparing helpers for {}. Failed to inject helper classes into instance {} of type {}",
+ "Error preparing helpers while processing {} for {}. Failed to inject helper classes into instance {} of type {}",
typeDescription,
+ requestingName,
classLoader,
classLoaderType,
e);

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java
@@ -106,7 +106,9 @@ public interface Instrumenter {
AgentBuilder.Identified.Extendable agentBuilder) {
final String[] helperClassNames = helperClassNames();
if (helperClassNames.length > 0) {
- agentBuilder = agentBuilder.transform(new HelperInjector(helperClassNames));
+ agentBuilder =
+ agentBuilder.transform(
+ new HelperInjector(this.getClass().getSimpleName(), helperClassNames));
}
return agentBuilder;
}

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/context/FieldBackedProvider.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/context/FieldBackedProvider.java
@@ -322,8 +322,10 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
/** Get transformer that forces helper injection onto bootstrap classloader. */
private AgentBuilder.Transformer bootstrapHelperInjector(
final Collection<DynamicType.Unloaded<?>> helpers) {
+ // TODO: Better to pass through the context of the Instrumenter
return new AgentBuilder.Transformer() {
- final HelperInjector injector = HelperInjector.forDynamicTypes(helpers);
+ final HelperInjector injector =
+ HelperInjector.forDynamicTypes(this.getClass().getSimpleName(), helpers);
@Override
public DynamicType.Builder<?> transform(

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVersionScanPlugin.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVersionScanPlugin.java
@@ -102,7 +102,9 @@ public class MuzzleVersionScanPlugin {
// verify helper injector works
final String[] helperClassNames = defaultInstrumenter.helperClassNames();
if (helperClassNames.length > 0) {
- new HelperInjector(createHelperMap(defaultInstrumenter))
+ new HelperInjector(
+ MuzzleVersionScanPlugin.class.getSimpleName(),
+ createHelperMap(defaultInstrumenter))
.transform(null, null, userClassLoader, null);
}
} catch (final Exception e) {

MODIFY
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/HelperInjectionTest.groovy
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/HelperInjectionTest.groovy
@@ -24,7 +24,7 @@ class HelperInjectionTest extends DDSpecification {
def "helpers injected to non-delegating classloader"() {
setup:
String helperClassName = HelperInjectionTest.getPackage().getName() + '.HelperClass'
- HelperInjector injector = new HelperInjector(helperClassName)
+ HelperInjector injector = new HelperInjector("test", helperClassName)
AtomicReference<URLClassLoader> emptyLoader = new AtomicReference<>(new URLClassLoader(new URL[0], (ClassLoader) null))
when:
@@ -56,7 +56,7 @@ class HelperInjectionTest extends DDSpecification {
ByteBuddyAgent.install()
AgentInstaller.installBytebuddyAgent(ByteBuddyAgent.getInstrumentation())
String helperClassName = HelperInjectionTest.getPackage().getName() + '.HelperClass'
- HelperInjector injector = new HelperInjector(helperClassName)
+ HelperInjector injector = new HelperInjector("test", helperClassName)
URLClassLoader bootstrapChild = new URLClassLoader(new URL[0], (ClassLoader) null)
when:

Improving debuggability by requestingName

This change adds a requestingName field to help in diagnosing the source of any injection failures.

Usually, the requestingName is the simple class name of the Instrumenter; however, this change doesn't propagate the Instumeneter named through FieldBackedProvider.