Enable instrumentation to scan for classes for version matching

This commit is contained in:
Tyler Benson 2017-08-11 17:44:54 -07:00
parent 03ca35426c
commit 860df9856c
5 changed files with 124 additions and 56 deletions

View File

@ -1,14 +1,18 @@
package com.datadoghq.trace.agent; package com.datadoghq.trace.agent;
import com.datadoghq.trace.resolver.FactoryUtils; import com.datadoghq.trace.resolver.FactoryUtils;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
@ -19,20 +23,23 @@ import lombok.extern.slf4j.Slf4j;
public class InstrumentationChecker { public class InstrumentationChecker {
private static final String CONFIG_FILE = "dd-trace-supported-framework"; private static final String CONFIG_FILE = "dd-trace-supported-framework";
private final Map<String, List<Map<String, String>>> rules; private final Map<String, List<ArtifactSupport>> rules;
private final Map<String, String> frameworks; private final Map<String, String> frameworks;
private static InstrumentationChecker INSTANCE; private static InstrumentationChecker INSTANCE;
private final ClassLoader classLoader;
/* For testing purpose */ /* For testing purpose */
InstrumentationChecker( InstrumentationChecker(
final Map<String, List<Map<String, String>>> rules, final Map<String, String> frameworks) { final Map<String, List<ArtifactSupport>> rules, final Map<String, String> frameworks) {
this.rules = rules; this.rules = rules;
this.frameworks = frameworks; this.frameworks = frameworks;
this.classLoader = ClassLoader.getSystemClassLoader();
INSTANCE = this; INSTANCE = this;
} }
private InstrumentationChecker() { private InstrumentationChecker(final ClassLoader classLoader) {
this.classLoader = classLoader;
rules = rules =
FactoryUtils.loadConfigFromResource( FactoryUtils.loadConfigFromResource(
CONFIG_FILE, new TypeReference<Map<String, List<Map<String, String>>>>() {}); CONFIG_FILE, new TypeReference<Map<String, List<Map<String, String>>>>() {});
@ -43,11 +50,12 @@ public class InstrumentationChecker {
* Return a list of unsupported rules regarding loading deps * Return a list of unsupported rules regarding loading deps
* *
* @return the list of unsupported rules * @return the list of unsupported rules
* @param classLoader
*/ */
public static synchronized List<String> getUnsupportedRules() { public static synchronized List<String> getUnsupportedRules(final ClassLoader classLoader) {
if (INSTANCE == null) { if (INSTANCE == null) {
INSTANCE = new InstrumentationChecker(); INSTANCE = new InstrumentationChecker(classLoader);
} }
return INSTANCE.doGetUnsupportedRules(); return INSTANCE.doGetUnsupportedRules();
@ -60,16 +68,24 @@ public class InstrumentationChecker {
// Check rules // Check rules
boolean supported = false; boolean supported = false;
for (final Map<String, String> check : rules.get(rule)) { for (final ArtifactSupport check : rules.get(rule)) {
if (frameworks.containsKey(check.get("artifact"))) { if (frameworks.containsKey(check.artifact)) {
final boolean matched = // If no classes to scan, fall back on version regex.
Pattern.matches( boolean matched =
check.get("supported_version"), frameworks.get(check.get("artifact"))); check.identifyingPresentClasses.isEmpty() && check.identifyingMissingClasses.isEmpty()
? Pattern.matches(check.supportedVersion, frameworks.get(check.artifact))
: true;
for (final String identifyingClass : check.identifyingPresentClasses) {
matched &= isClassPresent(identifyingClass);
}
for (final String identifyingClass : check.identifyingMissingClasses) {
matched &= !isClassPresent(identifyingClass);
}
if (!matched) { if (!matched) {
log.debug( log.debug(
"Library conflict: supported_version={}, actual_version={}", "Library conflict: supported_version={}, actual_version={}",
check.get("supported_version"), check.supportedVersion,
frameworks.get(check.get("artifact"))); frameworks.get(check.artifact));
supported = false; supported = false;
break; break;
} }
@ -87,6 +103,15 @@ public class InstrumentationChecker {
return unsupportedRules; return unsupportedRules;
} }
private boolean isClassPresent(final String identifyingPresentClass) {
try {
return identifyingPresentClass != null
&& Class.forName(identifyingPresentClass, false, classLoader) != null;
} catch (final ClassNotFoundException e) {
return false;
}
}
private static Map<String, String> scanLoadedLibraries() { private static Map<String, String> scanLoadedLibraries() {
final Map<String, String> frameworks = new HashMap<>(); final Map<String, String> frameworks = new HashMap<>();
@ -143,4 +168,19 @@ public class InstrumentationChecker {
return null; return null;
} }
} }
@Data
@JsonIgnoreProperties("check")
static class ArtifactSupport {
private String artifact;
@JsonProperty("supported_version")
private String supportedVersion;
@JsonProperty("identifying_present_classes")
private List<String> identifyingPresentClasses = Collections.emptyList();
@JsonProperty("identifying_missing_classes")
private List<String> identifyingMissingClasses = Collections.emptyList();
}
} }

View File

@ -11,7 +11,12 @@ import java.lang.reflect.Method;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javassist.ClassPool; import javassist.ClassPool;
@ -58,7 +63,8 @@ public class TraceAnnotationsManager {
final List<String> loadedScripts = loadRules(ClassLoader.getSystemClassLoader()); final List<String> loadedScripts = loadRules(ClassLoader.getSystemClassLoader());
//Check if some rules have to be uninstalled //Check if some rules have to be uninstalled
final List<String> uninstallScripts = InstrumentationChecker.getUnsupportedRules(); final List<String> uninstallScripts =
InstrumentationChecker.getUnsupportedRules(ClassLoader.getSystemClassLoader());
if (agentTracerConfig != null) { if (agentTracerConfig != null) {
final List<String> disabledInstrumentations = agentTracerConfig.getDisabledInstrumentations(); final List<String> disabledInstrumentations = agentTracerConfig.getDisabledInstrumentations();
if (disabledInstrumentations != null && !disabledInstrumentations.isEmpty()) { if (disabledInstrumentations != null && !disabledInstrumentations.isEmpty()) {

View File

@ -1,55 +1,56 @@
### This file define all supported libraries ### This file define all supported libraries
### Syntax: ### Syntax:
### <rulename>: # the rule name defined in the otarules.btm ### <rulename>: # the rule name defined in the otarules.btm
### - check: # a list of tests, if one not matched, thus the rule is removed ### - artifact: <artifactId> # the artifact name to be tested
### articfact: <artifactId> # the artifact name to be tested
### supported_version: # a regex expression to express the version required by the rule ### supported_version: # a regex expression to express the version required by the rule
### - check: ... ### identifying_present_classes: # a list of classes that distinctly identify the range of supported libraries
### - some.key.FrameworkClass # only if missing, supported_version regex is used.
### - ...
opentracing-apache-httpclient: opentracing-apache-httpclient:
- check: - artifact: httpclient
artifact: httpclient
supported_version: 4\.[3|4|5]\..* supported_version: 4\.[3|4|5]\..*
- check:
artifact: commons-httpclient - artifact: commons-httpclient
supported_version: none supported_version: none
opentracing-aws-sdk: opentracing-aws-sdk:
- check: - artifact: aws-java-sdk
artifact: aws-java-sdk
supported_version: 1\.11\..* supported_version: 1\.11\..*
opentracing-cassandra-driver: opentracing-cassandra-driver:
- check: - artifact: cassandra-driver-core
artifact: cassandra-driver-core
supported_version: 3\.2.* supported_version: 3\.2.*
opentracing-web-servlet-filter: opentracing-web-servlet-filter:
- check: - artifact: jetty-server
artifact: jetty-server
supported_version: (8\.|9\.).* supported_version: (8\.|9\.).*
- check:
artifact: tomcat_catalina - artifact: tomcat_catalina
supported_version: (8\.|9\.).* supported_version: (8\.|9\.).*
- check:
artifact: tomcat-embed-core - artifact: tomcat-embed-core
supported_version: (8\.|9\.).* supported_version: (8\.|9\.).*
opentracing-okhttp3: opentracing-okhttp3:
- check: - artifact: okhttp
artifact: okhttp
supported_version: 3\..* supported_version: 3\..*
# For rules opentracing-jms-2_{consumer,producer} # For rules opentracing-jms-2_{consumer,producer}
opentracing-jms-2: opentracing-jms-2:
- check: - artifact: javax.jms-api
artifact: javax.jms-api
supported_version: 2\..* supported_version: 2\..*
opentracing-mongo-driver: opentracing-mongo-driver:
- check: - artifact: mongo-java-driver
artifact: mongo-java-driver
supported_version: 3\..* supported_version: 3\..*
identifying_present_classes:
- com.mongodb.operation.AsyncReadOperation.class
- com.mongodb.client.model.MapReduceAction.class
- check: - check:
artifact: mongodb-driver-async artifact: mongodb-driver-async
supported_version: 3\..* supported_version: 3\..*
identifying_present_classes:
- com.mongodb.operation.AsyncReadOperation.class
- com.mongodb.client.model.MapReduceAction.class

View File

@ -6,7 +6,7 @@ import spock.lang.Specification
class InstrumentationCheckerTest extends Specification { class InstrumentationCheckerTest extends Specification {
Map<String, List<Map<String, String>>> rules = Map<String, List<Map<String, String>>> rules =
FactoryUtils.loadConfigFromResource("supported-version-test", new TypeReference<Map<String, List<Map<String, String>>>>() { FactoryUtils.loadConfigFromResource("supported-version-test", new TypeReference<Map<String, List<InstrumentationChecker.ArtifactSupport>>>() {
}); });
Map<String, String> frameworks = [ Map<String, String> frameworks = [
"artifact-1": "1.2.3.1232", "artifact-1": "1.2.3.1232",
@ -18,10 +18,16 @@ class InstrumentationCheckerTest extends Specification {
def "test rules"() { def "test rules"() {
setup: setup:
def rules = InstrumentationChecker.getUnsupportedRules(); def rules = InstrumentationChecker.getUnsupportedRules(java.lang.ClassLoader.getSystemClassLoader());
expect: expect:
rules.size() == 3 rules.size() == 3
rules.sort() == ["unsupportedRuleOne", "unsupportedRuleThree", "unsupportedRuleTwo"] rules.sort() == ["unsupportedRuleOne", "unsupportedRuleThree", "unsupportedRuleTwo"]
} }
static class DemoClass1 {}
static class DemoClass2 {}
static class DemoClass3 {}
} }

View File

@ -1,26 +1,41 @@
unsupportedRuleOne: unsupportedRuleOne:
- check: - artifact: artifact-1
artifact: artifact-1
supported_version: 1\.2\.3\..* supported_version: 1\.2\.3\..*
- check:
artifact: artifact-2 - artifact: artifact-2
supported_version: none supported_version: none
identifying_present_classes:
-
unsupportedRuleTwo: unsupportedRuleTwo:
- check: - artifact: artifact-1
artifact: artifact-1
supported_version: 2\.3\. supported_version: 2\.3\.
identifying_present_classes:
- com.datadoghq.trace.agent.InstrumentationCheckerTest$MissingClass
supportedRuleOne: supportedRuleOne:
- check: - artifact: artifact-3
artifact: artifact-3
supported_version: 5\..* supported_version: 5\..*
identifying_present_classes:
- com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1
- com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass2
- com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass3
supportedRuleTwo: supportedRuleTwo:
- check: - artifact: artifact-1
artifact: artifact-1
supported_version: 1\.2\.3\..* supported_version: 1\.2\.3\..*
- check: identifying_present_classes:
artifact: artifact-2 - com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1
- com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass2
- artifact: artifact-2
supported_version: 4\..* supported_version: 4\..*
identifying_present_classes:
- com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass3
unsupportedRuleThree: unsupportedRuleThree:
- check: - artifact: foo
artifact: foo
supported_version: 1 supported_version: 1
identifying_present_classes:
- com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1
- com.datadoghq.trace.agent.InstrumentationCheckerTest$MissingClass