Add returnedObj expression support for apm-customize-enhance-plugin (#83)

* issue #8292, add returnedObj expression support for apm-customize-enhance-plugin

* update apm-customize-enhance-plugin use plugin V2 API
This commit is contained in:
Daniel Qian 2021-12-14 22:34:25 +08:00 committed by GitHub
parent 2ba2e1964d
commit bc90371daa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 232 additions and 84 deletions

View File

@ -16,6 +16,7 @@ Release Notes.
* Support 2.8 and 2.9 of pulsar client.
* Add dubbo 3.x plugin.
* Fix TracePathMatcher should match pattern "**" with paths end by "/"
* Add support `returnedObj` expression for apm-customize-enhance-plugin
#### Documentation

View File

@ -16,9 +16,8 @@
*
*/
package org.apache.skywalking.apm.plugin.customize.util;
package org.apache.skywalking.apm.agent.core.util;
import org.apache.skywalking.apm.agent.core.util.CustomizeExpression;
import org.junit.Assert;
import org.junit.Test;

View File

@ -21,8 +21,8 @@ package org.apache.skywalking.apm.plugin.customize.define;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
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.enhance.ClassInstanceMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassInstanceMethodsEnhancePluginDefineV2;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch;
import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration;
@ -31,7 +31,7 @@ import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration;
* The instance of customize instrumentation.
*/
public class CustomizeInstanceInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
public class CustomizeInstanceInstrumentation extends ClassInstanceMethodsEnhancePluginDefineV2 {
private String enhanceClass;
@ -45,20 +45,20 @@ public class CustomizeInstanceInstrumentation extends ClassInstanceMethodsEnhanc
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() {
final ElementMatcher matcher = CustomizeConfiguration.INSTANCE.getInterceptPoints(enhanceClass, false);
if (matcher == null) {
return new InstanceMethodsInterceptPoint[0];
return new InstanceMethodsInterceptV2Point[0];
} else {
return new InstanceMethodsInterceptPoint[] {
new InstanceMethodsInterceptPoint() {
return new InstanceMethodsInterceptV2Point[] {
new InstanceMethodsInterceptV2Point() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return matcher;
}
@Override
public String getMethodsInterceptor() {
public String getMethodsInterceptorV2() {
return "org.apache.skywalking.apm.plugin.customize.interceptor.CustomizeInstanceInterceptor";
}

View File

@ -20,8 +20,8 @@ package org.apache.skywalking.apm.plugin.customize.define;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassStaticMethodsEnhancePluginDefineV2;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch;
import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration;
@ -30,7 +30,7 @@ import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration;
* The static of customize instrumentation.
*/
public class CustomizeStaticInstrumentation extends ClassStaticMethodsEnhancePluginDefine {
public class CustomizeStaticInstrumentation extends ClassStaticMethodsEnhancePluginDefineV2 {
private String enhanceClass;
public CustomizeStaticInstrumentation(String enhanceClass) {
@ -38,20 +38,20 @@ public class CustomizeStaticInstrumentation extends ClassStaticMethodsEnhancePlu
}
@Override
public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
public StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points() {
final ElementMatcher matcher = CustomizeConfiguration.INSTANCE.getInterceptPoints(enhanceClass, true);
if (matcher == null) {
return new StaticMethodsInterceptPoint[0];
return new StaticMethodsInterceptV2Point[0];
} else {
return new StaticMethodsInterceptPoint[] {
new StaticMethodsInterceptPoint() {
return new StaticMethodsInterceptV2Point[] {
new StaticMethodsInterceptV2Point() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return matcher;
}
@Override
public String getMethodsInterceptor() {
public String getMethodsInterceptorV2() {
return "org.apache.skywalking.apm.plugin.customize.interceptor.CustomizeStaticInterceptor";
}

View File

@ -18,75 +18,167 @@
package org.apache.skywalking.apm.plugin.customize.interceptor;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration;
import org.apache.skywalking.apm.plugin.customize.conf.MethodConfiguration;
import org.apache.skywalking.apm.plugin.customize.constants.Constants;
import org.apache.skywalking.apm.agent.core.util.CustomizeExpression;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
import org.apache.skywalking.apm.agent.core.util.CustomizeExpression;
import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration;
import org.apache.skywalking.apm.plugin.customize.conf.MethodConfiguration;
import org.apache.skywalking.apm.plugin.customize.constants.Constants;
class BaseInterceptorMethods {
void beforeMethod(Method method, Object[] allArguments) {
private static class SpanDataHolder {
final AbstractSpan localSpan;
final Map<String, String> tags;
final Map<String, String> logs;
final Map<String, String> spanTags;
final Map<String, String> spanLogs;
public SpanDataHolder(AbstractSpan localSpan, Map<String, String> tags,
Map<String, String> logs, Map<String, String> spanTags, Map<String, String> spanLogs) {
this.localSpan = localSpan;
this.tags = tags;
this.logs = logs;
this.spanTags = spanTags;
this.spanLogs = spanLogs;
}
}
void beforeMethod(Method method, Object[] allArguments, MethodInvocationContext miContext) {
Map<String, Object> configuration = CustomizeConfiguration.INSTANCE.getConfiguration(method);
String operationName = MethodConfiguration.getOperationName(configuration);
Map<String, Object> context = CustomizeExpression.evaluationContext(allArguments);
if (context == null || context.isEmpty()) {
ContextManager.createLocalSpan(operationName);
Map<String, Object> evalContext = CustomizeExpression.evaluationContext(allArguments);
Map<String, String> tags = MethodConfiguration.getTags(configuration);
Map<String, String> logs = MethodConfiguration.getLogs(configuration);
Map<String, String> spanTags = tags == null ? Collections.EMPTY_MAP : new HashMap<String, String>(tags.size());
Map<String, String> spanLogs = logs == null ? Collections.EMPTY_MAP : new HashMap<String, String>(logs.size());
if (evalContext == null || evalContext.isEmpty()) {
SpanDataHolder spanDataHolder = new SpanDataHolder(
ContextManager.createLocalSpan(operationName),
tags, logs, spanTags, spanLogs
);
miContext.setContext(spanDataHolder);
} else {
Map<String, String> tags = MethodConfiguration.getTags(configuration);
Map<String, String> spanTags = new HashMap<String, String>();
Map<String, String> logs = MethodConfiguration.getLogs(configuration);
Map<String, String> spanLogs = new HashMap<String, String>();
List<String> operationNameSuffixes = MethodConfiguration.getOperationNameSuffixes(configuration);
StringBuilder operationNameSuffix = new StringBuilder();
if (operationNameSuffixes != null && !operationNameSuffixes.isEmpty()) {
for (String expression : operationNameSuffixes) {
operationNameSuffix.append(Constants.OPERATION_NAME_SEPARATOR);
operationNameSuffix.append(CustomizeExpression.parseExpression(expression, context));
operationNameSuffix.append(CustomizeExpression.parseExpression(expression, evalContext));
}
}
if (tags != null && !tags.isEmpty()) {
for (Map.Entry<String, String> expression : tags.entrySet()) {
spanTags.put(expression.getKey(), CustomizeExpression.parseExpression(expression.getValue(), context));
}
}
if (logs != null && !logs.isEmpty()) {
for (Map.Entry<String, String> entries : logs.entrySet()) {
String expression = logs.get(entries.getKey());
spanLogs.put(entries.getKey(), CustomizeExpression.parseExpression(expression, context));
}
}
operationName = operationNameSuffix.insert(0, operationName).toString();
evalAndPopulate(evalContext, tags, spanTags);
evalAndPopulate(evalContext, logs, spanLogs);
AbstractSpan span = ContextManager.createLocalSpan(operationName);
if (!spanTags.isEmpty()) {
for (Map.Entry<String, String> tag : spanTags.entrySet()) {
span.tag(Tags.ofKey(tag.getKey()), tag.getValue());
}
}
if (!spanLogs.isEmpty()) {
span.log(System.currentTimeMillis(), spanLogs);
}
operationName = operationNameSuffix.insert(0, operationName).toString();
AbstractSpan localSpan = ContextManager.createLocalSpan(operationName);
tagSpanTags(localSpan, spanTags);
tagSpanLogs(localSpan, spanLogs);
spanTags.clear();
spanLogs.clear();
SpanDataHolder spanDataHolder = new SpanDataHolder(
localSpan, tags, logs, spanTags, spanLogs
);
miContext.setContext(spanDataHolder);
}
}
void afterMethod(Method method) {
ContextManager.stopSpan();
void afterMethod(Method method, Object ret, MethodInvocationContext miContext) {
if (!ContextManager.isActive()) {
return;
}
SpanDataHolder spanDataHolder = (SpanDataHolder) miContext.getContext();
if (spanDataHolder == null || spanDataHolder.localSpan == null) {
return;
}
AbstractSpan localSpan = spanDataHolder.localSpan;
if (ret == null) {
ContextManager.stopSpan(localSpan);
return;
}
Map<String, String> tags = spanDataHolder.tags;
Map<String, String> logs = spanDataHolder.logs;
Map<String, String> spanTags = spanDataHolder.spanTags;
Map<String, String> spanLogs = spanDataHolder.spanLogs;
try {
Map<String, Object> evalContext = CustomizeExpression.evaluationReturnContext(ret);
evalReturnAndPopulate(evalContext, tags, spanTags);
evalReturnAndPopulate(evalContext, logs, spanLogs);
tagSpanTags(localSpan, spanTags);
tagSpanLogs(localSpan, spanLogs);
spanTags.clear();
spanLogs.clear();
} finally {
ContextManager.stopSpan(localSpan);
}
}
void handleMethodException(Throwable t) {
if (ContextManager.isActive()) {
ContextManager.activeSpan().log(t);
ContextManager.activeSpan().log(t);
}
private void evalAndPopulate(Map<String, Object> context, Map<String, String> exprMap, Map<String, String> toMap) {
if (exprMap != null && !exprMap.isEmpty()) {
for (Map.Entry<String, String> entry : exprMap.entrySet()) {
String expression = entry.getValue();
if (isReturnedObjExpression(expression)) {
continue;
}
toMap.put(entry.getKey(), CustomizeExpression.parseExpression(expression, context));
}
}
}
private void evalReturnAndPopulate(Map<String, Object> context, Map<String, String> exprMap,
Map<String, String> toMap) {
if (exprMap != null && !exprMap.isEmpty()) {
for (Map.Entry<String, String> entry : exprMap.entrySet()) {
String expression = entry.getValue();
if (!isReturnedObjExpression(expression)) {
continue;
}
toMap.put(entry.getKey(), CustomizeExpression.parseReturnExpression(expression, context));
}
}
}
private void tagSpanTags(AbstractSpan span, Map<String, String> spanTags) {
if (spanTags != null && !spanTags.isEmpty()) {
for (Map.Entry<String, String> tag : spanTags.entrySet()) {
span.tag(Tags.ofKey(tag.getKey()), tag.getValue());
}
}
}
private void tagSpanLogs(AbstractSpan span, Map<String, String> spanLogs) {
if (spanLogs != null && !spanLogs.isEmpty()) {
span.log(System.currentTimeMillis(), spanLogs);
}
}
private boolean isReturnedObjExpression(String expression) {
String[] es = expression.split("\\.");
return "returnedObj".equals(es[0]);
}
}

View File

@ -18,29 +18,29 @@
package org.apache.skywalking.apm.plugin.customize.interceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import java.lang.reflect.Method;
public class CustomizeInstanceInterceptor extends BaseInterceptorMethods implements InstanceMethodsAroundInterceptor {
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
public class CustomizeInstanceInterceptor extends BaseInterceptorMethods implements InstanceMethodsAroundInterceptorV2 {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
super.beforeMethod(method, allArguments);
MethodInvocationContext context) throws Throwable {
super.beforeMethod(method, allArguments, context);
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
super.afterMethod(method);
Object ret, MethodInvocationContext context) throws Throwable {
super.afterMethod(method, ret, context);
return ret;
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
Class<?>[] argumentsTypes, Throwable t, MethodInvocationContext context) {
super.handleMethodException(t);
}
}

View File

@ -18,28 +18,29 @@
package org.apache.skywalking.apm.plugin.customize.interceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor;
import java.lang.reflect.Method;
public class CustomizeStaticInterceptor extends BaseInterceptorMethods implements StaticMethodsAroundInterceptor {
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2;
public class CustomizeStaticInterceptor extends BaseInterceptorMethods implements StaticMethodsAroundInterceptorV2 {
@Override
public void beforeMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
MethodInterceptResult result) {
super.beforeMethod(method, allArguments);
MethodInvocationContext context) {
super.beforeMethod(method, allArguments, context);
}
@Override
public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
Object ret) {
super.afterMethod(method);
Object ret, MethodInvocationContext context) {
super.afterMethod(method, ret, context);
return ret;
}
@Override
public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
Throwable t) {
Throwable t, MethodInvocationContext context) {
super.handleMethodException(t);
}
}

View File

@ -38,6 +38,15 @@ Implementing enhancements to custom classes requires two steps.
<tag key="tag_os">arg[0].os.[1]</tag>
<log key="log_map">arg[0].getM().['k1']</log>
</method>
<method method="retString(java.lang.String)" operation_name="/retString" static="false">
<tag key="tag_ret">returnedObj</tag>
<log key="log_map">returnedObj</log>
</method>
<method method="retModel0(test.apache.skywalking.apm.testcase.customize.model.Model0)"
operation_name="/retModel0" static="false">
<tag key="tag_ret">returnedObj.model1.id</tag>
<log key="log_map">returnedObj.model1.getId()</log>
</method>
</class>
<class class_name="test.apache.skywalking.testcase.customize.service.TestService2">
<method method="staticMethod(java.lang.String,int.class)" operation_name="/is_2_static_method" static="true">
@ -53,7 +62,6 @@ Implementing enhancements to custom classes requires two steps.
</method>
</class>
</enhanced>
```
- Explanation of the configuration in the file
@ -69,4 +77,5 @@ Implementing enhancements to custom classes requires two steps.
| arg[x] | What it means is to get the input arguments. such as arg[0] is means get first arguments. |
| .[x] | When the parsing object is Array or List, you can use it to get the object at the specified index. |
| .['key'] | When the parsing object is Map, you can get the map 'key' through it.|
| returnedObj | What it means is to get the return value. |

View File

@ -20,4 +20,4 @@ home="$(cd "$(dirname $0)"; pwd)"
java -jar ${agent_opts} \
-Dskywalking.plugin.customize.enhance_file=${home}/../config/customize_enhance.xml \
${home}/../libs/customize-scenario.jar &
${home}/../libs/customize-scenario.jar &

View File

@ -43,6 +43,15 @@
<tag key="tag_os">arg[0].os.[1]</tag>
<log key="log_map">arg[0].getM().['k1']</log>
</method>
<method method="retString(java.lang.String)" operation_name="/retString" static="false">
<tag key="tag_ret">returnedObj</tag>
<log key="log_map">returnedObj</log>
</method>
<method method="retModel0(test.apache.skywalking.apm.testcase.customize.model.Model0)"
operation_name="/retModel0" static="false">
<tag key="tag_ret">returnedObj.model1.id</tag>
<log key="log_map">returnedObj.model1.getId()</log>
</method>
</class>
<class class_name="test.apache.skywalking.apm.testcase.customize.service.TestService2">
<method method="staticMethod(java.lang.String,int.class)" operation_name="/is_2_static_method" static="true">
@ -57,4 +66,4 @@
<log key="log_5_1">arg[1]</log>
</method>
</class>
</enhanced>
</enhanced>

View File

@ -110,6 +110,32 @@ segmentItems:
- logEvent:
- {key: log_5_1, value: '123'}
skipAnalysis: 'false'
- operationName: /retString
parentSpanId: 0
spanId: 9
startTime: nq 0
endTime: nq 0
isError: false
spanType: Local
tags:
- {key: tag_ret, value: 'str0'}
logs:
- logEvent:
- {key: log_map, value: 'str0'}
skipAnalysis: 'false'
- operationName: /retModel0
parentSpanId: 0
spanId: 10
startTime: nq 0
endTime: nq 0
isError: false
spanType: Local
tags:
- {key: tag_ret, value: '100'}
logs:
- logEvent:
- {key: log_map, value: '100'}
skipAnalysis: 'false'
- operationName: GET:/case/customize
parentSpanId: -1
spanId: 0

View File

@ -78,6 +78,9 @@ public class CustomizeController {
add("a2");
}}, 123);
testService1.retString("str0");
testService1.retModel0(m0);
LOGGER.info(SUCCESS);
return SUCCESS;
}

View File

@ -44,4 +44,12 @@ public class TestService1 {
public void method(Model0 m0, String str0, int count) {
}
public String retString(String str0) {
return str0;
}
public Model0 retModel0(Model0 m0) {
return m0;
}
}