Added JarVersions sanity check with auto-disabling capabilities
ADD
dd-java-agent/src/main/java/io/opentracing/contrib/agent/JarVersionsChecker.java
+package io.opentracing.contrib.agent;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.datadoghq.trace.resolver.FactoryUtils;
+
+
+/**
+ * Utility class to check the validity of the classpath concerning the java automated instrumentations
+ */
+public class JarVersionsChecker {
+
+ private static Logger log = Logger.getLogger(JarVersionsChecker.class.getName());
+
+ public static String AUTHORIZED_VERSIONS_CONFIG ="dd-trace-authorized-versions.yaml";
+
+ /**
+ * Retrieves all the jars from the classpath
+ */
+ private static List<File> getJarsFromClasspath() {
+ return JarVersionsChecker.getJarFiles(System.getProperty("java.class.path"));
+ }
+
+ /**
+ * list files in the given directory and subdirs (with recursion)
+ * @param paths
+ * @return
+ */
+ public static List<File> getJarFiles(String paths) {
+ List<File> filesList = new ArrayList<File>();
+ for (final String path : paths.split(File.pathSeparator)) {
+ final File file = new File(path);
+ if( file.isDirectory()) {
+ recurse(filesList, file);
+ }
+ else {
+ if(file.getName().endsWith(".jar")){
+ filesList.add(file);
+ }
+ }
+ }
+ return filesList;
+ }
+
+ private static void recurse(List<File> filesList, File f) {
+ File list[] = f.listFiles();
+ for (File file : list) {
+ getJarFiles(file.getPath());
+ }
+ }
+
+ public static Pattern versionPattern = Pattern.compile("-(\\d+\\..+)\\.jar");
+ public static String extractJarVersion(String jarName){
+ Matcher matcher = versionPattern.matcher(jarName);
+ if (matcher.find())
+ return matcher.group(1);
+ else
+ return null;
+
+ }
+
+
+ public static void main(String args[]) throws Exception {
+ checkJarVersions();
+ }
+
+ /**
+ * Check all Jar versions in the classpath
+ *
+ * @return the list of jar keys that have been detected as potential issues
+ */
+ @SuppressWarnings("unchecked")
+ public static List<String> checkJarVersions() {
+ List<String> potentialIssues = new ArrayList<>();
+
+ //Load instrumentations versions
+ Map<String, Map<String,String>> versions = FactoryUtils.loadConfigFromResource(AUTHORIZED_VERSIONS_CONFIG, Map.class);
+ if(versions==null){
+ log.log(Level.WARNING, "DD agent: Authorized versions configuration file {} not found in classpath. Cannot proceed to the Jar versions check.",AUTHORIZED_VERSIONS_CONFIG);
+ return potentialIssues;
+ }
+
+ //Scan classpath provided jars
+ List<File> jars = getJarsFromClasspath();
+ for (File file: jars) {
+ String jarName = file.getName();
+ String versionRestrictions = extractJarVersion(jarName);
+
+ if(versionRestrictions!=null){
+ //Extract artifactId
+ String artifactId = file.getName().substring(0, jarName.indexOf(versionRestrictions)-1);
+ Map<String,String> restrictions = versions.get(artifactId);
+ if(restrictions!=null){
+ String versionPattern = restrictions.get("valid_versions");
+ if(versionPattern!=null){
+ if(!Pattern.matches(versionPattern, versionRestrictions)){
+ String key = restrictions.get("key");
+ if(key!=null){
+ potentialIssues.add(key);
+ }
+ log.log(Level.WARNING, "DD agent: The JAR {} as been found in the classpath. It may create some intrumentation issue, some rules are about to get disabled.",jarName);
+ }
+ }
+ }
+ }
+ }
+ return potentialIssues;
+ }
+
+
+}
\ No newline at end of file

