Skip to content

Commit ae3634d

Browse files
authored
Merge pull request #120 from DataDog/tyler/split-manager
Split up manager responsibility by job
2 parents 89f40e3 + a437b6d commit ae3634d

11 files changed

Lines changed: 283 additions & 195 deletions

File tree

.circleci/config.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ jobs:
1414

1515
- run:
1616
name: Run Tests
17-
command: ./gradlew clean check --parallel
17+
command: ./gradlew clean check --parallel --stacktrace
1818

1919
- run:
2020
name: Verify Version Scan
21-
command: ./gradlew verifyVersionScan --parallel
21+
command: ./gradlew verifyVersionScan --parallel --stacktrace
2222

2323
- save_cache:
2424
paths:
@@ -58,5 +58,5 @@ jobs:
5858
-PbintrayUser=${BINTRAY_USER} \
5959
-PbintrayApiKey=${BINTRAY_API_KEY} \
6060
-PbuildInfo.build.number=${CIRCLE_BUILD_NUM} \
61-
artifactoryPublish --max-workers=1
61+
artifactoryPublish --max-workers=1 --stacktrace
6262
fi

dd-java-agent-ittests/dd-java-agent-ittests.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ test {
4848
// Defining here to allow jacoco to be first on the command line.
4949
jvmArgs "-javaagent:${project(':dd-java-agent').buildDir}/libs/dd-java-agent-${project.version}.jar"
5050
}
51+
52+
testLogging {
53+
events "started"
54+
}
5155

