new feature add witness method (#6103)
https://lists.apache.org/thread.html/r4f67a7ed880a493e4e82bd2e9a0c26a6e43c37bbfcdaebe7b1a340dc%40%3Cdev.skywalking.apache.org%3E
This commit is contained in:
parent
4acbca0053
commit
e0a4c44036
|
|
@ -23,6 +23,7 @@ Release Notes.
|
|||
* Fix DataCarrier's `org.apache.skywalking.apm.commons.datacarrier.buffer.Buffer` implementation isn't activated in `IF_POSSIBLE` mode.
|
||||
* Fix ArrayBlockingQueueBuffer's useless `IF_POSSIBLE` mode list
|
||||
* Support building gRPC TLS channel but CA file is not required.
|
||||
* Add witness method mechanism in the agent plugin core.
|
||||
* Add Dolphinscheduler plugin definition.
|
||||
* Make sampling still works when the trace ignores plug-in activation.
|
||||
* Fix mssql-plugin occur ClassCastException when call the method of return generate key.
|
||||
|
|
|
|||
|
|
@ -27,8 +27,11 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsIn
|
|||
import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
|
||||
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine;
|
||||
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
|
||||
import org.apache.skywalking.apm.agent.core.util.CollectionUtil;
|
||||
import org.apache.skywalking.apm.util.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Basic abstract class of all sky-walking auto-instrumentation plugins.
|
||||
* <p>
|
||||
|
|
@ -57,19 +60,28 @@ public abstract class AbstractClassEnhancePluginDefine {
|
|||
}
|
||||
|
||||
LOGGER.debug("prepare to enhance class {} by {}.", transformClassName, interceptorDefineClassName);
|
||||
|
||||
WitnessFinder finder = WitnessFinder.INSTANCE;
|
||||
/**
|
||||
* find witness classes for enhance class
|
||||
*/
|
||||
String[] witnessClasses = witnessClasses();
|
||||
if (witnessClasses != null) {
|
||||
for (String witnessClass : witnessClasses) {
|
||||
if (!WitnessClassFinder.INSTANCE.exist(witnessClass, classLoader)) {
|
||||
if (!finder.exist(witnessClass, classLoader)) {
|
||||
LOGGER.warn("enhance class {} by plugin {} is not working. Because witness class {} is not existed.", transformClassName, interceptorDefineClassName, witnessClass);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
List<WitnessMethod> witnessMethods = witnessMethods();
|
||||
if (!CollectionUtil.isEmpty(witnessMethods)) {
|
||||
for (WitnessMethod witnessMethod : witnessMethods) {
|
||||
if (!finder.exist(witnessMethod, classLoader)) {
|
||||
LOGGER.warn("enhance class {} by plugin {} is not working. Because witness method {} is not existed.", transformClassName, interceptorDefineClassName, witnessMethod);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* find origin class source code for interceptor
|
||||
|
|
@ -104,6 +116,10 @@ public abstract class AbstractClassEnhancePluginDefine {
|
|||
return new String[] {};
|
||||
}
|
||||
|
||||
protected List<WitnessMethod> witnessMethods() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isBootstrapInstrumentation() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,24 +18,36 @@
|
|||
|
||||
package org.apache.skywalking.apm.agent.core.plugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The <code>WitnessClassFinder</code> represents a pool of {@link TypePool}s, each {@link TypePool} matches a {@link
|
||||
* ClassLoader}, which helps to find the class define existed or not.
|
||||
* The <code>WitnessFinder</code> represents a pool of {@link TypePool}s, each {@link TypePool} matches a {@link
|
||||
* ClassLoader}, which helps to find the class declaration existed or not.
|
||||
*/
|
||||
public enum WitnessClassFinder {
|
||||
public enum WitnessFinder {
|
||||
INSTANCE;
|
||||
|
||||
private Map<ClassLoader, TypePool> poolMap = new HashMap<ClassLoader, TypePool>();
|
||||
private final Map<ClassLoader, TypePool> poolMap = new HashMap<ClassLoader, TypePool>();
|
||||
|
||||
/**
|
||||
* @param classLoader for finding the witnessClass
|
||||
* @return true, if the given witnessClass exists, through the given classLoader.
|
||||
*/
|
||||
public boolean exist(String witnessClass, ClassLoader classLoader) {
|
||||
return getResolution(witnessClass, classLoader)
|
||||
.isResolved();
|
||||
}
|
||||
|
||||
/**
|
||||
* get TypePool.Resolution of the witness class
|
||||
* @param witnessClass class name
|
||||
* @param classLoader classLoader for finding the witnessClass
|
||||
* @return TypePool.Resolution
|
||||
*/
|
||||
private TypePool.Resolution getResolution(String witnessClass, ClassLoader classLoader) {
|
||||
ClassLoader mappingKey = classLoader == null ? NullClassLoader.INSTANCE : classLoader;
|
||||
if (!poolMap.containsKey(mappingKey)) {
|
||||
synchronized (poolMap) {
|
||||
|
|
@ -46,9 +58,24 @@ public enum WitnessClassFinder {
|
|||
}
|
||||
}
|
||||
TypePool typePool = poolMap.get(mappingKey);
|
||||
TypePool.Resolution witnessClassResolution = typePool.describe(witnessClass);
|
||||
return witnessClassResolution.isResolved();
|
||||
return typePool.describe(witnessClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classLoader for finding the witness method
|
||||
* @return true, if the given witness method exists, through the given classLoader.
|
||||
*/
|
||||
public boolean exist(WitnessMethod witnessMethod, ClassLoader classLoader) {
|
||||
TypePool.Resolution resolution = getResolution(witnessMethod.getDeclaringClassName(), classLoader);
|
||||
if (!resolution.isResolved()) {
|
||||
return false;
|
||||
}
|
||||
return !resolution.resolve()
|
||||
.getDeclaredMethods()
|
||||
.filter(witnessMethod.getElementMatcher())
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final class NullClassLoader extends ClassLoader {
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
/**
|
||||
* Witness Method for plugin activation
|
||||
*/
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public class WitnessMethod {
|
||||
|
||||
/**
|
||||
* the class or interface name where the witness method is declared.
|
||||
*/
|
||||
@Getter
|
||||
private final String declaringClassName;
|
||||
/**
|
||||
* matcher to match the witness method
|
||||
*/
|
||||
@Getter
|
||||
private final ElementMatcher<? super MethodDescription.InDefinedShape> elementMatcher;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.witness;
|
||||
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import org.apache.skywalking.apm.agent.core.plugin.WitnessFinder;
|
||||
import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* unit test for WitnessFinder
|
||||
*/
|
||||
public class WitnessTest {
|
||||
|
||||
private final String className = "org.apache.skywalking.apm.agent.core.plugin.witness.WitnessTest";
|
||||
|
||||
private final WitnessFinder finder = WitnessFinder.INSTANCE;
|
||||
|
||||
@Test
|
||||
public void testWitnessClass() {
|
||||
Assert.assertTrue(finder.exist(className, this.getClass().getClassLoader()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWitnessMethod() {
|
||||
ElementMatcher.Junction<MethodDescription> junction = ElementMatchers.named("foo")
|
||||
.and(ElementMatchers.returnsGeneric(target -> "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>".equals(target.getTypeName())))
|
||||
.and(ElementMatchers.takesGenericArgument(0, target -> "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>".equals(target.getTypeName())))
|
||||
.and(ElementMatchers.takesArgument(1, target -> "java.lang.String".equals(target.getName())));
|
||||
WitnessMethod witnessMethod = new WitnessMethod(className, junction);
|
||||
Assert.assertTrue(finder.exist(witnessMethod, this.getClass().getClassLoader()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWitnessMethodOnlyUsingName() {
|
||||
ElementMatcher.Junction<MethodDescription> junction = ElementMatchers.named("foo");
|
||||
WitnessMethod witnessMethod = new WitnessMethod(className, junction);
|
||||
Assert.assertTrue(finder.exist(witnessMethod, this.getClass().getClassLoader()));
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> foo(List<Map<String, Object>> param, String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -299,6 +299,31 @@ The following sections will tell you how to implement the interceptor.
|
|||
tomcat-7.x/8.x=TomcatInstrumentation
|
||||
```
|
||||
|
||||
4. Set up `witnessClasses` and/or `witnessMethods` if the instrumentation should be activated in specific versions.
|
||||
|
||||
Example:
|
||||
|
||||
```java
|
||||
// The plugin is activated only when the foo.Bar class exists.
|
||||
@Override
|
||||
protected String[] witnessClasses() {
|
||||
return new String[] {
|
||||
"foo.Bar"
|
||||
};
|
||||
}
|
||||
|
||||
// The plugin is activated only when the foo.Bar#hello method exists.
|
||||
@Override
|
||||
protected List<WitnessMethod> witnessMethods() {
|
||||
List<WitnessMethod> witnessMethodList = new ArrayList<>();
|
||||
WitnessMethod witnessMethod = new WitnessMethod("foo.Bar", ElementMatchers.named("hello"));
|
||||
witnessMethodList.add(witnessMethod);
|
||||
return witnessMethodList;
|
||||
}
|
||||
```
|
||||
For more example, see [WitnessTest.java](/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/plugin/witness/WitnessTest.java)
|
||||
|
||||
|
||||
|
||||
### Implement an interceptor
|
||||
As an interceptor for an instance method, the interceptor implements
|
||||
|
|
|
|||
Loading…
Reference in New Issue