MODIFY
dd-java-agent/src/main/java/io/opentracing/contrib/agent/TraceAnnotationsManager.java
dd-java-agent/src/main/java/io/opentracing/contrib/agent/TraceAnnotationsManager.java
@@ -63,12 +63,14 @@ public class TraceAnnotationsManager {
List<String> loadedScripts = loadRules(ClassLoader.getSystemClassLoader());
//Check if some rules have to be uninstalled
+ List<String> uninstallScripts = JarVersionsChecker.checkJarVersions();
if(agentTracerConfig != null){
- List<String> uninstallContributions = agentTracerConfig.getDisabledInstrumentations();
- if(uninstallContributions!=null && !uninstallContributions.isEmpty()){
- uninstallScripts(loadedScripts,uninstallContributions);
+ List<String> disabledInstrumentations = agentTracerConfig.getDisabledInstrumentations();
+ if(disabledInstrumentations!=null && !disabledInstrumentations.isEmpty()){
+ uninstallScripts.addAll(disabledInstrumentations);
}
}
+ uninstallScripts(loadedScripts,uninstallScripts);
//Check if annotations are enabled
if(agentTracerConfig != null && agentTracerConfig.isEnableCustomTracing()){
@@ -102,6 +104,49 @@ public class TraceAnnotationsManager {
log.log(Level.INFO, sw.toString());
}
+ /**
+ * This method loads any OpenTracing Agent rules (otarules.btm) found as resources
+ * within the supplied classloader.
+ *
+ * @param classLoader The classloader
+ */
+ public static List<String> loadRules(ClassLoader classLoader) {
+ List<String> scripts = new ArrayList<>();
+ if (transformer == null) {
+ log.severe("Attempt to load OpenTracing agent rules before transformer initialized");
+ return scripts;
+ }
+
+ List<String> scriptNames = new ArrayList<>();
+
+ // Load default and custom rules
+ try {
+ Enumeration<URL> iter = classLoader.getResources(AGENT_RULES);
+ while (iter.hasMoreElements()) {
+ loadRules(iter.nextElement().toURI(), scriptNames, scripts);
+ }
+
+ StringWriter sw=new StringWriter();
+ try (PrintWriter writer = new PrintWriter(sw)) {
+ try {
+ transformer.installScript(scripts, scriptNames, writer);
+ } catch (Exception e) {
+ log.log(Level.SEVERE, "Failed to install scripts", e);
+ }
+ }
+ if (log.isLoggable(Level.FINEST)) {
+ log.finest(sw.toString());
+ }
+ } catch (IOException | URISyntaxException e) {
+ log.log(Level.SEVERE, "Failed to load OpenTracing agent rules", e);
+ }
+
+ if (log.isLoggable(Level.FINE)) {
+ log.fine("OpenTracing Agent rules loaded");
+ }
+ return scripts;
+ }
+
/**
* Find all the methods annoted with @Trace and inject rules
*
@@ -186,49 +231,6 @@ public class TraceAnnotationsManager {
return ruleScript;
}
- /**
- * This method loads any OpenTracing Agent rules (otarules.btm) found as resources
- * within the supplied classloader.
- *
- * @param classLoader The classloader
- */
- public static List<String> loadRules(ClassLoader classLoader) {
- List<String> scripts = new ArrayList<>();
- if (transformer == null) {
- log.severe("Attempt to load OpenTracing agent rules before transformer initialized");
- return scripts;
- }
-
- List<String> scriptNames = new ArrayList<>();
-
- // Load default and custom rules
- try {
- Enumeration<URL> iter = classLoader.getResources(AGENT_RULES);
- while (iter.hasMoreElements()) {
- loadRules(iter.nextElement().toURI(), scriptNames, scripts);
- }
-
- StringWriter sw=new StringWriter();
- try (PrintWriter writer = new PrintWriter(sw)) {
- try {
- transformer.installScript(scripts, scriptNames, writer);
- } catch (Exception e) {
- log.log(Level.SEVERE, "Failed to install scripts", e);
- }
- }
- if (log.isLoggable(Level.FINEST)) {
- log.finest(sw.toString());
- }
- } catch (IOException | URISyntaxException e) {
- log.log(Level.SEVERE, "Failed to load OpenTracing agent rules", e);
- }
-
- if (log.isLoggable(Level.FINE)) {
- log.fine("OpenTracing Agent rules loaded");
- }
- return scripts;
- }
-
private static void loadRules(URI uri, final List<String> scriptNames,
final List<String> scripts) throws IOException {
if (log.isLoggable(Level.FINE)) {

ADD
dd-java-agent/src/main/resources/dd-trace-authorized-versions.yaml
+okhttp:
+ key: okhttp
+ valid_versions: 3\..*
+httpclient:
+ key: apache http
+ valid_versions: 4\..*
+aws-java-sdk:
+ key: aws
+ valid_versions: 1\..*
+jetty-server:
+ key: jetty
+ valid_versions: (8\.|9\.).*
+tomcat-embed-core:
+ key: tomcat
+ valid_versions: (8\.|9\.).*
+tomcat_catalina:
+ key: tomcat-catalina
+ artifactId: tomcat-catalina
+ valid_versions: (8\.|9\.).*
+spring_jdbc:
+ key: jdbc
+ valid_versions: 4\..*
+hibernate-core:
+ key: jdbc
+ valid_versions: 5\..*
+mongo-java-driver:
+ key: mongo
+ valid_versions: 3\..*
+opentracing-api:
+ key: opentracing-api
+ valid_versions: 0\.2.*
+
+
\ No newline at end of file

MODIFY
dd-trace-examples/dropwizard-mongo-client/pom.xml
dd-trace-examples/dropwizard-mongo-client/pom.xml
<!-- This dependency is required for the @trace annotation -->
<dependency>
<groupId>com.datadoghq</groupId>
- <artifactId>dd-java-agent</artifactId>
+ <artifactId>dd-trace</artifactId>
<version>${dd-trace.version}</version>
</dependency>

DELETE
dd-trace/src/main/java/com/datadoghq/trace/writer/DDSpanSerializer.java
-package com.datadoghq.trace.writer;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.opentracing.Span;
-
-/**
- * Main DDSpanSerializer: convert spans and traces to proper JSON
- */
-public class DDSpanSerializer {
-
- private final ObjectMapper objectMapper = new ObjectMapper();
-
- /* (non-Javadoc)
- * @see com.datadoghq.trace.DDSpanSerializer#serialize(io.opentracing.Span)
- */
- public String serialize(Span span) throws JsonProcessingException {
- return objectMapper.writeValueAsString(span);
- }
-
- /* (non-Javadoc)
- * @see com.datadoghq.trace.DDSpanSerializer#serialize(java.lang.Object)
- */
- public String serialize(Object spans) throws JsonProcessingException {
- return objectMapper.writeValueAsString(spans);
- }
-
- /* (non-Javadoc)
- * @see com.datadoghq.trace.DDSpanSerializer#deserialize(java.lang.String)
- */
- public io.opentracing.Span deserialize(String str) throws Exception {
- throw new UnsupportedOperationException("Deserialisation of spans is not implemented yet");
- }
-
-}

RENAME
dd-java-agent/src/main/java/io/opentracing/contrib/agent/Trace.java
dd-trace/src/main/java/io/opentracing/contrib/agent/Trace.java
MODIFY
dd-trace/src/test/java/com/datadoghq/trace/DDSpanSerializerTest.java
dd-trace/src/test/java/com/datadoghq/trace/DDSpanSerializerTest.java
package com.datadoghq.trace;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.datadoghq.trace.DDSpan;
-import com.datadoghq.trace.DDSpanContext;
-import com.datadoghq.trace.writer.DDSpanSerializer;
+import static org.assertj.core.api.Assertions.assertThat;
import java.util.HashMap;
import java.util.Map;
-import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
public class DDSpanSerializerTest {
- DDSpanSerializer serializer;
+ ObjectMapper serializer;
DDSpan span;
@Before
@@ -46,7 +44,7 @@ public class DDSpanSerializerTest {
context);
span.finish(133L);
- serializer = new DDSpanSerializer();
+ serializer = new ObjectMapper();
}
@Test
@@ -56,9 +54,9 @@ public class DDSpanSerializerTest {
String expected = "{\"meta\":{\"a-baggage\":\"value\",\"k1\":\"v1\"},\"service\":\"service\",\"error\":0,\"type\":\"type\",\"name\":\"operation\",\"duration\":33000,\"resource\":\"operation\",\"start\":100000,\"span_id\":2,\"parent_id\":0,\"trace_id\":1}";
// FIXME At the moment, just compare the string sizes
try {
- assertThat(serializer.serialize(span).length()).isEqualTo(expected.length());
+ assertThat(serializer.writeValueAsString(span).length()).isEqualTo(expected.length());
} catch (AssertionError e) {
- assertThat(serializer.serialize(span)).isEqualTo(expected);
+ assertThat(serializer.writeValueAsString(span)).isEqualTo(expected);
}
}

MODIFY
pom.xml
pom.xml
<executions>
<execution>
<id>sign-artifacts</id>
- <phase>verify</phase>
+ <phase>deploy</phase>
<goals>
<goal>sign</goal>
</goals>

Added JarVersions sanity check with auto-disabling capabilities