-
Notifications
You must be signed in to change notification settings - Fork 336
Support log trace injection for log4j 1.x and log4j 2.x when used without Slf4j #894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
22477ef
Add basic support for log4j ThreadContext for log injection.
labbati d9eb480
Rename log4j2 module to reflect library name
labbati 8d562f9
Rename log4j package removing the left-over from sl4j original class
labbati dcf0f57
Add support for log4j 1.x
labbati 34d589e
Create a reusable log context listener to be used for slf4j, log4j1 a…
labbati ecdf666
Refactor log4jX instrumentations
labbati c2bd5ee
Remove method that was copied and pasted from slf4j instrumentation …
labbati 4d1d5d1
Introduce the mandatory test suite that a supported logging library …
labbati b7393df
Remove jms functionality brought in by log4j1.X dependency
labbati cba8ba1
Refactor classes of log injection services to improve readability
labbati 27b4db8
Remove legacy transitive dependencies no longer bundled with the JVM …
labbati 0916a00
Fixed unnecessary semi-colon in groovy class
labbati d0f17e4
Rename log context injection test base
labbati ac7abcd
Fix expression to include tracing of log4j1 MDC in agent installer
labbati f279a61
Minor fixes to typos and code styles
labbati 8ee6d26
Rename log4j 1 and 2 instrumentation to log4j1 and log4j2 respectively
labbati File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
.../agent-tooling/src/main/java/datadog/trace/agent/tooling/log/LogContextScopeListener.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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') | ||
| } |
65 changes: 65 additions & 0 deletions
65
...n/log4j1/src/main/java/datadog/trace/instrumentation/log4j1/Log4j1MDCInstrumentation.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<? super TypeDescription> typeMatcher() { | ||
| return named("org.apache.log4j.MDC"); | ||
| } | ||
|
|
||
| @Override | ||
| public Map<? extends ElementMatcher<? super MethodDescription>, 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); | ||
| } | ||
| } | ||
| } | ||
| } | ||
15 changes: 15 additions & 0 deletions
15
dd-java-agent/instrumentation/log4j1/src/test/groovy/Log4j1MDCTest.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
60 changes: 60 additions & 0 deletions
60
...g4j2/src/main/java/datadog/trace/instrumentation/log4j2/ThreadContextInstrumentation.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<? super TypeDescription> typeMatcher() { | ||
| return named("org.apache.logging.log4j.ThreadContext"); | ||
| } | ||
|
|
||
| @Override | ||
| public Map<? extends ElementMatcher<? super MethodDescription>, 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); | ||
| } | ||
| } | ||
| } | ||
| } |
15 changes: 15 additions & 0 deletions
15
dd-java-agent/instrumentation/log4j2/src/test/groovy/Log4jThreadContextTest.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
org.apache.log4j.MDCseems to be class with only static methods. It seems slightly awkward to instrument it's constructor. Are you sure it is being called at all?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, nm, I was looking at only one implementation out of a few... using constructor as advice hook still seems odd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It took a while to find out the source code corresponding to the version being downloaded by gradle. Here is the link for future reference: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/MDC.java?view=markup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You do not have to change this now, or at all - but I think
isTypeInitializermight have been better matcher here.