142 lines
4.8 KiB
Java
142 lines
4.8 KiB
Java
package com.datadoghq.agent;
|
|
|
|
import com.datadoghq.trace.resolver.FactoryUtils;
|
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
import com.fasterxml.jackson.core.type.TypeReference;
|
|
import java.lang.reflect.Method;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import lombok.Data;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
/**
|
|
* Utility class to check the validity of the classpath concerning the java automated
|
|
* instrumentations
|
|
*/
|
|
@Slf4j
|
|
public class InstrumentationChecker {
|
|
|
|
private static final String CONFIG_FILE = "dd-trace-supported-framework";
|
|
|
|
private final Map<String, List<ArtifactSupport>> rules;
|
|
|
|
/* For testing purpose */
|
|
InstrumentationChecker(
|
|
final Map<String, List<ArtifactSupport>> rules, final Map<String, String> frameworks) {
|
|
this.rules = rules;
|
|
}
|
|
|
|
public InstrumentationChecker() {
|
|
rules =
|
|
FactoryUtils.loadConfigFromResource(
|
|
CONFIG_FILE, new TypeReference<Map<String, List<ArtifactSupport>>>() {});
|
|
}
|
|
|
|
public List<String> getUnsupportedRules(final ClassLoader classLoader) {
|
|
log.debug("Checking rule compatibility on classloader {}", classLoader);
|
|
|
|
final List<String> unsupportedRules = new ArrayList<>();
|
|
for (final String rule : rules.keySet()) {
|
|
|
|
// Check rules
|
|
boolean supported = false;
|
|
for (final ArtifactSupport check : rules.get(rule)) {
|
|
log.debug("Checking rule {}", check);
|
|
|
|
boolean matched =
|
|
(check.identifyingPresentClasses != null
|
|
&& !check.identifyingPresentClasses.entrySet().isEmpty())
|
|
|| (check.identifyingMissingClasses != null
|
|
&& !check.identifyingMissingClasses.isEmpty());
|
|
if (check.identifyingPresentClasses != null) {
|
|
for (final Map.Entry<String, String> identifier :
|
|
check.identifyingPresentClasses.entrySet()) {
|
|
final boolean classPresent = isClassPresent(identifier.getKey(), classLoader);
|
|
if (!classPresent) {
|
|
log.debug(
|
|
"Instrumentation {} not applied due to missing class {}.", rule, identifier);
|
|
} else {
|
|
final String identifyingMethod = identifier.getValue();
|
|
if (identifyingMethod != null && !identifyingMethod.isEmpty()) {
|
|
final Class clazz = getClassIfPresent(identifier.getKey(), classLoader);
|
|
// already confirmed above the class is there.
|
|
final Method[] declaredMethods = clazz.getDeclaredMethods();
|
|
boolean methodFound = false;
|
|
for (final Method m : declaredMethods) {
|
|
if (m.getName().equals(identifyingMethod)) {
|
|
methodFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!methodFound) {
|
|
log.debug(
|
|
"Instrumentation {} not applied due to missing method {}.{}",
|
|
rule,
|
|
identifier.getKey(),
|
|
identifyingMethod);
|
|
matched = false;
|
|
}
|
|
}
|
|
}
|
|
matched &= classPresent;
|
|
}
|
|
}
|
|
if (check.identifyingMissingClasses != null) {
|
|
for (final String identifyingClass : check.identifyingMissingClasses) {
|
|
final boolean classMissing = !isClassPresent(identifyingClass, classLoader);
|
|
if (!classMissing) {
|
|
log.debug(
|
|
"Instrumentation {} not applied due to present class {}.",
|
|
rule,
|
|
identifyingClass);
|
|
}
|
|
matched &= classMissing;
|
|
}
|
|
}
|
|
|
|
supported |= matched;
|
|
if (supported) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!supported) {
|
|
log.info("Instrumentation rule={} is not supported", rule);
|
|
unsupportedRules.add(rule);
|
|
}
|
|
}
|
|
|
|
return unsupportedRules;
|
|
}
|
|
|
|
static boolean isClassPresent(
|
|
final String identifyingPresentClass, final ClassLoader classLoader) {
|
|
return getClassIfPresent(identifyingPresentClass, classLoader) != null;
|
|
}
|
|
|
|
static Class getClassIfPresent(
|
|
final String identifyingPresentClass, final ClassLoader classLoader) {
|
|
try {
|
|
return Class.forName(identifyingPresentClass, false, classLoader);
|
|
} catch (final Exception e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Data
|
|
static class ArtifactSupport {
|
|
private String artifact;
|
|
|
|
@JsonProperty("supported_version")
|
|
private String supportedVersion;
|
|
|
|
@JsonProperty("identifying_present_classes")
|
|
private Map<String, String> identifyingPresentClasses = Collections.emptyMap();
|
|
|
|
@JsonProperty("identifying_missing_classes")
|
|
private List<String> identifyingMissingClasses = Collections.emptyList();
|
|
}
|
|
}
|