From 8c3b951785f4d98cd03f4f10d33299424c21a300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8E=E7=8E=89=E6=A1=94?= <769213327@qq.com> Date: Wed, 13 Mar 2019 07:07:45 +0800 Subject: [PATCH] Customize Enhance code of plugin (#2300) * Submit custom enhancement kernel code for the first time. * Submit the code again. * Optimize ClassUtil code. * Submit custom enhancement plugin code for the first time. * To fix CI issue. * To fix CI header Tmp issue * To fix CI Tmp header issue * Customize plugin code submission * Add md file. * Fix some bug. * Fix ci issue * Fix md desc * Fix unapproved license * Fix bug * Fix collection bug. * Fix java8 pair ref issue * Fix CI issue. * Add more detail for expression. * Fix review issue * Fix review issue 2 * Fix review code issue 2 * Fix md issue * Fix bug and op desc md. * Update customize-enhance-trace.md * Update customize-enhance-trace.md * FIx code review issue. --- .../apm/agent/core/conf/Config.java | 15 + .../core/plugin/DynamicPluginLoader.java | 48 +++ .../agent/core/plugin/PluginBootstrap.java | 3 +- .../plugin/loader/InstrumentationLoader.java | 34 ++ .../apm/agent/core/util/MethodUtil.java | 45 +++ .../apm/agent/core/util/MethodUtilTest.java | 37 ++ .../TraceAnnotationMethodInterceptor.java | 16 +- .../customize-enhance-plugin/pom.xml | 33 ++ .../conf/CustomizeConfiguration.java | 330 ++++++++++++++++++ .../customize/conf/MethodConfiguration.java | 150 ++++++++ .../plugin/customize/constants/Constants.java | 84 +++++ .../CustomizeInstanceInstrumentation.java | 80 +++++ .../CustomizeStaticInstrumentation.java | 73 ++++ .../interceptor/BaseInterceptorMethods.java | 100 ++++++ .../CustomizeInstanceInterceptor.java | 47 +++ .../CustomizeStaticInterceptor.java | 46 +++ .../CustomizeInstrumentationLoader.java | 64 ++++ .../customize/util/CustomizeExpression.java | 114 ++++++ .../plugin/customize/util/CustomizeUtil.java | 72 ++++ ...t.core.plugin.loader.InstrumentationLoader | 19 + .../util/CustomizeExpressionTest.java | 173 +++++++++ .../customize/util/CustomizeUtilTest.java | 40 +++ apm-sniffer/optional-plugins/pom.xml | 1 + .../java-agent/Customize-enhance-trace.md | 72 ++++ .../setup/service-agent/java-agent/README.md | 1 + 25 files changed, 1682 insertions(+), 15 deletions(-) create mode 100755 apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/DynamicPluginLoader.java create mode 100755 apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/loader/InstrumentationLoader.java create mode 100644 apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/MethodUtil.java create mode 100644 apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/util/MethodUtilTest.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/pom.xml create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/CustomizeConfiguration.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/MethodConfiguration.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/constants/Constants.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/define/CustomizeInstanceInstrumentation.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/define/CustomizeStaticInstrumentation.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/BaseInterceptorMethods.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/CustomizeInstanceInterceptor.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/CustomizeStaticInterceptor.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/loader/CustomizeInstrumentationLoader.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeExpression.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeUtil.java create mode 100755 apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/resources/META-INF/services/org.apache.skywalking.apm.agent.core.plugin.loader.InstrumentationLoader create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/test/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeExpressionTest.java create mode 100644 apm-sniffer/optional-plugins/customize-enhance-plugin/src/test/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeUtilTest.java create mode 100644 docs/en/setup/service-agent/java-agent/Customize-enhance-trace.md diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java index 1791eb6bf..f973f50e5 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java @@ -23,6 +23,9 @@ import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; import org.apache.skywalking.apm.agent.core.logging.core.LogLevel; import org.apache.skywalking.apm.agent.core.logging.core.WriterFactory; +import java.util.HashMap; +import java.util.Map; + /** * This is the core config in sniffer agent. * @@ -163,5 +166,17 @@ public class Config { */ public static boolean TRACE_DSL = false; } + public static class Customize { + /** + * Custom enhancement class configuration file path, recommended to use an absolute path. + */ + public static String ENHANCE_FILE = ""; + + /** + * Some information after custom enhancements, this configuration is used by the custom enhancement plugin. + * And using Map CONTEXT for avoiding classloader isolation issue. + */ + public static Map CONTEXT = new HashMap(); + } } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/DynamicPluginLoader.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/DynamicPluginLoader.java new file mode 100755 index 000000000..d8f522b0f --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/DynamicPluginLoader.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader; +import org.apache.skywalking.apm.agent.core.plugin.loader.InstrumentationLoader; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +/** + * The plugin can be inserted into the kernel by implementing this spi return PluginDefine list. + * + * @author zhaoyuguang + */ + +public enum DynamicPluginLoader { + + INSTANCE; + + public List load(AgentClassLoader classLoader) { + List all = new ArrayList(); + for (InstrumentationLoader instrumentationLoader : ServiceLoader.load(InstrumentationLoader.class, classLoader)) { + List plugins = instrumentationLoader.load(classLoader); + if (plugins != null && !plugins.isEmpty()) { + all.addAll(plugins); + } + } + return all; + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginBootstrap.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginBootstrap.java index c663b08ab..73dc34f14 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginBootstrap.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginBootstrap.java @@ -16,7 +16,6 @@ * */ - package org.apache.skywalking.apm.agent.core.plugin; import java.net.URL; @@ -78,6 +77,8 @@ public class PluginBootstrap { } } + plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault())); + return plugins; } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/loader/InstrumentationLoader.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/loader/InstrumentationLoader.java new file mode 100755 index 000000000..05614c94d --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/loader/InstrumentationLoader.java @@ -0,0 +1,34 @@ +/* + * 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.loader; + +import org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine; + +import java.util.List; + +/** + * the spi of the InstrumentationLoader. + * + * @author : zhaoyuguang + */ + +public interface InstrumentationLoader { + + List load(AgentClassLoader classLoader); +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/MethodUtil.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/MethodUtil.java new file mode 100644 index 000000000..80260446e --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/MethodUtil.java @@ -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.util; + +import java.lang.reflect.Method; + +/** + * According to the input parameter, + * return the OperationName for the span record, + * It can determine the unique method + * + * @author zhaoyuguang + */ + +public class MethodUtil { + + public static String generateOperationName(Method method) { + StringBuilder operationName = new StringBuilder(method.getDeclaringClass().getName() + "." + method.getName() + "("); + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + operationName.append(parameterTypes[i].getName()); + if (i < (parameterTypes.length - 1)) { + operationName.append(","); + } + } + operationName.append(")"); + return operationName.toString(); + } +} diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/util/MethodUtilTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/util/MethodUtilTest.java new file mode 100644 index 000000000..87fa84d6b --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/util/MethodUtilTest.java @@ -0,0 +1,37 @@ +/* + * 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.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Method; + +/** + * @author zhaoyuguang + */ + +public class MethodUtilTest { + + @Test + public void testClassForName() throws NoSuchMethodException { + Assert.assertTrue(MethodUtil.generateOperationName(MethodUtil.class.getMethod("generateOperationName", Method.class)) + .equals("org.apache.skywalking.apm.agent.core.util.MethodUtil.generateOperationName(java.lang.reflect.Method)")); + } +} diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationMethodInterceptor.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationMethodInterceptor.java index dd5d992ae..d96f5ed74 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationMethodInterceptor.java +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationMethodInterceptor.java @@ -25,6 +25,7 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager; 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 org.apache.skywalking.apm.agent.core.util.MethodUtil; /** * {@link TraceAnnotationMethodInterceptor} create a local span and set the operation name which fetch from @@ -40,25 +41,12 @@ public class TraceAnnotationMethodInterceptor implements InstanceMethodsAroundIn Trace trace = method.getAnnotation(Trace.class); String operationName = trace.operationName(); if (operationName.length() == 0) { - operationName = generateOperationName(method); + operationName = MethodUtil.generateOperationName(method); } ContextManager.createLocalSpan(operationName); } - private String generateOperationName(Method method) { - StringBuilder operationName = new StringBuilder(method.getDeclaringClass().getName() + "." + method.getName() + "("); - Class[] parameterTypes = method.getParameterTypes(); - for (int i = 0; i < parameterTypes.length; i++) { - operationName.append(parameterTypes[i].getName()); - if (i < (parameterTypes.length - 1)) { - operationName.append(","); - } - } - operationName.append(")"); - return operationName.toString(); - } - @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/pom.xml b/apm-sniffer/optional-plugins/customize-enhance-plugin/pom.xml new file mode 100644 index 000000000..197bb855e --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/pom.xml @@ -0,0 +1,33 @@ + + + + + + org.apache.skywalking + optional-plugins + 6.1.0-SNAPSHOT + + 4.0.0 + + apm-customize-enhance-plugin + customize-enhance-plugin + jar + \ No newline at end of file diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/CustomizeConfiguration.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/CustomizeConfiguration.java new file mode 100644 index 000000000..24c9c50de --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/CustomizeConfiguration.java @@ -0,0 +1,330 @@ +/* + * 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.plugin.customize.conf; + +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.conf.Config; +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.interceptor.enhance.ClassEnhancePluginDefine; +import org.apache.skywalking.apm.plugin.customize.constants.Constants; +import org.apache.skywalking.apm.plugin.customize.util.CustomizeUtil; +import org.apache.skywalking.apm.agent.core.util.MethodUtil; +import org.apache.skywalking.apm.util.StringUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.*; + +import static net.bytebuddy.matcher.ElementMatchers.*; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +/** + * The CustomizeConfiguration class is the core class for + * parsing custom enhanced configuration files, + * parsing configuration files, + * and converting content into plugins for loading into the kernel. + * + * @author zhaoyuguang + */ + +public enum CustomizeConfiguration { + + INSTANCE; + + private static final ILog logger = LogManager.getLogger(CustomizeConfiguration.class); + + /** + * The load method is resolver configuration file, + * and parse it to kernel. + */ + public void load() { + try { + parse(resolver()); + } catch (Exception e) { + logger.error("CustomizeConfiguration load fail", e); + } + } + + /** + * Resolver custom enhancement file method total entry. + * + * @return configurations is a bridge resolver method and parse method, + * mainly used for decoupling. + * @throws ParserConfigurationException link {@link ParserConfigurationException} + * @throws IOException link {@link IOException} + * @throws SAXException link {@link SAXException} + */ + private List> resolver() throws ParserConfigurationException, IOException, SAXException { + List> customizeMethods = new ArrayList>(); + File file = new File(Config.Plugin.Customize.ENHANCE_FILE); + if (file.exists() && file.isFile()) { + NodeList classNodeList = resolverFileClassDesc(file); + resolverClassNodeList(classNodeList, customizeMethods); + } + return customizeMethods; + } + + /** + * According to the custom enhancement file, return lass description nodes in the file. + * + * @param file the custom enhanced files + * @return all class description nodes + * @throws ParserConfigurationException link {@link ParserConfigurationException} + * @throws IOException link {@link IOException} + * @throws SAXException link {@link SAXException} + */ + private NodeList resolverFileClassDesc(File file) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(file); + return doc.getElementsByTagName(Constants.XML_ELEMENT_CLASS); + } + + /** + * Resolver all class description nodes to customizeMethods. + * + * @param classNodeList all class description nodes. + * @param customizeMethods of memory address, the element {@link MethodConfiguration}. + */ + private void resolverClassNodeList(NodeList classNodeList, List> customizeMethods) { + for (int ec = 0; ec < classNodeList.getLength(); ec++) { + Node classDesc = classNodeList.item(ec); + NodeList methodNodeList = classDesc.getChildNodes(); + for (int ms = 0; ms < methodNodeList.getLength(); ms++) { + Node methodDesc = methodNodeList.item(ms); + if (methodDesc.getNodeType() == Node.ELEMENT_NODE) { + String className = classDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_CLASS_NAME).getNodeValue(); + Map configuration = resolverMethodNodeDesc(className, methodDesc); + if (configuration != null) { + customizeMethods.add(configuration); + } + } + } + } + } + + /** + * Resolver according to the description of the method under the current class node. + * + * @param className class name. + * @param methodDesc method node. + * @return configurations is a bridge resolver method and parse method, + * mainly used for decoupling. + */ + private Map resolverMethodNodeDesc(String className, Node methodDesc) { + Map configuration = new HashMap(); + if (methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_OPERATION_NAME) != null) { + MethodConfiguration.setOperationName(configuration, methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_OPERATION_NAME).getNodeValue()); + } + if (methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_CLOSE_BEFORE_METHOD) != null) { + MethodConfiguration.setCloseBeforeMethod(configuration, Boolean.valueOf(methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_CLOSE_BEFORE_METHOD).getNodeValue())); + } else { + MethodConfiguration.setCloseBeforeMethod(configuration, false); + } + if (methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_CLOSE_AFTER_METHOD) != null) { + MethodConfiguration.setCloseAfterMethod(configuration, Boolean.valueOf(methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_CLOSE_AFTER_METHOD).getNodeValue())); + } else { + MethodConfiguration.setCloseAfterMethod(configuration, false); + } + if (methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_METHOD_IS_STATIC) != null) { + MethodConfiguration.setStatic(configuration, Boolean.valueOf(methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_METHOD_IS_STATIC).getNodeValue())); + } + setAdvancedField(configuration, methodDesc); + return resolverClassAndMethod(className, + methodDesc.getAttributes().getNamedItem(Constants.XML_ELEMENT_METHOD).getNodeValue(), + configuration); + } + + /** + * Add some private properties of the Advanced method configuration. + * + * @param configuration {@link MethodConfiguration}. + * @param methodNode method node. + */ + private void setAdvancedField(Map configuration, Node methodNode) { + NodeList methodContents = methodNode.getChildNodes(); + for (int mc = 0; mc < methodContents.getLength(); mc++) { + Node methodContentNode = methodContents.item(mc); + if (methodContentNode.getNodeType() == Node.ELEMENT_NODE) { + if (Constants.XML_ELEMENT_OPERATION_NAME_SUFFIX.equals(methodContentNode.getNodeName())) { + MethodConfiguration.addOperationNameSuffixes(configuration, methodContentNode.getTextContent()); + } + if (Constants.XML_ELEMENT_TAG.equals(methodContentNode.getNodeName())) { + MethodConfiguration.addTag(configuration, methodContentNode.getAttributes().getNamedItem(Constants.XML_ELEMENT_KEY).getNodeValue(), methodContentNode.getTextContent()); + } + if (Constants.XML_ELEMENT_LOG.equals(methodContentNode.getNodeName())) { + MethodConfiguration.addLog(configuration, methodContentNode.getAttributes().getNamedItem(Constants.XML_ELEMENT_KEY).getNodeValue(), methodContentNode.getTextContent()); + } + } + } + } + + /** + * Parse class and method, + * if no error log is printed in this JVM, and return null. + * primitive desc impl by {@link CustomizeUtil} + * At the bottom, the default operation name is added. + * + * @param className class name. + * @param methodDesc method desc. + * @param configuration {@link MethodConfiguration}. + * @return configuration of method. + */ + private Map resolverClassAndMethod(String className, String methodDesc, Map configuration) { + try { + int openParen = methodDesc.indexOf(Constants.LEFT_PARENTHESIS); + int closeParen = methodDesc.indexOf(Constants.RIGHT_PARENTHESIS); + String methodName = methodDesc.substring(0, openParen); + String[] arguments = methodDesc.substring(openParen + 1, closeParen).split(Constants.COMMA); + MethodConfiguration.setClz(configuration, className); + MethodConfiguration.setMethod(configuration, CustomizeUtil.generateOperationName(className, methodName, arguments)); + MethodConfiguration.setMethodName(configuration, methodName); + MethodConfiguration.setArguments(configuration, StringUtil.isEmpty(arguments[0]) ? new String[0] : arguments); + if (StringUtil.isEmpty(MethodConfiguration.getOperationName(configuration))) { + MethodConfiguration.setOperationName(configuration, MethodConfiguration.getMethod(configuration)); + } + return configuration; + } catch (Exception e) { + logger.error(e, "Failed to resolver, className is {}, methodDesc is {}.", className, methodDesc); + } + return null; + } + + /** + * Put the plugin configuration into the kernel according to the configuration. + * + * @param configurations is a bridge resolver method and parse method, + * mainly used for decoupling. + */ + private void parse(List> configurations) { + init(); + for (Map configuration : configurations) { + addContextMethodConfiguration(configuration); + addContextEnhanceClass(configuration); + } + } + + /** + * In order to avoid the judgment of the useless null pointer exception. + */ + private void init() { + Config.Plugin.Customize.CONTEXT.put(Constants.CONTEXT_METHOD_CONFIGURATIONS, new HashMap>()); + Config.Plugin.Customize.CONTEXT.put(Constants.CONTEXT_ENHANCE_CLASSES, new HashMap()); + } + + /** + * The configuration of each method is put into the kernel. + * + * @param configuration {@link MethodConfiguration}. + */ + private void addContextMethodConfiguration(Map configuration) { + getMethodConfigurations().put(MethodConfiguration.getMethod(configuration), configuration); + } + + + /** + * The private method for get the configuration of this method. + * + * @return all method configs. + */ + @SuppressWarnings("unchecked") + private Map> getMethodConfigurations() { + return (Map>) Config.Plugin.Customize.CONTEXT.get(Constants.CONTEXT_METHOD_CONFIGURATIONS); + } + + /** + * The configuration of each class is put into the kernel. + * + * @param configuration {@link MethodConfiguration} + */ + private void addContextEnhanceClass(Map configuration) { + String key = CustomizeUtil.generateClassDesc(MethodConfiguration.getClz(configuration), MethodConfiguration.isStatic(configuration)); + HashMap enhanceClasses = getEnhanceClasses(); + ElementMatcher matcher = enhanceClasses.get(key); + enhanceClasses.put(key, matcher == null ? parserMethodsMatcher(configuration) : ((ElementMatcher.Junction) matcher).or(parserMethodsMatcher(configuration))); + } + + /** + * Parse each configuration to matcher. + * + * @param configuration {@link MethodConfiguration}. + * @return matcher {@link ElementMatcher}. + */ + private ElementMatcher parserMethodsMatcher(Map configuration) { + String[] arguments = MethodConfiguration.getArguments(configuration); + ElementMatcher matcher = named(MethodConfiguration.getMethodName(configuration)).and(takesArguments(arguments.length)); + if (arguments.length > 0) { + for (int i = 0; i < arguments.length; i++) { + matcher = ((ElementMatcher.Junction) matcher).and( + CustomizeUtil.isJavaClass(arguments[i]) ? + takesArgument(i, CustomizeUtil.getJavaClass(arguments[i])) : + takesArgumentWithType(i, arguments[i])); + } + } + return matcher; + } + + /** + * Get InterceptPoints, the input dimension is class and is static. + * + * @param enhanceClass Real enhancement class + * @param isStatic Is it static, because static or not, + * logic is different in the SkyWalking kernel, + * so this dimension is abstracted out. + * @return all the interceptPoints. + */ + public ElementMatcher getInterceptPoints(String enhanceClass, boolean isStatic) { + HashMap enhanceClasses = getEnhanceClasses(); + return enhanceClasses.get(CustomizeUtil.generateClassDesc(enhanceClass, isStatic)); + } + + /** + * Get all the instrumentation {@link ClassEnhancePluginDefine} that need custom enhancements. + * + * @return all the custom instrumentation. + */ + public Set getInstrumentations() { + HashMap enhanceClasses = getEnhanceClasses(); + return enhanceClasses.keySet(); + } + + /** + * Get all the private methods of interceptPoints that need custom enhancements. + * + * @return all config of the custom instrumentation. + */ + @SuppressWarnings("unchecked") + private HashMap getEnhanceClasses() { + return (HashMap) Config.Plugin.Customize.CONTEXT.get(Constants.CONTEXT_ENHANCE_CLASSES); + } + + public Map getConfiguration(Method method) { + return getMethodConfigurations().get(MethodUtil.generateOperationName(method)); + } +} + diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/MethodConfiguration.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/MethodConfiguration.java new file mode 100644 index 000000000..07c7442ed --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/MethodConfiguration.java @@ -0,0 +1,150 @@ +/* + * 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.plugin.customize.conf; + +import org.apache.skywalking.apm.plugin.customize.constants.Constants; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Default custom enhancement configuration. + * + * @author zhaoyuguang + */ + +public class MethodConfiguration { + + static String getMethod(Map configuration) { + return (String) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_METHOD); + } + + static String getClz(Map configuration) { + return (String) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_CLZ); + } + + static Boolean isStatic(Map configuration) { + return (Boolean) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_IS_STATIC); + } + + static String getMethodName(Map configuration) { + return (String) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_METHOD_NAME); + } + + static String[] getArguments(Map configuration) { + return (String[]) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_ARGUMENTS); + } + + static void setOperationName(Map configuration, String operationName) { + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_OPERATION_NAME, operationName); + } + + static void setCloseBeforeMethod(Map configuration, Boolean closeBeforeMethod) { + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_CLOSE_BEFORE_METHOD, closeBeforeMethod); + } + + static void setCloseAfterMethod(Map configuration, Boolean closeAfterMethod) { + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_CLOSE_AFTER_METHOD, closeAfterMethod); + } + + static void setStatic(Map configuration, Boolean isStatic) { + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_IS_STATIC, isStatic); + } + + @SuppressWarnings("unchecked") + static void addOperationNameSuffixes(Map configuration, String suffix) { + List suffixes = (List) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_OPERATION_NAME_SUFFIXES); + if (suffixes == null) { + suffixes = new ArrayList(); + suffixes.add(suffix); + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_OPERATION_NAME_SUFFIXES, suffixes); + } else { + suffixes.add(suffix); + } + } + + @SuppressWarnings("unchecked") + static void addTag(Map configuration, String key, String value) { + Map tags = (Map) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_TAGS); + if (tags == null) { + tags = new HashMap(); + tags.put(key, value); + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_TAGS, tags); + } else { + tags.put(key, value); + } + } + + @SuppressWarnings("unchecked") + static void addLog(Map configuration, String key, String value) { + Map logs = (Map) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_LOGS); + if (logs == null) { + logs = new HashMap(); + logs.put(key, value); + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_LOGS, logs); + } else { + logs.put(key, value); + } + } + + static void setClz(Map configuration, String className) { + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_CLZ, className); + } + + static void setMethod(Map configuration, String method) { + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_METHOD, method); + } + + static void setMethodName(Map configuration, String methodName) { + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_METHOD_NAME, methodName); + } + + static void setArguments(Map configuration, String[] arguments) { + configuration.put(Constants.CONFIGURATION_ATTRIBUTE_ARGUMENTS, arguments); + } + + public static String getOperationName(Map configuration) { + return (String) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_OPERATION_NAME); + } + + public static boolean isCloseBeforeMethod(Map configuration) { + return (Boolean) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_CLOSE_BEFORE_METHOD); + } + + public static boolean isCloseAfterMethod(Map configuration) { + return (Boolean) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_CLOSE_AFTER_METHOD); + } + + @SuppressWarnings("unchecked") + public static Map getTags(Map configuration) { + return (Map) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_TAGS); + } + + @SuppressWarnings("unchecked") + public static Map getLogs(Map configuration) { + return (Map) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_LOGS); + } + + @SuppressWarnings("unchecked") + public static List getOperationNameSuffixes(Map configuration) { + return (List) configuration.get(Constants.CONFIGURATION_ATTRIBUTE_OPERATION_NAME_SUFFIXES); + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/constants/Constants.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/constants/Constants.java new file mode 100644 index 000000000..34fe8d9ff --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/constants/Constants.java @@ -0,0 +1,84 @@ +/* + * 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.plugin.customize.constants; + +/** + * The constant of customize enhance. + * + * @author zhaoyuguang + */ + +public class Constants { + + public static final String OPERATION_NAME_SEPARATOR = "/"; + + public static final String COMMA = ","; + + public static final String LEFT_PARENTHESIS = "("; + + public static final String RIGHT_PARENTHESIS = ")"; + + public static final String XML_ELEMENT_CLASS = "class"; + + public static final String XML_ELEMENT_CLASS_NAME = "class_name"; + + public static final String XML_ELEMENT_METHOD = "method"; + + public static final String XML_ELEMENT_METHOD_IS_STATIC = "static"; + + public static final String XML_ELEMENT_OPERATION_NAME = "operation_name"; + + public static final String XML_ELEMENT_CLOSE_BEFORE_METHOD = "close_before_method"; + + public static final String XML_ELEMENT_CLOSE_AFTER_METHOD = "close_after_method"; + + public static final String XML_ELEMENT_OPERATION_NAME_SUFFIX = "operation_name_suffix"; + + public static final String XML_ELEMENT_TAG = "tag"; + + public static final String XML_ELEMENT_LOG = "log"; + + public static final String XML_ELEMENT_KEY = "key"; + + public static final String CONTEXT_METHOD_CONFIGURATIONS = "CONTEXT_METHOD_CONFIGURATIONS"; + + public static final String CONTEXT_ENHANCE_CLASSES = "CONTEXT_ENHANCE_CLASSES"; + + public static final String CONFIGURATION_ATTRIBUTE_METHOD = "CONFIGURATION_ATTRIBUTE_METHOD"; + + public static final String CONFIGURATION_ATTRIBUTE_METHOD_NAME = "CONFIGURATION_ATTRIBUTE_METHOD_NAME"; + + public static final String CONFIGURATION_ATTRIBUTE_ARGUMENTS = "CONFIGURATION_ATTRIBUTE_ARGUMENTS"; + + public static final String CONFIGURATION_ATTRIBUTE_IS_STATIC = "CONFIGURATION_ATTRIBUTE_IS_STATIC"; + + public static final String CONFIGURATION_ATTRIBUTE_CLZ = "CONFIGURATION_ATTRIBUTE_CLZ"; + + public static final String CONFIGURATION_ATTRIBUTE_OPERATION_NAME = "CONFIGURATION_ATTRIBUTE_OPERATION_NAME"; + + public static final String CONFIGURATION_ATTRIBUTE_CLOSE_BEFORE_METHOD = "CONFIGURATION_ATTRIBUTE_CLOSE_BEFORE_METHOD"; + + public static final String CONFIGURATION_ATTRIBUTE_CLOSE_AFTER_METHOD = "CONFIGURATION_ATTRIBUTE_CLOSE_AFTER_METHOD"; + + public static final String CONFIGURATION_ATTRIBUTE_OPERATION_NAME_SUFFIXES = "CONFIGURATION_ATTRIBUTE_OPERATION_NAME_SUFFIXES"; + + public static final String CONFIGURATION_ATTRIBUTE_TAGS = "CONFIGURATION_ATTRIBUTE_TAGS"; + + public static final String CONFIGURATION_ATTRIBUTE_LOGS = "CONFIGURATION_ATTRIBUTE_LOGS"; +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/define/CustomizeInstanceInstrumentation.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/define/CustomizeInstanceInstrumentation.java new file mode 100644 index 000000000..53931b403 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/define/CustomizeInstanceInstrumentation.java @@ -0,0 +1,80 @@ +/* + * 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.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.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; +import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration; + +/** + * The instance of customize instrumentation. + * + * @author zhaoyuguang + */ + +public class CustomizeInstanceInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private String enhanceClass; + + public CustomizeInstanceInstrumentation(String enhanceClass) { + this.enhanceClass = enhanceClass; + } + + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + final ElementMatcher matcher = CustomizeConfiguration.INSTANCE.getInterceptPoints(enhanceClass, false); + if (matcher == null) { + return new InstanceMethodsInterceptPoint[0]; + } else { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return matcher; + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.customize.interceptor.CustomizeInstanceInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(enhanceClass); + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/define/CustomizeStaticInstrumentation.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/define/CustomizeStaticInstrumentation.java new file mode 100644 index 000000000..4fb09f197 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/define/CustomizeStaticInstrumentation.java @@ -0,0 +1,73 @@ +/* + * 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.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.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; +import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration; + +/** + * The static of customize instrumentation. + * + * @author zhaoyuguang + */ + +public class CustomizeStaticInstrumentation extends ClassStaticMethodsEnhancePluginDefine { + private String enhanceClass; + + public CustomizeStaticInstrumentation(String enhanceClass) { + this.enhanceClass = enhanceClass; + } + + @Override + protected StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() { + final ElementMatcher matcher = CustomizeConfiguration.INSTANCE.getInterceptPoints(enhanceClass, true); + if (matcher == null) { + return new StaticMethodsInterceptPoint[0]; + } else { + return new StaticMethodsInterceptPoint[]{ + new StaticMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return matcher; + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.customize.interceptor.CustomizeStaticInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(enhanceClass); + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/BaseInterceptorMethods.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/BaseInterceptorMethods.java new file mode 100644 index 000000000..691d44ec5 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/BaseInterceptorMethods.java @@ -0,0 +1,100 @@ +/* + * 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.plugin.customize.interceptor; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +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.plugin.customize.util.CustomizeExpression; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author zhaoyuguang + */ + +class BaseInterceptorMethods { + + void beforeMethod(Method method, Object[] allArguments) { + Map configuration = CustomizeConfiguration.INSTANCE.getConfiguration(method); + if (!MethodConfiguration.isCloseBeforeMethod(configuration)) { + String operationName = MethodConfiguration.getOperationName(configuration); + Map context = CustomizeExpression.evaluationContext(allArguments); + if (context == null || context.isEmpty()) { + ContextManager.createLocalSpan(operationName); + } else { + + Map tags = MethodConfiguration.getTags(configuration); + Map spanTags = new HashMap(); + Map logs = MethodConfiguration.getLogs(configuration); + Map spanLogs = new HashMap(); + + List 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)); + } + } + if (tags != null && !tags.isEmpty()) { + for (String key : tags.keySet()) { + String expression = tags.get(key); + spanTags.put(key, CustomizeExpression.parseExpression(expression, context)); + } + } + if (logs != null && !logs.isEmpty()) { + for (String key : logs.keySet()) { + String expression = logs.get(key); + spanLogs.put(key, CustomizeExpression.parseExpression(expression, context)); + } + } + operationName = operationNameSuffix.insert(0, operationName).toString(); + + AbstractSpan span = ContextManager.createLocalSpan(operationName); + if (!spanTags.isEmpty()) { + for (Map.Entry tag : spanTags.entrySet()) { + span.tag(tag.getKey(), tag.getValue()); + } + } + if (!spanLogs.isEmpty()) { + span.log(System.currentTimeMillis(), spanLogs); + } + } + } + } + + void afterMethod(Method method) { + if (!MethodConfiguration.isCloseAfterMethod(CustomizeConfiguration.INSTANCE.getConfiguration(method))) { + ContextManager.stopSpan(); + } + } + + void handleMethodException(Throwable t) { + if (ContextManager.activeSpan() != null) { + ContextManager.activeSpan().errorOccurred().log(t); + } + } + +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/CustomizeInstanceInterceptor.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/CustomizeInstanceInterceptor.java new file mode 100644 index 000000000..e9bf07a6c --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/CustomizeInstanceInterceptor.java @@ -0,0 +1,47 @@ +/* + * 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.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; + +/** + * @author zhaoyuguang + */ + +public class CustomizeInstanceInterceptor extends BaseInterceptorMethods implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + super.beforeMethod(method, allArguments); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + super.afterMethod(method); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + super.handleMethodException(t); + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/CustomizeStaticInterceptor.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/CustomizeStaticInterceptor.java new file mode 100644 index 000000000..68975c339 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/interceptor/CustomizeStaticInterceptor.java @@ -0,0 +1,46 @@ +/* + * 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.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; + +/** + * @author zhaoyuguang + */ + +public class CustomizeStaticInterceptor extends BaseInterceptorMethods implements StaticMethodsAroundInterceptor { + @Override + public void beforeMethod(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, MethodInterceptResult result) { + super.beforeMethod(method, allArguments); + } + + @Override + public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, Object ret) { + super.afterMethod(method); + return ret; + } + + @Override + public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, Throwable t) { + super.handleMethodException(t); + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/loader/CustomizeInstrumentationLoader.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/loader/CustomizeInstrumentationLoader.java new file mode 100644 index 000000000..afaf80475 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/loader/CustomizeInstrumentationLoader.java @@ -0,0 +1,64 @@ +/* + * 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.plugin.customize.loader; + +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.loader.AgentClassLoader; +import org.apache.skywalking.apm.agent.core.plugin.loader.InstrumentationLoader; +import org.apache.skywalking.apm.plugin.customize.conf.CustomizeConfiguration; +import org.apache.skywalking.apm.plugin.customize.define.CustomizeInstanceInstrumentation; +import org.apache.skywalking.apm.plugin.customize.define.CustomizeStaticInstrumentation; +import org.apache.skywalking.apm.plugin.customize.util.CustomizeUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * The customize instrumentation plugin loader, + * so implements {@link InstrumentationLoader} + * + * @author zhaoyuguang + */ + +public class CustomizeInstrumentationLoader implements InstrumentationLoader { + + private static final ILog logger = LogManager.getLogger(CustomizeConfiguration.class); + + @Override + public List load(AgentClassLoader classLoader) { + List instrumentations = new ArrayList(); + CustomizeConfiguration.INSTANCE.load(); + Set enhanceClasses = CustomizeConfiguration.INSTANCE.getInstrumentations(); + try { + for (String enhanceClass : enhanceClasses) { + String[] classDesc = CustomizeUtil.getClassDesc(enhanceClass); + AbstractClassEnhancePluginDefine plugin = (AbstractClassEnhancePluginDefine) Class.forName( + Boolean.valueOf(classDesc[1]) ? CustomizeStaticInstrumentation.class.getName() : CustomizeInstanceInstrumentation.class.getName(), + true, classLoader).getConstructor(String.class).newInstance(classDesc[0]); + instrumentations.add(plugin); + } + } catch (Exception e) { + logger.error(e, "InstrumentationLoader loader is error, spi loader is {}", CustomizeInstrumentationLoader.class.getName()); + } + return instrumentations; + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeExpression.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeExpression.java new file mode 100644 index 000000000..66627cc60 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeExpression.java @@ -0,0 +1,114 @@ +/* + * 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.plugin.customize.util; + +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * a simple parsing expression + * + * @author zhaoyuguang + */ + +public class CustomizeExpression { + + private static final ILog logger = LogManager.getLogger(CustomizeExpression.class); + + public static Map evaluationContext(Object[] allArguments) { + Map context = new HashMap(); + for (int i = 0; i < allArguments.length; i++) { + context.put("arg[" + i + "]", allArguments[i]); + } + return context; + } + + public static String parseExpression(String expression, Map context) { + try { + String[] es = expression.split("\\."); + Object o = context.get(es[0]); + return o == null ? "null" : String.valueOf(parse(es, o, 0)); + } catch (Exception e) { + logger.debug("parse expression error, expression is {}, exception is {}", expression, e.getMessage()); + } + return "null"; + + } + + private static Object parse(String[] expressions, Object o, int i) { + int next = i + 1; + if (next == expressions.length) { + return o; + } else { + o = parse0(expressions[next], o); + return o == null ? "null" : parse(expressions, o, next); + } + } + + private static Object parse0(String expression, Object o) { + if (o instanceof Map) { + return matcherMap(expression, o); + } else if (o instanceof List) { + return matcherList(expression, o); + } else if (o.getClass().isArray()) { + return matcherArray(expression, o); + } else { + return matcherDefault(expression, o); + } + } + + private static Object matcherMap(String expression, Object o) { + String key = expression.replace("['", "").replace("']", ""); + return ((Map) o).get(key); + } + + private static Object matcherList(String expression, Object o) { + int index = Integer.valueOf(expression.replace("[", "").replace("]", "")); + List l = (List) o; + return l != null && l.size() > index ? l.get(index) : null; + } + + private static Object matcherArray(String expression, Object o) { + int index = Integer.valueOf(expression.replace("[", "").replace("]", "")); + return o != null && Array.getLength(o) > index ? Array.get(o, index) : null; + } + + private static Object matcherDefault(String expression, Object o) { + try { + if (expression.contains("()")) { + Method m = o.getClass().getMethod(expression.replace("()", ""), null); + return m.invoke(o, null); + } else { + Field f = o.getClass().getDeclaredField(expression); + f.setAccessible(true); + return f.get(o); + } + } catch (Exception e) { + logger.debug("matcher default error, expression is {}, object is {}, expression is {}", expression, o, e.getMessage()); + } + return null; + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeUtil.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeUtil.java new file mode 100644 index 000000000..617f58cd3 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeUtil.java @@ -0,0 +1,72 @@ +/* + * 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.plugin.customize.util; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author zhaoyuguang + */ + +public class CustomizeUtil { + + private static final Map JAVA_CLASS = new HashMap(); + + static { + JAVA_CLASS.put("boolean.class", boolean.class); + JAVA_CLASS.put("char.class", char.class); + JAVA_CLASS.put("byte.class", byte.class); + JAVA_CLASS.put("short.class", short.class); + JAVA_CLASS.put("int.class", int.class); + JAVA_CLASS.put("long.class", long.class); + JAVA_CLASS.put("float.class", float.class); + JAVA_CLASS.put("double.class", double.class); + JAVA_CLASS.put("java.util.List", java.util.List.class); + JAVA_CLASS.put("java.util.Map", java.util.Map.class); + } + + public static boolean isJavaClass(String className) { + return JAVA_CLASS.containsKey(className); + } + + public static Class getJavaClass(String className) { + return JAVA_CLASS.get(className); + } + + public static String generateOperationName(String className, String methodName, String[] parameterTypes) { + StringBuilder operationName = new StringBuilder(className + "." + methodName + "("); + for (int i = 0; i < parameterTypes.length; i++) { + operationName.append(CustomizeUtil.isJavaClass(parameterTypes[i]) ? CustomizeUtil.getJavaClass(parameterTypes[i]).getName() : parameterTypes[i]); + if (i < (parameterTypes.length - 1)) { + operationName.append(","); + } + } + operationName.append(")"); + return operationName.toString(); + } + + public static String generateClassDesc(String className, boolean isStatic) { + return className + ":" + isStatic; + } + + public static String[] getClassDesc(String enhanceClass) { + return enhanceClass.split(":"); + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/resources/META-INF/services/org.apache.skywalking.apm.agent.core.plugin.loader.InstrumentationLoader b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/resources/META-INF/services/org.apache.skywalking.apm.agent.core.plugin.loader.InstrumentationLoader new file mode 100755 index 000000000..3c8414ef5 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/resources/META-INF/services/org.apache.skywalking.apm.agent.core.plugin.loader.InstrumentationLoader @@ -0,0 +1,19 @@ +# +# 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. +# +# + +org.apache.skywalking.apm.plugin.customize.loader.CustomizeInstrumentationLoader \ No newline at end of file diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/test/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeExpressionTest.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/test/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeExpressionTest.java new file mode 100644 index 000000000..7911e9db0 --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/test/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeExpressionTest.java @@ -0,0 +1,173 @@ +/* + * 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.plugin.customize.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author zhaoyuguang + */ + +public class CustomizeExpressionTest { + + @Test + public void testExpression() { + Object[] allArguments = init(); + Map context = CustomizeExpression.evaluationContext(allArguments); + Assert.assertTrue("String_test".equals(CustomizeExpression.parseExpression("arg[0]", context))); + Assert.assertTrue("1024".equals(CustomizeExpression.parseExpression("arg[1]", context))); + Assert.assertTrue("v2_1".equals(CustomizeExpression.parseExpression("arg[2].['k2_1']", context))); + Assert.assertTrue("test1".equals(CustomizeExpression.parseExpression("arg[3].[1]", context))); + Assert.assertTrue("null".equals(CustomizeExpression.parseExpression("arg[3].[100]", context))); + Assert.assertTrue("100".equals(CustomizeExpression.parseExpression("arg[4].id", context))); + Assert.assertTrue("sw".equals(CustomizeExpression.parseExpression("arg[4].getName()", context))); + Assert.assertTrue("ext_v_1".equals(CustomizeExpression.parseExpression("arg[4].ext.['ext_k_1']", context))); + Assert.assertTrue("uuid".equals(CustomizeExpression.parseExpression("arg[5].uuid", context))); + Assert.assertTrue("c".equals(CustomizeExpression.parseExpression("arg[5].orderIds.[0]", context))); + Assert.assertTrue("2".equals(CustomizeExpression.parseExpression("arg[5].ids.[2]", context))); + Assert.assertTrue("3".equals(CustomizeExpression.parseExpression("arg[5].ids.[1]", context))); + Assert.assertTrue("open_id".equals(CustomizeExpression.parseExpression("arg[5].openId", context))); + Assert.assertTrue("ext_v_2".equals(CustomizeExpression.parseExpression("arg[5].user.ext.['ext_k_2']", context))); + } + + private static Object[] init() { + Object[] allArguments = new Object[6]; + allArguments[0] = "String_test"; + allArguments[1] = 1024; + Map m0 = new HashMap(); + m0.put("k2_1", "v2_1"); + allArguments[2] = m0; + List l0 = new ArrayList(); + l0.add("test0"); + l0.add("test1"); + allArguments[3] = l0; + Map m1 = new HashMap(); + m1.put("ext_k_1", "ext_v_1"); + allArguments[4] = new User(100, "sw", m1); + Map m2 = new HashMap(); + m2.put("ext_k_2", "ext_v_2"); + User user2 = new User(101, "sw0", m2); + List l1 = new ArrayList(); + l1.add("c"); + Order order = new Order(999, "uuid", l1, user2, "open_id", new Object[]{0, 3, "2"}); + allArguments[5] = order; + return allArguments; + } + + static class Order { + public Order(int id, String uuid, List orderIds, User user, String openId, Object[] ids) { + this.id = id; + this.uuid = uuid; + this.orderIds = orderIds; + this.user = user; + this.openId = openId; + this.ids = ids; + } + + private int id; + private String uuid; + private List orderIds; + private User user; + public String openId; + private Object[] ids; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public List getOrderIds() { + return orderIds; + } + + public void setOrderIds(List orderIds) { + this.orderIds = orderIds; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Object[] getIds() { + return ids; + } + + public void setIds(Object[] ids) { + this.ids = ids; + } + } + + static class User { + + public User(int id, String name, Map ext) { + this.id = id; + this.name = name; + this.ext = ext; + } + + private int id; + private String name; + private Map ext; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getExt() { + return ext; + } + + public void setExt(Map ext) { + this.ext = ext; + } + } +} diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/test/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeUtilTest.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/test/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeUtilTest.java new file mode 100644 index 000000000..9e7ea1abf --- /dev/null +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/test/java/org/apache/skywalking/apm/plugin/customize/util/CustomizeUtilTest.java @@ -0,0 +1,40 @@ +/* + * 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.plugin.customize.util; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author zhaoyuguang + */ + +public class CustomizeUtilTest { + + @Test + public void testClassForName() throws ClassNotFoundException { + Assert.assertTrue(CustomizeUtil.isJavaClass("boolean.class")); + Assert.assertTrue(CustomizeUtil.isJavaClass("char.class")); + Assert.assertTrue(CustomizeUtil.getJavaClass("byte.class") == byte.class); + Assert.assertTrue(CustomizeUtil.getJavaClass("short.class") == short.class); + Assert.assertTrue(CustomizeUtil.getJavaClass("int.class") == int.class); + Assert.assertTrue(CustomizeUtil.getJavaClass("long.class") == long.class); + Assert.assertTrue(CustomizeUtil.getJavaClass("float.class") == float.class); + } +} diff --git a/apm-sniffer/optional-plugins/pom.xml b/apm-sniffer/optional-plugins/pom.xml index be50e205b..4140e6fe5 100644 --- a/apm-sniffer/optional-plugins/pom.xml +++ b/apm-sniffer/optional-plugins/pom.xml @@ -46,6 +46,7 @@ gson-2.8.x-plugin lettuce-5.x-plugin zookeeper-3.4.x-plugin + customize-enhance-plugin diff --git a/docs/en/setup/service-agent/java-agent/Customize-enhance-trace.md b/docs/en/setup/service-agent/java-agent/Customize-enhance-trace.md new file mode 100644 index 000000000..36cf31e89 --- /dev/null +++ b/docs/en/setup/service-agent/java-agent/Customize-enhance-trace.md @@ -0,0 +1,72 @@ +## Support custom enhance +Here is an optional plugin `apm-customize-enhance-plugin` + +## Introduce +SkyWalking has provided [Java agent plugin development guide](https://github.com/apache/incubator-skywalking/blob/master/docs/en/guides/Java-Plugin-Development-Guide.md) to help developers to build new plugin. + +This plugin is not designed for replacement but for user convenience. The behaviour is very similar with [@Trace toolkit](Application-toolkit-trace.md), but without code change requirement, and more powerful, such as provide tag and log. + +## How to configure +Implementing enhancements to custom classes requires two steps. + +1. Active the plugin, move the `optional-plugins/apm-customize-enhance-plugin.jar` to `plugin/apm-customize-enhance-plugin.jar`. +2. Set `plugin.customize.enhance_file` in agent.config, which targets to rule file, such as `/absolute/path/to/customize_enhance.xml`. +3. Set enhancement rules in `customize_enhance.xml`. + ```xml + + + + + + arg[0] + arg[1] + arg[3].[0] + arg[2].['k1'] + arg[4].[1] + arg[4].[2] + + + + arg[0] + arg[0] + arg[1] + + + arg[0].id + arg[0].model1.name + arg[0].model1.getId() + arg[0].os.[1] + arg[0].getM().['k1'] + + + + + arg[0] + arg[1] + + + arg[0].[0] + + + arg[0].[0] + arg[1] + + + + ``` + +- Explanation of the configuration in the file + + | configuration | explanation | + |:----------------- |:---------------| + | class_name | The enhanced class | + | method | The interceptor method of the class | + | operation_name | If fill it out, will use it instead of the default operation_name. | + | operation_name\_suffix | What it means adding dynamic data after the operation_name. | + | static | Is this method static. | + | tag | Will add a tag in local span. The value of key needs to be represented on the XML node. | + | log | Will add a log in local span. The value of key needs to be represented on the XML node. | + | 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.| + diff --git a/docs/en/setup/service-agent/java-agent/README.md b/docs/en/setup/service-agent/java-agent/README.md index 85df6900b..2e94271cc 100644 --- a/docs/en/setup/service-agent/java-agent/README.md +++ b/docs/en/setup/service-agent/java-agent/README.md @@ -92,6 +92,7 @@ Now, we have the following known optional plugins. * Gson serialization lib in optional plugin folder * Lettuce 5.x(JRE1.8+) in optional plugin folder * Zookeeper 3.4.x in optional plugin folder. The reason of being optional plugin is, many business irrelevant traces are generated, which cause extra payload to agents and backends. At the same time, those traces may be just heartbeat(s). +* [Customize enhance](Customize-enhance-trace.md) Trace methods based on description files, rather than write plugin or change source codes. ## Advanced Features * Set the settings through system properties for config file override. Read [setting override](Setting-override.md).