5256
if (project.hasProperty("disableShadowRelocate") && disableShadowRelocate) {
5357
exclude 'com/datadoghq/trace/agent/ShadowPackageRenamingTest.class'

dd-java-agent/dd-java-agent.gradle

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,9 @@ apply from: "${rootDir}/gradle/jacoco.gradle"
1010
jacocoTestReport.dependsOn ':dd-java-agent-ittests:test'
1111

1212
whitelistedInstructionClasses += whitelistedBranchClasses += [
13-
"com.datadoghq.trace.agent.integration.*",
14-
'com.datadoghq.trace.agent.AnnotationsTracingAgent',
15-
'com.datadoghq.trace.agent.AgentTracerConfig',
16-
'com.datadoghq.trace.agent.TraceAnnotationsManager',
17-
'com.datadoghq.trace.agent.InstrumentationChecker',
18-
'com.datadoghq.trace.agent.DDJavaAgentInfo',
13+
'com.datadoghq.trace.agent.*',
14+
'com.datadoghq.trace.agent.integration.*',
1915
'io.opentracing.contrib.mongo.TracingCommandListenerFactory',
20-
'com.datadoghq.trace.agent.InstrumentationChecker.1',
21-
'com.datadoghq.trace.agent.InstrumentationChecker.ArtifactSupport',
2216
]
2317

2418
dependencies {
@@ -55,8 +49,8 @@ jar {
5549
attributes(
5650
"Main-Class": "com.datadoghq.trace.agent.DDJavaAgentInfo",
5751
// I don't think we want to define this since we can't really load after startup:
58-
//"Agent-Class": "com.datadoghq.trace.agent.AnnotationsTracingAgent",
59-
"Premain-Class": "com.datadoghq.trace.agent.AnnotationsTracingAgent",
52+
//"Agent-Class": "com.datadoghq.trace.agent.TracingAgent",
53+
"Premain-Class": "com.datadoghq.trace.agent.TracingAgent",
6054
"Can-Redefine-Classes": true,
6155
"Can-Retransform-Classes": true,
6256
// It is dangerous putting everything on the bootstrap classpath,
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.datadoghq.trace.agent;
2+
3+
import com.datadoghq.trace.DDTraceAnnotationsInfo;
4+
import com.datadoghq.trace.DDTraceInfo;
5+
import com.datadoghq.trace.resolver.DDTracerFactory;
6+
import com.datadoghq.trace.resolver.FactoryUtils;
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.io.PrintWriter;
10+
import java.io.StringWriter;
11+
import java.net.URI;
12+
import java.net.URISyntaxException;
13+
import java.net.URL;
14+
import java.util.ArrayList;
15+
import java.util.Enumeration;
16+
import java.util.List;
17+
import lombok.extern.slf4j.Slf4j;
18+
import org.jboss.byteman.agent.Retransformer;
19+
20+
@Slf4j
21+
public class AgentRulesManager {
22+
23+
// Initialize the info classes so they print out their version info:
24+
private static final String ddJavaAgentVersion = DDJavaAgentInfo.VERSION;
25+
private static final String ddTraceVersion = DDTraceInfo.VERSION;
26+
private static final String ddTraceAnnotationsVersion = DDTraceAnnotationsInfo.VERSION;
27+
28+
private static final String SPRING_BOOT_RULE = "spring-boot-rule.btm";
29+
30+
protected static volatile AgentRulesManager INSTANCE;
31+
32+
protected final Retransformer transformer;
33+
protected final TracingAgentConfig agentTracerConfig;
34+
protected final InstrumentationRulesManager instrumentationRulesManager;
35+
protected final TraceAnnotationsManager traceAnnotationsManager;
36+
37+
public AgentRulesManager(Retransformer trans, TracingAgentConfig config) {
38+
transformer = trans;
39+
agentTracerConfig = config;
40+
traceAnnotationsManager = new TraceAnnotationsManager(trans, config);
41+
instrumentationRulesManager = new InstrumentationRulesManager(trans, config, this);
42+
}
43+
44+
/**
45+
* This method initializes the manager.
46+
*
47+
* @param trans The ByteMan retransformer
48+
*/
49+
public static void initialize(final Retransformer trans) {
50+
log.debug("Initializing {}", AgentRulesManager.class.getSimpleName());
51+
52+
TracingAgentConfig config =
53+
FactoryUtils.loadConfigFromFilePropertyOrResource(
54+
DDTracerFactory.SYSTEM_PROPERTY_CONFIG_PATH,
55+
DDTracerFactory.CONFIG_PATH,
56+
TracingAgentConfig.class);
57+
58+
log.debug("Configuration: {}", config.toString());
59+
60+
AgentRulesManager manager = new AgentRulesManager(trans, config);
61+
62+
INSTANCE = manager;
63+
64+
manager.loadRules(SPRING_BOOT_RULE, ClassLoader.getSystemClassLoader());
65+
manager.traceAnnotationsManager.initialize();
66+
}
67+
68+
/**
69+
* This method loads any OpenTracing Agent rules (integration-rules.btm) found as resources within
70+
* the supplied classloader.
71+
*
72+
* @param classLoader The classloader
73+
*/
74+
protected List<String> loadRules(String rulesFileName, final ClassLoader classLoader) {
75+
final List<String> scripts = new ArrayList<>();
76+
if (transformer == null) {
77+
log.warn(
78+
"Attempt to load rules file {} on classloader {} before transformer initialized",
79+
rulesFileName,
80+
classLoader == null ? "bootstrap" : classLoader);
81+
return scripts;
82+
}
83+
84+
log.debug("Loading rules with classloader {}", classLoader == null ? "bootstrap" : classLoader);
85+
86+
final List<String> scriptNames = new ArrayList<>();
87+
88+
// Load default and custom rules
89+
try {
90+
final Enumeration<URL> iter = classLoader.getResources(rulesFileName);
91+
while (iter.hasMoreElements()) {
92+
loadRules(iter.nextElement().toURI(), scriptNames, scripts);
93+
}
94+
95+
final StringWriter sw = new StringWriter();
96+
try (PrintWriter writer = new PrintWriter(sw)) {
97+
try {
98+
transformer.installScript(scripts, scriptNames, writer);
99+
} catch (final Exception e) {
100+
log.warn("Failed to install scripts", e);
101+
}
102+
}
103+
log.debug(sw.toString());
104+
} catch (IOException | URISyntaxException e) {
105+
log.warn("Failed to load rules", e);
106+
}
107+
108+
log.debug("Rules loaded from {} on classloader {}", rulesFileName, classLoader);
109+
if (log.isTraceEnabled()) {
110+
for (final String rule : scripts) {
111+
log.trace("Loading rule: {}", rule);
112+
}
113+
}
114+
return scripts;
115+
}
116+
117+
private static void loadRules(
118+
final URI uri, final List<String> scriptNames, final List<String> scripts)
119+
throws IOException {
120+
log.debug("Load rules from URI uri={} ", uri);
121+
122+
final StringBuilder str = new StringBuilder();
123+
try (InputStream is = uri.toURL().openStream()) {
124+
125+
final byte[] b = new byte[10240];
126+
int len;
127+
while ((len = is.read(b)) != -1) {
128+
str.append(new String(b, 0, len));
129+
}
130+
}
131+
scripts.add(str.toString());
132+
scriptNames.add(uri.toString());
133+
}
134+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.datadoghq.trace.agent;
2+
3+
import com.google.common.collect.Sets;
4+
import java.io.PrintWriter;
5+
import java.io.StringWriter;
6+
import java.util.ArrayList;
7+
import java.util.HashSet;
8+
import java.util.List;
9+
import java.util.Set;
10+
import java.util.regex.Matcher;
11+
import java.util.regex.Pattern;
12+
import lombok.extern.slf4j.Slf4j;
13+
import org.jboss.byteman.agent.Retransformer;
14+
15+
/**
16+
* This manager is loaded at pre-main. It loads all the scripts contained in all the 'oatrules.btm'
17+
* resource files.
18+
*/
19+
@Slf4j
20+
public class InstrumentationRulesManager {
21+
22+
private static final String INTEGRATION_RULES = "integration-rules.btm";
23+
24+
private final Retransformer transformer;
25+
private final TracingAgentConfig config;
26+
private final AgentRulesManager agentRulesManager;
27+
28+
private final Set<ClassLoader> initializedClassloaders = Sets.newConcurrentHashSet();
29+
30+
public InstrumentationRulesManager(
31+
Retransformer trans, TracingAgentConfig config, AgentRulesManager agentRulesManager) {
32+
this.transformer = trans;
33+
this.config = config;
34+
this.agentRulesManager = agentRulesManager;
35+
36+
if (InstrumentationChecker.isClassPresent(
37+
"org.springframework.boot.loader.LaunchedURLClassLoader",
38+
ClassLoader.getSystemClassLoader())) {
39+
log.info(
40+
"Running in the context of a Spring Boot executable jar. Deferring rule loading to run in the LaunchedURLClassLoader.");
41+
agentRulesManager.loadRules("spring-boot-rule.btm", ClassLoader.getSystemClassLoader());
42+
} else {
43+
initialize(ClassLoader.getSystemClassLoader());
44+
}
45+
}
46+
47+
public static void registerClassLoad(Object obj) {
48+
log.info("Calling initialize with {}", obj);
49+
ClassLoader cl;
50+
if (obj instanceof ClassLoader) {
51+
cl = (ClassLoader) obj;
52+
} else {
53+
cl = obj.getClass().getClassLoader();
54+
}
55+
56+
AgentRulesManager.INSTANCE.instrumentationRulesManager.initialize(cl);
57+
}
58+
59+
/**
60+
* This method is separated out from initialize to allow Spring Boot's LaunchedURLClassLoader to
61+
* call it once it is loaded.
62+
*
63+
* @param classLoader
64+
*/
65+
public void initialize(ClassLoader classLoader) {
66+
synchronized (classLoader) {
67+
if (initializedClassloaders.contains(classLoader)) {
68+
return;
69+
}
70+
initializedClassloaders.add(classLoader);
71+
}
72+
73+
final List<String> loadedScripts = agentRulesManager.loadRules(INTEGRATION_RULES, classLoader);
74+
75+
//Check if some rules have to be uninstalled
76+
final List<String> uninstallScripts = InstrumentationChecker.getUnsupportedRules(classLoader);
77+
if (config != null) {
78+
final List<String> disabledInstrumentations = config.getDisabledInstrumentations();
79+
if (disabledInstrumentations != null && !disabledInstrumentations.isEmpty()) {
80+
uninstallScripts.addAll(disabledInstrumentations);
81+
}
82+
}
83+
84+
try {
85+
uninstallScripts(loadedScripts, uninstallScripts);
86+
} catch (Exception e) {
87+
log.warn("Error uninstalling scripts", e);
88+
}
89+
}
90+
91+
/**
92+
* Uninstall some scripts from a list of patterns. All the rules that contain the pattern will be
93+
* uninstalled
94+
*
95+
* @param patterns not case sensitive (eg. "mongo", "apache http", "elasticsearch", etc...])
96+
*/
97+
private void uninstallScripts(final List<String> installedScripts, final List<String> patterns)
98+
throws Exception {
99+
final Set<String> rulesToRemove = new HashSet<>();
100+
101+
for (final String strPattern : patterns) {
102+
final Pattern pattern = Pattern.compile("(?i)RULE [^\n]*" + strPattern + "[^\n]*\n");
103+
for (final String loadedScript : installedScripts) {
104+
final Matcher matcher = pattern.matcher(loadedScript);
105+
while (matcher.find()) {
106+
rulesToRemove.add(matcher.group());
107+
}
108+
}
109+
}
110+
111+
if (!rulesToRemove.isEmpty()) {
112+
final StringWriter sw = new StringWriter();
113+
try (PrintWriter pr = new PrintWriter(sw)) {
114+
transformer.removeScripts(new ArrayList<>(rulesToRemove), pr);
115+
}
116+
log.info("Uninstall rule scripts: {}", rulesToRemove.toString());
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)