Add support for log4j 1.x
MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java
@@ -128,6 +128,7 @@ public class AgentInstaller {
.or(nameStartsWith("com.jinspired."))
.or(nameStartsWith("org.jinspired."))
.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$$"))

ADD
dd-java-agent/instrumentation/log4j1/log4j1.gradle
+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
+ // dependency brought in by :dd-java-agent:testing over 'log4j-over-slf4j' which would shadow
+ // the log4j module under test.
+ testCompile.exclude group: 'org.slf4j', module: 'log4j-over-slf4j'
+}
+
+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')
+}

ADD
dd-java-agent/instrumentation/log4j1/src/main/java/datadog/trace/instrumentation/log4j1/Log4j1MDCInstrumentation.java
+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.api.Config;
+import datadog.trace.api.CorrelationIdentifier;
+import datadog.trace.api.GlobalTracer;
+import datadog.trace.context.ScopeListener;
+import java.lang.reflect.Method;
+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;
+import net.bytebuddy.matcher.ElementMatcher;
+
+@AutoService(Instrumenter.class)
+public class Log4j1MDCInstrumentation extends Instrumenter.Default {
+ public static final String MDC_INSTRUMENTATION_NAME = "log4j-mdc";
+
+ private static final String mdcClassName = "org.apache.log4j.MDC";
+
+ public Log4j1MDCInstrumentation() {
+ super(MDC_INSTRUMENTATION_NAME);
+ }
+
+ @Override
+ protected boolean defaultEnabled() {
+ return Config.getBooleanSettingFromEnvironment(
+ Config.LOGS_INJECTION_ENABLED, Config.DEFAULT_LOGS_INJECTION_ENABLED);
+ }
+
+ @Override
+ public ElementMatcher<? super TypeDescription> typeMatcher() {
+ return named(mdcClassName);
+ }
+
+ @Override
+ public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
+ return singletonMap(isConstructor(), MDCContextAdvice.class.getName());
+ }
+
+ @Override
+ public String[] helperClassNames() {
+ return new String[] {MDCContextAdvice.class.getName() + "$MDCScopeListener"};
+ }
+
+ public static class MDCContextAdvice {
+ @Advice.OnMethodExit(suppress = Throwable.class)
+ public static void mdcClassInitialized(@Advice.This Object instance) {
+ try {
+ if (instance == null) {
+ return;
+ }
+
+ 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 MDCScopeListener(putMethod, removeMethod));
+ } catch (final NoSuchMethodException e) {
+ }
+ }
+
+ @Slf4j
+ public static class MDCScopeListener implements ScopeListener {
+ private final Method putMethod;
+ private final Method removeMethod;
+
+ public MDCScopeListener(final Method putMethod, final Method removeMethod) {
+ System.out.println("Initializing scope listener");
+ 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 thread 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 thread context context", e);
+ }
+ }
+ }
+ }
+}

ADD
dd-java-agent/instrumentation/log4j1/src/main/java/datadoggggg/trace/instrumentation/log4j1/something/SomeClass.java
+package datadoggggg.trace.instrumentation.log4j1.something;
+
+public class SomeClass {
+
+ private static SomeClass instance = new SomeClass();
+
+ public SomeClass() {
+ System.out.println("SomeClass Constructor.......!!!!!!!");
+ }
+
+ public static void put() {
+ instance.doSomething();
+ }
+
+ public void doSomething() {
+ System.out.println("SomeClass Doing something............");
+ }
+}

ADD
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 org.apache.log4j.MDC
+import java.util.concurrent.atomic.AtomicReference
+
+class Log4j1MDCTest extends AgentTestRunner {
+
+ static {
+ 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()))
+ }
+ }
+
+ 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()
+ }
+}

MODIFY
dd-java-agent/instrumentation/log4j2/log4j2.gradle
dd-java-agent/instrumentation/log4j2/log4j2.gradle
@@ -4,6 +4,13 @@ ext {
log4jVersion = '2.11.2'
}
+configurations {
+ // In order to test the real log4j library we need to remove the log4j transitive dependency
+ // dependency brought in by :dd-java-agent:testing over 'log4j-over-slf4j' which would shadow
+ // the log4j module under test.
+ testCompile.exclude group: 'org.slf4j', module: 'log4j-over-slf4j'
+}
+
muzzle {
pass {
group = 'org.apache.logging.log4j'

RENAME
dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j/ThreadContextInstrumentation.java
dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/ThreadContextInstrumentation.java
-package datadog.trace.instrumentation.log4j;
+package datadog.trace.instrumentation.log4j2;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer;

MODIFY
settings.gradle
settings.gradle
@@ -66,6 +66,7 @@ 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'

Add support for log4j 1.x