Introduce the mandatory test suite  that a supported logging library MUST satisfy for log injection
MODIFY
dd-java-agent/instrumentation/log4j1/src/test/groovy/Log4j1MDCTest.groovy
dd-java-agent/instrumentation/log4j1/src/test/groovy/Log4j1MDCTest.groovy
-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.LogContextContextInjectionTest
import org.apache.log4j.MDC
-import java.util.concurrent.atomic.AtomicReference
-class Log4j1MDCTest extends AgentTestRunner {
+class Log4j1MDCTest extends LogContextContextInjectionTest {
- static {
- ConfigUtils.updateConfig {
- System.setProperty("dd.logs.injection", "true")
- }
- }
-
- 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:
- ConfigUtils.updateConfig {
- System.setProperty("dd.logs.injection", "true")
- }
- AtomicReference<Object> thread1TraceId = new AtomicReference<>()
- AtomicReference<Object> thread2TraceId = new AtomicReference<>()
-
- final Thread thread1 = new Thread() {
@Override
- void run() {
- // no trace in scope
- thread1TraceId.set(MDC.get(CorrelationIdentifier.getTraceIdKey()))
- }
+ def put(String key, Object value) {
+ return MDC.put(key, value)
}
- 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()
+ def get(String key) {
+ return MDC.get(key)
}
}

MODIFY
dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/ThreadContextInstrumentation.java
dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/ThreadContextInstrumentation.java
@@ -10,13 +10,11 @@ import datadog.trace.agent.tooling.log.LogContextScopeListener;
import datadog.trace.api.Config;
import datadog.trace.api.GlobalTracer;
import java.lang.reflect.Method;
-import java.security.ProtectionDomain;
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;
-import net.bytebuddy.utility.JavaModule;
@AutoService(Instrumenter.class)
public class ThreadContextInstrumentation extends Instrumenter.Default {

MODIFY
dd-java-agent/instrumentation/log4j2/src/test/groovy/Log4jThreadContextTest.groovy
dd-java-agent/instrumentation/log4j2/src/test/groovy/Log4jThreadContextTest.groovy
-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.LogContextContextInjectionTest
import org.apache.logging.log4j.ThreadContext
-import java.util.concurrent.atomic.AtomicReference
+class Log4jThreadContextTest extends LogContextContextInjectionTest {
-class Log4jThreadContextTest extends AgentTestRunner {
-
- static {
- ConfigUtils.updateConfig {
- System.setProperty("dd.logs.injection", "true")
- }
- }
-
- def "ThreadContext shows trace and span ids for active scope"() {
- when:
- ThreadContext.put("foo", "bar")
- Scope rootScope = GlobalTracer.get().buildSpan("root").startActive(true)
-
- then:
- ThreadContext.get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId()
- ThreadContext.get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId()
- ThreadContext.get("foo") == "bar"
-
- when:
- Scope childScope = GlobalTracer.get().buildSpan("child").startActive(true)
-
- then:
- ThreadContext.get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId()
- ThreadContext.get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId()
- ThreadContext.get("foo") == "bar"
-
- when:
- childScope.close()
-
- then:
- ThreadContext.get(CorrelationIdentifier.getTraceIdKey()) == CorrelationIdentifier.getTraceId()
- ThreadContext.get(CorrelationIdentifier.getSpanIdKey()) == CorrelationIdentifier.getSpanId()
- ThreadContext.get("foo") == "bar"
-
- when:
- rootScope.close()
-
- then:
- ThreadContext.get(CorrelationIdentifier.getTraceIdKey()) == null
- ThreadContext.get(CorrelationIdentifier.getSpanIdKey()) == null
- ThreadContext.get("foo") == "bar"
- }
-
- def "ThreadContext context scoped by thread"() {
- setup:
- ConfigUtils.updateConfig {
- System.setProperty("dd.logs.injection", "true")
- }
- AtomicReference<String> thread1TraceId = new AtomicReference<>()
- AtomicReference<String> thread2TraceId = new AtomicReference<>()
-
- final Thread thread1 = new Thread() {
@Override
- void run() {
- // no trace in scope
- thread1TraceId.set(ThreadContext.get(CorrelationIdentifier.getTraceIdKey()))
- }
+ def put(String key, Object value) {
+ return ThreadContext.put(key, value as String)
}
- final Thread thread2 = new Thread() {
@Override
- void run() {
- // other trace in scope
- final Scope thread2Scope = GlobalTracer.get().buildSpan("root2").startActive(true)
- try {
- thread2TraceId.set(ThreadContext.get(CorrelationIdentifier.getTraceIdKey()))
- } finally {
- thread2Scope.close()
- }
- }
- }
- final Scope mainScope = GlobalTracer.get().buildSpan("root").startActive(true)
- thread1.start()
- thread2.start()
- final String mainThreadTraceId = ThreadContext.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()
+ def get(String key) {
+ return ThreadContext.get(key)
}
}

MODIFY
dd-java-agent/instrumentation/slf4j-mdc/src/test/groovy/Slf4jMDCTest.groovy
dd-java-agent/instrumentation/slf4j-mdc/src/test/groovy/Slf4jMDCTest.groovy
-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.LogContextContextInjectionTest
import org.slf4j.MDC
-import java.util.concurrent.atomic.AtomicReference
+class Slf4jMDCTest extends LogContextContextInjectionTest {
-class Slf4jMDCTest extends AgentTestRunner {
- static {
- ConfigUtils.updateConfig {
- System.setProperty("dd.logs.injection", "true")
- }
- }
-
- 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<String> thread1TraceId = new AtomicReference<>()
- AtomicReference<String> thread2TraceId = new AtomicReference<>()
-
- final Thread thread1 = new Thread() {
@Override
- void run() {
- // no trace in scope
- thread1TraceId.set(MDC.get(CorrelationIdentifier.getTraceIdKey()))
- }
+ def put(String key, Object value) {
+ return MDC.put(key, value as String)
}
- 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()
+ def get(String key) {
+ return MDC.get(key)
}
}

ADD
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/log/injection/LogContextContextInjectionTest.groovy
+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 integration MUST satisfy in order to support log injection.
+ */
+abstract class LogContextContextInjectionTest 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 given 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<String> thread1TraceId = new AtomicReference<>()
+ AtomicReference<String> 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()
+ }
+}

Introduce the mandatory test suite  that a supported logging library MUST satisfy for log injection