Remove jre logging integration
This commit is contained in:
parent
3295739e28
commit
6ffaa9e04f
|
@ -1,19 +0,0 @@
|
||||||
// Set properties before any plugins get loaded
|
|
||||||
project.ext {
|
|
||||||
// Execute tests on all JVMs, even rare and outdated ones
|
|
||||||
coreJavaInstrumentation = true
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "${rootDir}/gradle/java.gradle"
|
|
||||||
|
|
||||||
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')
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
package datadog.trace.instrumentation.jre.logging;
|
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.failSafe;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
|
||||||
import datadog.trace.agent.tooling.ClassLoaderMatcher;
|
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
|
||||||
import datadog.trace.agent.tooling.Utils;
|
|
||||||
import datadog.trace.agent.tooling.muzzle.Reference;
|
|
||||||
import datadog.trace.agent.tooling.muzzle.ReferenceMatcher;
|
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
|
||||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
|
||||||
import net.bytebuddy.description.field.FieldDescription;
|
|
||||||
import net.bytebuddy.description.field.FieldList;
|
|
||||||
import net.bytebuddy.description.method.MethodList;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
|
||||||
import net.bytebuddy.implementation.Implementation;
|
|
||||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
|
||||||
import net.bytebuddy.jar.asm.ClassWriter;
|
|
||||||
import net.bytebuddy.jar.asm.Label;
|
|
||||||
import net.bytebuddy.jar.asm.MethodVisitor;
|
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
|
||||||
import net.bytebuddy.jar.asm.Type;
|
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
|
||||||
import net.bytebuddy.pool.TypePool;
|
|
||||||
import net.bytebuddy.utility.JavaModule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This instrumentation patches java.util.logging.Logger to return a "safe" logger which doesn't
|
|
||||||
* touch the global log manager when static log creators (e.g. getLogger) are invoked under datadog
|
|
||||||
* threads.
|
|
||||||
*
|
|
||||||
* <p>Our PatchLogger solution is not enough here because it's possible for dd-threads to use
|
|
||||||
* bootstrap utils which in turn use call Logger.getLogger(...)
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@AutoService(Instrumenter.class)
|
|
||||||
public class LoggerInstrumentation implements Instrumenter {
|
|
||||||
// Intentionally doing the string replace to bypass gradle shadow rename
|
|
||||||
// loggerClassName = java.util.logging.Logger
|
|
||||||
private static final String loggerClassName =
|
|
||||||
"java.util.logging.TMP".replaceFirst("TMP", "Logger");
|
|
||||||
private ReferenceMatcher referenceMatcher = null;
|
|
||||||
|
|
||||||
public LoggerInstrumentation() {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentBuilder instrument(AgentBuilder agentBuilder) {
|
|
||||||
return agentBuilder
|
|
||||||
.type(
|
|
||||||
failSafe(
|
|
||||||
named(loggerClassName),
|
|
||||||
"Instrumentation type matcher unexpected exception: " + getClass().getName()),
|
|
||||||
failSafe(
|
|
||||||
new ElementMatcher<ClassLoader>() {
|
|
||||||
@Override
|
|
||||||
public boolean matches(ClassLoader target) {
|
|
||||||
return target == ClassLoaderMatcher.BOOTSTRAP_CLASSLOADER;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Instrumentation class loader matcher unexpected exception: "
|
|
||||||
+ getClass().getName()))
|
|
||||||
.and(
|
|
||||||
new AgentBuilder.RawMatcher() {
|
|
||||||
@Override
|
|
||||||
public boolean matches(
|
|
||||||
TypeDescription typeDescription,
|
|
||||||
ClassLoader classLoader,
|
|
||||||
JavaModule module,
|
|
||||||
Class<?> classBeingRedefined,
|
|
||||||
ProtectionDomain protectionDomain) {
|
|
||||||
// make sure the private constructor is present before applying instrumentation.
|
|
||||||
return getReferenceMatcher().matches(classLoader);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.transform(
|
|
||||||
new AgentBuilder.Transformer() {
|
|
||||||
@Override
|
|
||||||
public DynamicType.Builder<?> transform(
|
|
||||||
DynamicType.Builder<?> builder,
|
|
||||||
TypeDescription typeDescription,
|
|
||||||
ClassLoader classLoader,
|
|
||||||
JavaModule module) {
|
|
||||||
return builder.visit(new ReturnPatchLoggerForDDThreadsVisitor());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create or build a muzzle reference matcher to assert private logging constructor is present.
|
|
||||||
*
|
|
||||||
* <p>Logging instrumentation uses this private constructor because it's the only path to create a
|
|
||||||
* logger which does not touch the global log manager.
|
|
||||||
*/
|
|
||||||
private synchronized ReferenceMatcher getReferenceMatcher() {
|
|
||||||
if (null == referenceMatcher) {
|
|
||||||
referenceMatcher =
|
|
||||||
new ReferenceMatcher(
|
|
||||||
new Reference.Builder(loggerClassName)
|
|
||||||
.withMethod(
|
|
||||||
new Reference.Source[0],
|
|
||||||
new Reference.Flag[] {Reference.Flag.PRIVATE_OR_HIGHER},
|
|
||||||
"<init>",
|
|
||||||
Type.VOID_TYPE,
|
|
||||||
Type.getType(String.class))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
return referenceMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace java.util.logging.Logger#getLogger() methods with an early-return for datadog threads
|
|
||||||
* to avoid initializing the global log manager.
|
|
||||||
*/
|
|
||||||
private static class ReturnPatchLoggerForDDThreadsVisitor implements AsmVisitorWrapper {
|
|
||||||
@Override
|
|
||||||
public int mergeWriter(int flags) {
|
|
||||||
return flags | ClassWriter.COMPUTE_MAXS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int mergeReader(int flags) {
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassVisitor wrap(
|
|
||||||
TypeDescription instrumentedType,
|
|
||||||
ClassVisitor classVisitor,
|
|
||||||
Implementation.Context implementationContext,
|
|
||||||
TypePool typePool,
|
|
||||||
FieldList<FieldDescription.InDefinedShape> fields,
|
|
||||||
MethodList<?> methods,
|
|
||||||
int writerFlags,
|
|
||||||
int readerFlags) {
|
|
||||||
return new ClassVisitor(Opcodes.ASM7, classVisitor) {
|
|
||||||
@Override
|
|
||||||
public void visit(
|
|
||||||
int version,
|
|
||||||
int access,
|
|
||||||
String name,
|
|
||||||
String signature,
|
|
||||||
String superName,
|
|
||||||
String[] interfaces) {
|
|
||||||
super.visit(version, access, name, signature, superName, interfaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(
|
|
||||||
int access,
|
|
||||||
final String methodName,
|
|
||||||
final String methodDesc,
|
|
||||||
String signature,
|
|
||||||
String[] exceptions) {
|
|
||||||
final MethodVisitor mv =
|
|
||||||
super.visitMethod(access, methodName, methodDesc, signature, exceptions);
|
|
||||||
// if isStatic and returns a logger, then add our advice
|
|
||||||
if ((access & Opcodes.ACC_STATIC) != 0) {
|
|
||||||
final Type returnType = Type.getReturnType(methodDesc);
|
|
||||||
if (returnType != null
|
|
||||||
&& returnType.getSort() == Type.OBJECT
|
|
||||||
&& returnType.getInternalName().equals(Utils.getInternalName(loggerClassName))) {
|
|
||||||
return new MethodVisitor(Opcodes.ASM7, mv) {
|
|
||||||
@Override
|
|
||||||
public void visitCode() {
|
|
||||||
/* Appends an early return to the method body for datadog threads:
|
|
||||||
*
|
|
||||||
* if (Thread.currentThread().getName().startsWith("dd-")) {
|
|
||||||
* // intentionally invoke private constructor to avoid initializing the LogManager
|
|
||||||
* return new Logger("datadog");
|
|
||||||
* }
|
|
||||||
* // original method body
|
|
||||||
*/
|
|
||||||
final Label originalMethodBody = new Label();
|
|
||||||
mv.visitMethodInsn(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
"java/lang/Thread",
|
|
||||||
"currentThread",
|
|
||||||
"()Ljava/lang/Thread;",
|
|
||||||
false);
|
|
||||||
mv.visitMethodInsn(
|
|
||||||
Opcodes.INVOKEVIRTUAL,
|
|
||||||
"java/lang/Thread",
|
|
||||||
"getName",
|
|
||||||
"()Ljava/lang/String;",
|
|
||||||
false);
|
|
||||||
// TODO: move dd- prefix to a constant once agent-bootstrap project is visible to
|
|
||||||
// all subprojects
|
|
||||||
mv.visitLdcInsn("dd-");
|
|
||||||
mv.visitMethodInsn(
|
|
||||||
Opcodes.INVOKEVIRTUAL,
|
|
||||||
"java/lang/String",
|
|
||||||
"startsWith",
|
|
||||||
"(Ljava/lang/String;)Z",
|
|
||||||
false);
|
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, originalMethodBody);
|
|
||||||
mv.visitTypeInsn(Opcodes.NEW, Utils.getInternalName(loggerClassName));
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
mv.visitLdcInsn("datadog-logger");
|
|
||||||
mv.visitMethodInsn(
|
|
||||||
Opcodes.INVOKESPECIAL,
|
|
||||||
Utils.getInternalName(loggerClassName),
|
|
||||||
"<init>",
|
|
||||||
"(Ljava/lang/String;)V",
|
|
||||||
false);
|
|
||||||
mv.visitInsn(Opcodes.ARETURN);
|
|
||||||
mv.visitLabel(originalMethodBody);
|
|
||||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
|
||||||
super.visitCode();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mv;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
import datadog.trace.agent.test.AgentTestRunner
|
|
||||||
|
|
||||||
import java.util.logging.Logger
|
|
||||||
|
|
||||||
class LoggerPatchTest extends AgentTestRunner {
|
|
||||||
def "datadog threads receive custom logger"() {
|
|
||||||
setup:
|
|
||||||
String threadName = Thread.currentThread().getName()
|
|
||||||
Thread.currentThread().setName("dd-test")
|
|
||||||
Logger log = Logger.getLogger("foobar")
|
|
||||||
|
|
||||||
expect:
|
|
||||||
log.getName() == "datadog-logger"
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
Thread.currentThread().setName(threadName)
|
|
||||||
}
|
|
||||||
|
|
||||||
def "normal threads can reach the normal logger"() {
|
|
||||||
setup:
|
|
||||||
Logger log = Logger.getLogger("foobar")
|
|
||||||
|
|
||||||
expect:
|
|
||||||
log.getName() == "foobar"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -50,7 +50,6 @@ include ':dd-java-agent:instrumentation:jdbc'
|
||||||
include ':dd-java-agent:instrumentation:jedis-1.4'
|
include ':dd-java-agent:instrumentation:jedis-1.4'
|
||||||
include ':dd-java-agent:instrumentation:jetty-8'
|
include ':dd-java-agent:instrumentation:jetty-8'
|
||||||
include ':dd-java-agent:instrumentation:jms'
|
include ':dd-java-agent:instrumentation:jms'
|
||||||
include ':dd-java-agent:instrumentation:jre-logging'
|
|
||||||
include ':dd-java-agent:instrumentation:jsp-2.3'
|
include ':dd-java-agent:instrumentation:jsp-2.3'
|
||||||
include ':dd-java-agent:instrumentation:kafka-clients-0.11'
|
include ':dd-java-agent:instrumentation:kafka-clients-0.11'
|
||||||
include ':dd-java-agent:instrumentation:kafka-streams-0.11'
|
include ':dd-java-agent:instrumentation:kafka-streams-0.11'
|
||||||
|
|
Loading…
Reference in New Issue