diff --git a/buildSrc/src/main/groovy/MuzzlePlugin.groovy b/buildSrc/src/main/groovy/MuzzlePlugin.groovy index e62679f21e3..ce31a4bd6a5 100644 --- a/buildSrc/src/main/groovy/MuzzlePlugin.groovy +++ b/buildSrc/src/main/groovy/MuzzlePlugin.groovy @@ -263,9 +263,15 @@ class MuzzlePlugin implements Plugin { private static Task addMuzzleTask(MuzzleDirective muzzleDirective, Artifact versionArtifact, Project instrumentationProject, Task runAfter, Project bootstrapProject, Project toolingProject) { def taskName = "muzzle-Assert${muzzleDirective.assertPass ? "Pass" : "Fail"}-$versionArtifact.groupId-$versionArtifact.artifactId-$versionArtifact.version${muzzleDirective.name ? "-${muzzleDirective.getNameSlug()}" : ""}" def config = instrumentationProject.configurations.create(taskName) - config.dependencies.add(instrumentationProject.dependencies.create("$versionArtifact.groupId:$versionArtifact.artifactId:$versionArtifact.version") { + def dep = instrumentationProject.dependencies.create("$versionArtifact.groupId:$versionArtifact.artifactId:$versionArtifact.version") { transitive = true - }) + } + // The following optional transitive dependencies are brought in by some legacy module such as log4j 1.x but are no + // longer bundled with the JVM and have to be excluded for the muzzle tests to be able to run. + dep.exclude group: 'com.sun.jdmk', module: 'jmxtools' + dep.exclude group: 'com.sun.jmx', module: 'jmxri' + + config.dependencies.add(dep) for (String additionalDependency : muzzleDirective.additionalDependencies) { config.dependencies.add(instrumentationProject.dependencies.create(additionalDependency) { transitive = true diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java index ba39b39e5fb..b51197d4ada 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java @@ -127,7 +127,7 @@ public static ResettableClassFileTransformer installBytebuddyAgent( .or(nameStartsWith("com.singularity.")) .or(nameStartsWith("com.jinspired.")) .or(nameStartsWith("org.jinspired.")) - .or(nameStartsWith("org.apache.log4j.")) + .or(nameStartsWith("org.apache.log4j.").and(not(named("org.apache.log4j.MDC")))) .or(nameStartsWith("org.slf4j.").and(not(named("org.slf4j.MDC")))) .or(nameContains("$JaxbAccessor")) .or(nameContains("CGLIB$$")) diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/log/LogContextScopeListener.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/log/LogContextScopeListener.java new file mode 100644 index 00000000000..7e3ddff23b7 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/log/LogContextScopeListener.java @@ -0,0 +1,47 @@ +package datadog.trace.agent.tooling.log; + +import datadog.trace.api.CorrelationIdentifier; +import datadog.trace.context.ScopeListener; +import java.lang.reflect.Method; +import lombok.extern.slf4j.Slf4j; + +/** + * A scope listener that receives the MDC/ThreadContext put and receive methods and update the trace + * and span reference anytime a new scope is activated or closed. + */ +@Slf4j +public class LogContextScopeListener implements ScopeListener { + + /** A reference to the log context method that sets a new attribute in the log context */ + private final Method putMethod; + + /** A reference to the log context method that removes an attribute from the log context */ + private final Method removeMethod; + + public LogContextScopeListener(final Method putMethod, final Method removeMethod) { + this.putMethod = putMethod; + this.removeMethod = removeMethod; + } + + @Override + public void afterScopeActivated() { + try { + putMethod.invoke( + null, CorrelationIdentifier.getTraceIdKey(), CorrelationIdentifier.getTraceId()); + putMethod.invoke( + null, CorrelationIdentifier.getSpanIdKey(), CorrelationIdentifier.getSpanId()); + } catch (final Exception e) { + log.debug("Exception setting log context context", e); + } + } + + @Override + public void afterScopeClosed() { + try { + removeMethod.invoke(null, CorrelationIdentifier.getTraceIdKey()); + removeMethod.invoke(null, CorrelationIdentifier.getSpanIdKey()); + } catch (final Exception e) { + log.debug("Exception removing log context context", e); + } + } +} diff --git a/dd-java-agent/instrumentation/log4j1/log4j1.gradle b/dd-java-agent/instrumentation/log4j1/log4j1.gradle new file mode 100644 index 00000000000..a69b9a67607 --- /dev/null +++ b/dd-java-agent/instrumentation/log4j1/log4j1.gradle @@ -0,0 +1,37 @@ +apply from: "${rootDir}/gradle/java.gradle" + +ext { + log4jVersion = '1.2.17' +} + +muzzle { + pass { + group = 'log4j' + module = 'log4j' + versions = '(,)' + } +} + +configurations { + // In order to test the real log4j library we need to remove the log4j transitive + // dependency 'log4j-over-slf4j' brought in by :dd-java-agent:testing which would shadow + // the log4j module under test using a proxy to slf4j instead. + testCompile.exclude group: 'org.slf4j', module: 'log4j-over-slf4j' + + // See: https://stackoverflow.com/a/9047963/2749853 + testCompile.exclude group: 'javax.jms', module: 'jms' +} + +dependencies { + compile project(':dd-trace-api') + compile project(':dd-java-agent:agent-tooling') + + testCompile group: 'log4j', name: 'log4j', version: log4jVersion + + compile deps.bytebuddy + compile deps.opentracing + annotationProcessor deps.autoservice + implementation deps.autoservice + + testCompile project(':dd-java-agent:testing') +} diff --git a/dd-java-agent/instrumentation/log4j1/src/main/java/datadog/trace/instrumentation/log4j1/Log4j1MDCInstrumentation.java b/dd-java-agent/instrumentation/log4j1/src/main/java/datadog/trace/instrumentation/log4j1/Log4j1MDCInstrumentation.java new file mode 100644 index 00000000000..2e970b08d9b --- /dev/null +++ b/dd-java-agent/instrumentation/log4j1/src/main/java/datadog/trace/instrumentation/log4j1/Log4j1MDCInstrumentation.java @@ -0,0 +1,65 @@ +package datadog.trace.instrumentation.log4j1; + +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.log.LogContextScopeListener; +import datadog.trace.api.Config; +import datadog.trace.api.GlobalTracer; +import java.lang.reflect.Method; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class Log4j1MDCInstrumentation extends Instrumenter.Default { + public static final String MDC_INSTRUMENTATION_NAME = "log4j1"; + + public Log4j1MDCInstrumentation() { + super(MDC_INSTRUMENTATION_NAME); + } + + @Override + protected boolean defaultEnabled() { + return Config.get().isLogsInjectionEnabled(); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.log4j.MDC"); + } + + @Override + public Map, String> transformers() { + return singletonMap(isConstructor(), MDCContextAdvice.class.getName()); + } + + @Override + public String[] helperClassNames() { + return new String[] {LogContextScopeListener.class.getName()}; + } + + public static class MDCContextAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void mdcClassInitialized(@Advice.This Object instance) { + if (instance == null) { + return; + } + + try { + Class mdcClass = instance.getClass(); + final Method putMethod = mdcClass.getMethod("put", String.class, Object.class); + final Method removeMethod = mdcClass.getMethod("remove", String.class); + GlobalTracer.get().addScopeListener(new LogContextScopeListener(putMethod, removeMethod)); + } catch (final NoSuchMethodException e) { + org.slf4j.LoggerFactory.getLogger(instance.getClass()) + .debug("Failed to add log4j ThreadContext span listener", e); + } + } + } +} diff --git a/dd-java-agent/instrumentation/log4j1/src/test/groovy/Log4j1MDCTest.groovy b/dd-java-agent/instrumentation/log4j1/src/test/groovy/Log4j1MDCTest.groovy new file mode 100644 index 00000000000..24a9c3264ee --- /dev/null +++ b/dd-java-agent/instrumentation/log4j1/src/test/groovy/Log4j1MDCTest.groovy @@ -0,0 +1,15 @@ +import datadog.trace.agent.test.log.injection.LogContextInjectionTestBase +import org.apache.log4j.MDC + +class Log4j1MDCTest extends LogContextInjectionTestBase { + + @Override + def put(String key, Object value) { + return MDC.put(key, value) + } + + @Override + def get(String key) { + return MDC.get(key) + } +} diff --git a/dd-java-agent/instrumentation/log4j2/log4j2.gradle b/dd-java-agent/instrumentation/log4j2/log4j2.gradle new file mode 100644 index 00000000000..7130100cfc6 --- /dev/null +++ b/dd-java-agent/instrumentation/log4j2/log4j2.gradle @@ -0,0 +1,40 @@ +apply from: "${rootDir}/gradle/java.gradle" + +ext { + log4jVersion = '2.11.2' +} + +configurations { + // In order to test the real log4j library we need to remove the log4j transitive + // dependency 'log4j-over-slf4j' brought in by :dd-java-agent:testing which would shadow + // the log4j module under test using a proxy to slf4j instead. + testCompile.exclude group: 'org.slf4j', module: 'log4j-over-slf4j' +} + +muzzle { + pass { + group = 'org.apache.logging.log4j' + module = 'log4j-core' + versions = '(,)' + } + + pass { + group = 'org.apache.logging.log4j' + module = 'log4j-api' + versions = '(,)' + } +} + +dependencies { + compile project(':dd-trace-api') + compile project(':dd-java-agent:agent-tooling') + + compile deps.bytebuddy + compile deps.opentracing + annotationProcessor deps.autoservice + implementation deps.autoservice + + testCompile project(':dd-java-agent:testing') + testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: log4jVersion + testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: log4jVersion +} diff --git a/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/ThreadContextInstrumentation.java b/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/ThreadContextInstrumentation.java new file mode 100644 index 00000000000..954526a9d06 --- /dev/null +++ b/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/ThreadContextInstrumentation.java @@ -0,0 +1,60 @@ +package datadog.trace.instrumentation.log4j2; + +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.log.LogContextScopeListener; +import datadog.trace.api.Config; +import datadog.trace.api.GlobalTracer; +import java.lang.reflect.Method; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class ThreadContextInstrumentation extends Instrumenter.Default { + public static final String MDC_INSTRUMENTATION_NAME = "log4j"; + + public ThreadContextInstrumentation() { + super(MDC_INSTRUMENTATION_NAME); + } + + @Override + protected boolean defaultEnabled() { + return Config.get().isLogsInjectionEnabled(); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.logging.log4j.ThreadContext"); + } + + @Override + public Map, String> transformers() { + return singletonMap(isTypeInitializer(), ThreadContextAdvice.class.getName()); + } + + @Override + public String[] helperClassNames() { + return new String[] {LogContextScopeListener.class.getName()}; + } + + public static class ThreadContextAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void mdcClassInitialized(@Advice.Origin final Class threadClass) { + try { + final Method putMethod = threadClass.getMethod("put", String.class, String.class); + final Method removeMethod = threadClass.getMethod("remove", String.class); + GlobalTracer.get().addScopeListener(new LogContextScopeListener(putMethod, removeMethod)); + } catch (final NoSuchMethodException e) { + org.slf4j.LoggerFactory.getLogger(threadClass) + .debug("Failed to add log4j ThreadContext span listener", e); + } + } + } +} diff --git a/dd-java-agent/instrumentation/log4j2/src/test/groovy/Log4jThreadContextTest.groovy b/dd-java-agent/instrumentation/log4j2/src/test/groovy/Log4jThreadContextTest.groovy new file mode 100644 index 00000000000..9dacdb7a0a1 --- /dev/null +++ b/dd-java-agent/instrumentation/log4j2/src/test/groovy/Log4jThreadContextTest.groovy @@ -0,0 +1,15 @@ +import datadog.trace.agent.test.log.injection.LogContextInjectionTestBase +import org.apache.logging.log4j.ThreadContext + +class Log4jThreadContextTest extends LogContextInjectionTestBase { + + @Override + def put(String key, Object value) { + return ThreadContext.put(key, value as String) + } + + @Override + def get(String key) { + return ThreadContext.get(key) + } +} diff --git a/dd-java-agent/instrumentation/slf4j-mdc/src/main/java/datadog/trace/instrumentation/slf4j/mdc/MDCInjectionInstrumentation.java b/dd-java-agent/instrumentation/slf4j-mdc/src/main/java/datadog/trace/instrumentation/slf4j/mdc/MDCInjectionInstrumentation.java index 992923d30c2..d9a203d971d 100644 --- a/dd-java-agent/instrumentation/slf4j-mdc/src/main/java/datadog/trace/instrumentation/slf4j/mdc/MDCInjectionInstrumentation.java +++ b/dd-java-agent/instrumentation/slf4j-mdc/src/main/java/datadog/trace/instrumentation/slf4j/mdc/MDCInjectionInstrumentation.java @@ -6,14 +6,12 @@ import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.log.LogContextScopeListener; import datadog.trace.api.Config; -import datadog.trace.api.CorrelationIdentifier; import datadog.trace.api.GlobalTracer; -import datadog.trace.context.ScopeListener; import java.lang.reflect.Method; import java.security.ProtectionDomain; import java.util.Map; -import lombok.extern.slf4j.Slf4j; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -61,7 +59,7 @@ public Map, String> transfor @Override public String[] helperClassNames() { - return new String[] {MDCAdvice.class.getName() + "$MDCScopeListener"}; + return new String[] {LogContextScopeListener.class.getName()}; } public static class MDCAdvice { @@ -70,43 +68,10 @@ public static void mdcClassInitialized(@Advice.Origin final Class mdcClass) { try { final Method putMethod = mdcClass.getMethod("put", String.class, String.class); final Method removeMethod = mdcClass.getMethod("remove", String.class); - GlobalTracer.get().addScopeListener(new MDCScopeListener(putMethod, removeMethod)); + GlobalTracer.get().addScopeListener(new LogContextScopeListener(putMethod, removeMethod)); } catch (final NoSuchMethodException e) { org.slf4j.LoggerFactory.getLogger(mdcClass).debug("Failed to add MDC span listener", e); } } - - @Slf4j - public static class MDCScopeListener implements ScopeListener { - private final Method putMethod; - private final Method removeMethod; - - public MDCScopeListener(final Method putMethod, final Method removeMethod) { - this.putMethod = putMethod; - this.removeMethod = removeMethod; - } - - @Override - public void afterScopeActivated() { - try { - putMethod.invoke( - null, CorrelationIdentifier.getTraceIdKey(), CorrelationIdentifier.getTraceId()); - putMethod.invoke( - null, CorrelationIdentifier.getSpanIdKey(), CorrelationIdentifier.getSpanId()); - } catch (final Exception e) { - log.debug("Exception setting mdc context", e); - } - } - - @Override - public void afterScopeClosed() { - try { - removeMethod.invoke(null, CorrelationIdentifier.getTraceIdKey()); - removeMethod.invoke(null, CorrelationIdentifier.getSpanIdKey()); - } catch (final Exception e) { - log.debug("Exception removing mdc context", e); - } - } - } } } diff --git a/dd-java-agent/instrumentation/slf4j-mdc/src/test/groovy/Slf4jMDCTest.groovy b/dd-java-agent/instrumentation/slf4j-mdc/src/test/groovy/Slf4jMDCTest.groovy index 727c68d9c1a..c83b658ad4e 100644 --- a/dd-java-agent/instrumentation/slf4j-mdc/src/test/groovy/Slf4jMDCTest.groovy +++ b/dd-java-agent/instrumentation/slf4j-mdc/src/test/groovy/Slf4jMDCTest.groovy @@ -1,95 +1,15 @@ -import datadog.trace.agent.test.AgentTestRunner -import datadog.trace.agent.test.utils.ConfigUtils -import datadog.trace.api.CorrelationIdentifier -import io.opentracing.Scope -import io.opentracing.util.GlobalTracer +import datadog.trace.agent.test.log.injection.LogContextInjectionTestBase import org.slf4j.MDC -import java.util.concurrent.atomic.AtomicReference +class Slf4jMDCTest extends LogContextInjectionTestBase { -class Slf4jMDCTest extends AgentTestRunner { - static { - ConfigUtils.updateConfig { - System.setProperty("dd.logs.injection", "true") - } + @Override + def put(String key, Object value) { + return MDC.put(key, value as String) } - def "mdc shows trace and span ids for active scope"() { - when: - MDC.put("foo", "bar") - Scope rootScope = GlobalTracer.get().buildSpan("root").startActive(true) - - then: - MDC.get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId() - MDC.get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId() - MDC.get("foo") == "bar" - - when: - Scope childScope = GlobalTracer.get().buildSpan("child").startActive(true) - - then: - MDC.get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId() - MDC.get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId() - MDC.get("foo") == "bar" - - when: - childScope.close() - - then: - MDC.get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId() - MDC.get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId() - MDC.get("foo") == "bar" - - when: - rootScope.close() - - then: - MDC.get(CorrelationIdentifier.getTraceIdKey()) == null - MDC.get(CorrelationIdentifier.getSpanIdKey()) == null - MDC.get("foo") == "bar" - } - - def "mdc context scoped by thread"() { - setup: - AtomicReference thread1TraceId = new AtomicReference<>() - AtomicReference thread2TraceId = new AtomicReference<>() - - final Thread thread1 = new Thread() { - @Override - void run() { - // no trace in scope - thread1TraceId.set(MDC.get(CorrelationIdentifier.getTraceIdKey())) - } - } - - final Thread thread2 = new Thread() { - @Override - void run() { - // other trace in scope - final Scope thread2Scope = GlobalTracer.get().buildSpan("root2").startActive(true) - try { - thread2TraceId.set(MDC.get(CorrelationIdentifier.getTraceIdKey())) - } finally { - thread2Scope.close() - } - } - } - final Scope mainScope = GlobalTracer.get().buildSpan("root").startActive(true) - thread1.start() - thread2.start() - final String mainThreadTraceId = MDC.get(CorrelationIdentifier.getTraceIdKey()) - final String expectedMainThreadTraceId = CorrelationIdentifier.getTraceId() - - thread1.join() - thread2.join() - - expect: - mainThreadTraceId == expectedMainThreadTraceId - thread1TraceId.get() == null - thread2TraceId.get() != null - thread2TraceId.get() != mainThreadTraceId - - cleanup: - mainScope?.close() + @Override + def get(String key) { + return MDC.get(key) } } diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/log/injection/LogContextInjectionTestBase.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/log/injection/LogContextInjectionTestBase.groovy new file mode 100644 index 00000000000..1eee80b584e --- /dev/null +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/log/injection/LogContextInjectionTestBase.groovy @@ -0,0 +1,114 @@ +package datadog.trace.agent.test.log.injection + +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.utils.ConfigUtils +import datadog.trace.api.CorrelationIdentifier +import io.opentracing.Scope +import io.opentracing.util.GlobalTracer + +import java.util.concurrent.atomic.AtomicReference + +/** + * This class represents the standard test cases that new logging library integrations MUST + * satisfy in order to support log injection. + */ +abstract class LogContextInjectionTestBase extends AgentTestRunner { + + /** + * Set in the framework-specific context the given value at the given key + */ + abstract put(String key, Object value) + + /** + * Get from the framework-specific context the value at the given key + */ + abstract get(String key) + + static { + ConfigUtils.updateConfig { + System.setProperty("dd.logs.injection", "true") + } + } + + def "Log context shows trace and span ids for active scope"() { + when: + put("foo", "bar") + Scope rootScope = GlobalTracer.get().buildSpan("root").startActive(true) + + then: + get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId() + get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId() + get("foo") == "bar" + + when: + Scope childScope = GlobalTracer.get().buildSpan("child").startActive(true) + + then: + get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId() + get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId() + get("foo") == "bar" + + when: + childScope.close() + + then: + get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId() + get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId() + get("foo") == "bar" + + when: + rootScope.close() + + then: + get(CorrelationIdentifier.getTraceIdKey()) == null + get(CorrelationIdentifier.getSpanIdKey()) == null + get("foo") == "bar" + } + + def "Log context is scoped by thread"() { + setup: + ConfigUtils.updateConfig { + System.setProperty("dd.logs.injection", "true") + } + AtomicReference thread1TraceId = new AtomicReference<>() + AtomicReference thread2TraceId = new AtomicReference<>() + + final Thread thread1 = new Thread() { + @Override + void run() { + // no trace in scope + thread1TraceId.set(get(CorrelationIdentifier.getTraceIdKey())) + } + } + + final Thread thread2 = new Thread() { + @Override + void run() { + // other trace in scope + final Scope thread2Scope = GlobalTracer.get().buildSpan("root2").startActive(true) + try { + thread2TraceId.set(get(CorrelationIdentifier.getTraceIdKey())) + } finally { + thread2Scope.close() + } + } + } + final Scope mainScope = GlobalTracer.get().buildSpan("root").startActive(true) + thread1.start() + thread2.start() + final String mainThreadTraceId = get(CorrelationIdentifier.getTraceIdKey()) + final String expectedMainThreadTraceId = CorrelationIdentifier.getTraceId() + + thread1.join() + thread2.join() + + expect: + mainThreadTraceId == expectedMainThreadTraceId + thread1TraceId.get() == null + thread2TraceId.get() != null + thread2TraceId.get() != mainThreadTraceId + + cleanup: + mainScope?.close() + } +} diff --git a/dd-java-agent/testing/testing.gradle b/dd-java-agent/testing/testing.gradle index 4a951232a60..5fe8b9f022c 100644 --- a/dd-java-agent/testing/testing.gradle +++ b/dd-java-agent/testing/testing.gradle @@ -5,6 +5,7 @@ minimumInstructionCoverage = 0.5 excludedClassesCoverage += [ 'datadog.trace.agent.test.asserts.*Assert', 'datadog.trace.agent.test.base.*', + 'datadog.trace.agent.test.log.*', 'datadog.trace.agent.test.AgentTestRunner.ErrorCountingListener', 'datadog.trace.agent.test.utils.*', // Avoid applying jacoco instrumentation to classes instrumented by tested agent diff --git a/gradle/jacoco.gradle b/gradle/jacoco.gradle index e946702d49f..b77294005ae 100644 --- a/gradle/jacoco.gradle +++ b/gradle/jacoco.gradle @@ -32,7 +32,7 @@ project.ext.minimumInstructionCoverage = 0.9 afterEvaluate { test { jacoco { - // Make sure that excluded classes do not get jacoc instrumentation applied since it may confuse apm agent in some cases + // Make sure that excluded classes do not get jacoco instrumentation applied since it may confuse apm agent in some cases excludes = project.excludedClassesCoverage } } diff --git a/settings.gradle b/settings.gradle index 0cd07e4c116..23b68dd31f0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -66,6 +66,8 @@ include ':dd-java-agent:instrumentation:jsp-2.3' include ':dd-java-agent:instrumentation:kafka-clients-0.11' include ':dd-java-agent:instrumentation:kafka-streams-0.11' include ':dd-java-agent:instrumentation:lettuce-5' +include ':dd-java-agent:instrumentation:log4j1' +include ':dd-java-agent:instrumentation:log4j2' include ':dd-java-agent:instrumentation:mongo' include ':dd-java-agent:instrumentation:mongo:driver-3.1' include ':dd-java-agent:instrumentation:mongo:driver-async-3.3'