Make auto instrumentation works when Module definition existing (#3199)

* Refactor open edge.

* Make refactor works.

* Revert log back.

* Remove unnecessary codes.

* Open edge for all internal binding interceptors.

* Add document about JDK 9+ module.

* Change bytebuddy to 1.10.1(unreleased)

* Change document

* 1. Adopt the latest bytebudy changes(snapshot only), commit b609711564.
2. Remove disruptor by ArrayBlockingQueue, because Disruptor can't work in both JDK 8(1) and 9-12, if we don't add `--add-modules jdk.unsupported`
3. Doc update.

* Update README.md
This commit is contained in:
吴晟 Wu Sheng 2019-08-15 07:46:28 +08:00 committed by GitHub
parent 19b37cab6f
commit 0ae2228a86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 223 additions and 115 deletions

View File

@ -36,15 +36,11 @@
<jetty.version>9.4.2.v20170220</jetty.version>
<grpc.version>1.14.0</grpc.version>
<guava.version>20.0</guava.version>
<bytebuddy.version>1.9.16</bytebuddy.version>
<disruptor.version>3.3.6</disruptor.version>
<bytebuddy.version>1.10.1</bytebuddy.version>
<wiremock.version>2.6.0</wiremock.version>
<netty-tcnative-boringssl-static.version>2.0.7.Final</netty-tcnative-boringssl-static.version>
<os-maven-plugin.version>1.4.1.Final</os-maven-plugin.version>
<shade.package>org.apache.skywalking.apm.dependencies</shade.package>
<shade.com.lmax.disruptor.source>com.lmax.disruptor</shade.com.lmax.disruptor.source>
<shade.com.lmax.disruptor.target>${shade.package}.${shade.com.lmax.disruptor.source}
</shade.com.lmax.disruptor.target>
<shade.com.google.source>com.google</shade.com.google.source>
<shade.com.google.target>${shade.package}.${shade.com.google.source}</shade.com.google.target>
<shade.io.grpc.source>io.grpc</shade.io.grpc.source>
@ -72,11 +68,6 @@
<artifactId>byte-buddy</artifactId>
<version>${bytebuddy.version}</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>${disruptor.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
@ -180,10 +171,6 @@
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>${shade.com.lmax.disruptor.source}</pattern>
<shadedPattern>${shade.com.lmax.disruptor.target}</shadedPattern>
</relocation>
<relocation>
<pattern>${shade.com.google.source}</pattern>
<shadedPattern>${shade.com.google.target}</shadedPattern>

View File

@ -19,35 +19,33 @@
package org.apache.skywalking.apm.agent.core.logging.core;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.util.DaemonThreadFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.skywalking.apm.agent.core.boot.DefaultNamedThreadFactory;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.conf.Constants;
import org.apache.skywalking.apm.util.RunnableWithExceptionProtection;
/**
* The <code>FileWriter</code> support async file output, by using a queue as buffer.
*
* @author wusheng
*/
public class FileWriter implements IWriter, EventHandler<LogMessageHolder> {
public class FileWriter implements IWriter {
private static FileWriter INSTANCE;
private static final Object CREATE_LOCK = new Object();
private Disruptor<LogMessageHolder> disruptor;
private RingBuffer<LogMessageHolder> buffer;
private FileOutputStream fileOutputStream;
private volatile boolean started = false;
private ArrayBlockingQueue logBuffer;
private volatile int fileSize;
private volatile int lineNum;
public static FileWriter get() {
if (INSTANCE == null) {
@ -61,44 +59,45 @@ public class FileWriter implements IWriter, EventHandler<LogMessageHolder> {
}
private FileWriter() {
disruptor = new Disruptor<LogMessageHolder>(new EventFactory<LogMessageHolder>() {
@Override
public LogMessageHolder newInstance() {
return new LogMessageHolder();
logBuffer = new ArrayBlockingQueue(1024);
final ArrayList<String> outputLogs = new ArrayList<String>(200);
Executors
.newSingleThreadScheduledExecutor(new DefaultNamedThreadFactory("LogFileWriter"))
.scheduleAtFixedRate(new RunnableWithExceptionProtection(new Runnable() {
@Override public void run() {
logBuffer.drainTo(outputLogs);
for (String log : outputLogs) {
writeToFile(log + Constants.LINE_SEPARATOR);
}
try {
fileOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}, new RunnableWithExceptionProtection.CallbackWhenException() {
@Override public void handle(Throwable t) {
}
}
}, 1024, DaemonThreadFactory.INSTANCE);
disruptor.handleEventsWith(this);
buffer = disruptor.getRingBuffer();
lineNum = 0;
disruptor.start();
), 0, 1, TimeUnit.SECONDS);
}
@Override
public void onEvent(LogMessageHolder event, long sequence, boolean endOfBatch) throws Exception {
if (hasWriteStream()) {
/**
* @param message to be written into the file.
*/
private void writeToFile(String message) {
if (prepareWriteStream()) {
try {
lineNum++;
write(event.getMessage() + Constants.LINE_SEPARATOR, endOfBatch);
fileOutputStream.write(message.getBytes());
fileSize += message.length();
} catch (IOException e) {
e.printStackTrace();
} finally {
event.setMessage(null);
switchFile();
}
}
}
private void write(String message, boolean forceFlush) {
try {
fileOutputStream.write(message.getBytes());
fileSize += message.length();
if (forceFlush || lineNum % 20 == 0) {
fileOutputStream.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
switchFile();
}
}
private void switchFile() {
if (fileSize > Config.Logging.MAX_FILE_SIZE) {
forceExecute(new Callable() {
@ -128,7 +127,6 @@ public class FileWriter implements IWriter, EventHandler<LogMessageHolder> {
@Override
public Object call() throws Exception {
fileOutputStream = null;
started = false;
return null;
}
});
@ -143,37 +141,40 @@ public class FileWriter implements IWriter, EventHandler<LogMessageHolder> {
}
}
private boolean hasWriteStream() {
/**
* @return true if stream is prepared ready.
*/
private boolean prepareWriteStream() {
if (fileOutputStream != null) {
return true;
}
if (!started) {
File logFilePath = new File(Config.Logging.DIR);
if (!logFilePath.exists()) {
logFilePath.mkdirs();
} else if (!logFilePath.isDirectory()) {
System.err.println("Log dir(" + Config.Logging.DIR + ") is not a directory.");
}
try {
fileOutputStream = new FileOutputStream(new File(logFilePath, Config.Logging.FILE_NAME), true);
fileSize = Long.valueOf(new File(logFilePath, Config.Logging.FILE_NAME).length()).intValue();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
started = true;
File logFilePath = new File(Config.Logging.DIR);
if (!logFilePath.exists()) {
logFilePath.mkdirs();
} else if (!logFilePath.isDirectory()) {
System.err.println("Log dir(" + Config.Logging.DIR + ") is not a directory.");
}
try {
fileOutputStream = new FileOutputStream(new File(logFilePath, Config.Logging.FILE_NAME), true);
fileSize = Long.valueOf(new File(logFilePath, Config.Logging.FILE_NAME).length()).intValue();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return fileOutputStream != null;
}
@Override
public void write(String message) {
long next = buffer.next();
/**
* Write log to the queue.
* W/ performance trade off, set 2ms timeout for the log OP.
*
* @param message to log
*/
@Override public void write(String message) {
try {
LogMessageHolder messageHolder = buffer.get(next);
messageHolder.setMessage(message);
} finally {
buffer.publish(next);
logBuffer.offer(message, 2, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.agent.core.plugin;
/**
* All ByteBuddy core classes required to expose, including open edge for JDK 9+ module, or Bootstrap instrumentation.
*
* @author wusheng
*/
public class ByteBuddyCoreClasses {
private static final String SHADE_PACKAGE = "org.apache.skywalking.apm.dependencies.";
public static final String[] CLASSES = {
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.RuntimeType",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.This",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.AllArguments",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.AllArguments$Assignment",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.SuperCall",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.Origin",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.Morph",
};
}

View File

@ -35,12 +35,14 @@ import net.bytebuddy.pool.TypePool;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
import org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.ByteBuddyCoreClasses;
import org.apache.skywalking.apm.agent.core.plugin.InstrumentDebuggingClass;
import org.apache.skywalking.apm.agent.core.plugin.PluginException;
import org.apache.skywalking.apm.agent.core.plugin.PluginFinder;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.jdk9module.JDK9ModuleExporter;
import org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader;
import static net.bytebuddy.matcher.ElementMatchers.named;
@ -53,32 +55,26 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
*/
public class BootstrapInstrumentBoost {
private static final ILog logger = LogManager.getLogger(BootstrapInstrumentBoost.class);
private static final String SHADE_PACKAGE = "org.apache.skywalking.apm.dependencies.";
private static final String[] HIGH_PRIORITY_CLASSES = {
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable",
"org.apache.skywalking.apm.agent.core.plugin.bootstrap.IBootstrapLog",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.RuntimeType",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.This",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.AllArguments",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.AllArguments$Assignment",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.SuperCall",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.Origin",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.Morph"
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult"
};
private static String INSTANCE_METHOD_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.InstanceMethodInterTemplate";
private static String INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.InstanceMethodInterWithOverrideArgsTemplate";
private static String CONSTRUCTOR_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.ConstructorInterTemplate";
private static String STATIC_METHOD_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.StaticMethodInterTemplate";
private static String STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.StaticMethodInterWithOverrideArgsTemplate";
public static AgentBuilder inject(PluginFinder pluginFinder, AgentBuilder agentBuilder,
Instrumentation instrumentation) throws PluginException {
public static AgentBuilder inject(PluginFinder pluginFinder, Instrumentation instrumentation, AgentBuilder agentBuilder,
JDK9ModuleExporter.EdgeClasses edgeClasses) throws PluginException {
Map<String, byte[]> classesTypeMap = new HashMap<String, byte[]>();
if (!prepareJREInstrumentation(pluginFinder, classesTypeMap)) {
@ -88,36 +84,25 @@ public class BootstrapInstrumentBoost {
for (String highPriorityClass : HIGH_PRIORITY_CLASSES) {
loadHighPriorityClass(classesTypeMap, highPriorityClass);
}
for (String highPriorityClass : ByteBuddyCoreClasses.CLASSES) {
loadHighPriorityClass(classesTypeMap, highPriorityClass);
}
/**
* Prepare to open edge of necessary classes.
*/
for (String generatedClass : classesTypeMap.keySet()) {
edgeClasses.add(generatedClass);
}
/**
* Inject the classes into bootstrap class loader by using Unsafe Strategy.
* ByteBuddy adapts the sun.misc.Unsafe and jdk.internal.misc.Unsafe automatically.
*/
ClassInjector.UsingUnsafe.ofBootLoader().injectRaw(classesTypeMap);
agentBuilder = agentBuilder.enableUnsafeBootstrapInjection();
ClassInjector.UsingUnsafe.Factory factory = ClassInjector.UsingUnsafe.Factory.resolve(instrumentation);
factory.make(null, null).injectRaw(classesTypeMap);
agentBuilder = agentBuilder.with(new AgentBuilder.InjectionStrategy.UsingUnsafe.OfFactory(factory));
/**
* Assures that all modules of the supplied types are read by the module of any instrumented type.
* JDK Module system was introduced since JDK9.
*
* The following codes work only JDK Module system exist.
*/
for (String highPriorityClass : HIGH_PRIORITY_CLASSES) {
try {
agentBuilder = agentBuilder.assureReadEdgeTo(instrumentation, Class.forName(highPriorityClass));
} catch (ClassNotFoundException e) {
logger.error(e, "Fail to open the high priority class " + highPriorityClass + " to public access in JDK9+");
throw new UnsupportedOperationException("Fail to open the high priority class " + highPriorityClass + " to public access in JDK9+", e);
}
}
for (String generatedClass : classesTypeMap.keySet()) {
try {
agentBuilder = agentBuilder.assureReadEdgeTo(instrumentation, Class.forName(generatedClass));
} catch (ClassNotFoundException e) {
logger.error(e, "Fail to open the high generated class " + generatedClass + " to public access in JDK9+");
throw new UnsupportedOperationException("Fail to open the high generated class " + generatedClass + " to public access in JDK9+", e);
}
}
return agentBuilder;
}

View File

@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.agent.core.plugin.jdk9module;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.List;
import net.bytebuddy.agent.builder.AgentBuilder;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
import org.apache.skywalking.apm.agent.core.plugin.ByteBuddyCoreClasses;
/**
* Since JDK 9, module concept has been introduced. By supporting that, agent core needs to open the
*
* @author wusheng
*/
public class JDK9ModuleExporter {
private static final ILog logger = LogManager.getLogger(JDK9ModuleExporter.class);
private static final String[] HIGH_PRIORITY_CLASSES = {
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ConstructorInter",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInterWithOverrideArgs",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsInter",
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsInterWithOverrideArgs",
};
/**
* Assures that all modules of the supplied types are read by the module of any instrumented type. JDK Module system
* was introduced since JDK9.
*
* The following codes work only JDK Module system exist.
*/
public static AgentBuilder openReadEdge(Instrumentation instrumentation, AgentBuilder agentBuilder,
EdgeClasses classes) {
for (String className : classes.classes) {
try {
agentBuilder = agentBuilder.assureReadEdgeFromAndTo(instrumentation, Class.forName(className));
} catch (ClassNotFoundException e) {
throw new UnsupportedOperationException("Fail to open read edge for class " + className + " to public access in JDK9+", e);
}
}
for (String className : HIGH_PRIORITY_CLASSES) {
try {
agentBuilder = agentBuilder.assureReadEdgeFromAndTo(instrumentation, Class.forName(className));
} catch (ClassNotFoundException e) {
throw new UnsupportedOperationException("Fail to open read edge for class " + className + " to public access in JDK9+", e);
}
}
return agentBuilder;
}
public static class EdgeClasses {
private List<String> classes = new ArrayList<String>();
public EdgeClasses() {
for (String className : ByteBuddyCoreClasses.CLASSES) {
add(className);
}
}
public void add(String className) {
if (!classes.contains(className)) {
classes.add(className);
}
}
}
}

View File

@ -76,7 +76,6 @@
</transformers>
<artifactSet>
<excludes>
<exclude>com.lmax:*</exclude>
<exclude>*:gson</exclude>
<exclude>io.grpc:*</exclude>
<exclude>io.netty:*</exclude>

View File

@ -44,6 +44,7 @@ import org.apache.skywalking.apm.agent.core.plugin.PluginBootstrap;
import org.apache.skywalking.apm.agent.core.plugin.PluginException;
import org.apache.skywalking.apm.agent.core.plugin.PluginFinder;
import org.apache.skywalking.apm.agent.core.plugin.bootstrap.BootstrapInstrumentBoost;
import org.apache.skywalking.apm.agent.core.plugin.jdk9module.JDK9ModuleExporter;
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
@ -97,13 +98,21 @@ public class SkyWalkingAgent {
.or(allSkyWalkingAgentExcludeToolkit())
.or(ElementMatchers.<TypeDescription>isSynthetic()));
JDK9ModuleExporter.EdgeClasses edgeClasses = new JDK9ModuleExporter.EdgeClasses();
try {
agentBuilder = BootstrapInstrumentBoost.inject(pluginFinder, agentBuilder, instrumentation);
agentBuilder = BootstrapInstrumentBoost.inject(pluginFinder, instrumentation, agentBuilder, edgeClasses);
} catch (Exception e) {
logger.error(e, "SkyWalking agent inject bootstrap instrumentation failure. Shutting down.");
return;
}
try {
agentBuilder = JDK9ModuleExporter.openReadEdge(instrumentation, agentBuilder, edgeClasses);
} catch (Exception e) {
logger.error(e, "SkyWalking agent open read edge in JDK 9+ failure. Shutting down.");
return;
}
agentBuilder
.type(pluginFinder.buildMatch())
.transform(new Transformer(pluginFinder))