diff --git a/.github/workflows/plugins-test.3.yaml b/.github/workflows/plugins-test.3.yaml index 42069a540..d06107efe 100644 --- a/.github/workflows/plugins-test.3.yaml +++ b/.github/workflows/plugins-test.3.yaml @@ -43,6 +43,7 @@ jobs: - { name: 'spring-async-scenario', title: 'Spring Async 4.3.x-5.1.x (35)' } - { name: 'vertx-eventbus-3.x-scenario', title: 'Vert.x EventBus 3.2.0-3.9.0 (27)' } - { name: 'vertx-web-3.x-scenario', title: 'Vert.x Web 3.0.0-3.9.0 (29)' } + - { name: 'mariadb-scenario', title: 'Mariadb 2.x (8)' } steps: - uses: actions/checkout@v2 with: diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java index 136ebb6df..c608ae334 100755 --- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java +++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java @@ -156,4 +156,6 @@ public class ComponentsDefine { public static final OfficialComponent UNDERTOW = new OfficialComponent(84, "Undertow"); public static final OfficialComponent FINAGLE = new OfficialComponent(85, "Finagle"); + + public static final OfficialComponent MARIADB_JDBC = new OfficialComponent(87, "mariadb-jdbc"); } 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 764e514f4..8a4990d71 100755 --- 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 @@ -328,6 +328,22 @@ public class Config { public static int SQL_PARAMETERS_MAX_LENGTH = 512; } + public static class MARIADB { + /** + * If set to true, the parameters of the sql (typically {@link java.sql.PreparedStatement}) would be + * collected. + */ + public static boolean TRACE_SQL_PARAMETERS = false; + + /** + * For the sake of performance, SkyWalking won't save the entire parameters string into the tag, but only + * the first {@code SQL_PARAMETERS_MAX_LENGTH} characters. + *

+ * Set a negative number to save the complete parameter string to the tag. + */ + public static int SQL_PARAMETERS_MAX_LENGTH = 512; + } + public static class SolrJ { /** * If true, trace all the query parameters(include deleteByIds and deleteByQuery) in Solr query request, diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/PSSetterDefinitionOfJDBCInstrumentation.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/PSSetterDefinitionOfJDBCInstrumentation.java index bdb890a9e..d641ca6ec 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/PSSetterDefinitionOfJDBCInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/PSSetterDefinitionOfJDBCInstrumentation.java @@ -42,7 +42,7 @@ public class PSSetterDefinitionOfJDBCInstrumentation implements InstanceMethodsI public ElementMatcher getMethodsMatcher() { ElementMatcher.Junction matcher = none(); - if (Config.Plugin.MySQL.TRACE_SQL_PARAMETERS || Config.Plugin.POSTGRESQL.TRACE_SQL_PARAMETERS) { + if (Config.Plugin.MySQL.TRACE_SQL_PARAMETERS || Config.Plugin.POSTGRESQL.TRACE_SQL_PARAMETERS || Config.Plugin.MARIADB.TRACE_SQL_PARAMETERS) { final Set setters = ignorable ? PS_IGNORABLE_SETTERS : PS_SETTERS; for (String setter : setters) { matcher = matcher.or(named(setter)); diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/PreparedStatementParameterBuilder.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/PreparedStatementParameterBuilder.java new file mode 100644 index 000000000..1209b80c8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/PreparedStatementParameterBuilder.java @@ -0,0 +1,78 @@ +/* + * 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.jdbc; + +public class PreparedStatementParameterBuilder { + private static final String EMPTY_LIST = "[]"; + private Object[] parameters; + private Integer maxIndex; + private int maxLength = 0; + + public PreparedStatementParameterBuilder setParameters(Object[] parameters) { + this.parameters = parameters; + return this; + } + + public PreparedStatementParameterBuilder setMaxIndex(int maxIndex) { + this.maxIndex = maxIndex; + return this; + } + + public PreparedStatementParameterBuilder setMaxLength(int maxLength) { + this.maxLength = maxLength; + return this; + } + + public String build() { + if (parameters == null) { + return EMPTY_LIST; + } + + String parameterString = getParameterString(); + return truncate(parameterString); + } + + private String getParameterString() { + StringBuilder stringBuilder = new StringBuilder(); + boolean first = true; + for (int i = 0; i < getMaxIndex(); i++) { + Object parameter = parameters[i]; + if (!first) { + stringBuilder.append(","); + } + stringBuilder.append(parameter); + first = false; + } + return String.format("[%s]", stringBuilder.toString()); + } + + private int getMaxIndex() { + int maxIdx = maxIndex != null ? maxIndex : parameters.length; + return Math.min(maxIdx, parameters.length); + } + + private String truncate(String parameterString) { + if (maxLength > 0 && parameterString.length() > maxLength) { + parameterString = parameterString.substring(0, maxLength) + "..."; + } + + return parameterString; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/MariadbURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/MariadbURLParser.java new file mode 100644 index 000000000..65b21e652 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/MariadbURLParser.java @@ -0,0 +1,27 @@ +/* + * 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.jdbc.connectionurl.parser; + +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +public class MariadbURLParser extends MysqlURLParser { + public MariadbURLParser(String url) { + super(url, "Mariadb", ComponentsDefine.MARIADB_JDBC); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/MysqlURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/MysqlURLParser.java index 7b86ea71e..d4ca1b860 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/MysqlURLParser.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/MysqlURLParser.java @@ -19,6 +19,7 @@ package org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.network.trace.component.OfficialComponent; import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; /** @@ -27,12 +28,19 @@ import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; public class MysqlURLParser extends AbstractURLParser { private static final int DEFAULT_PORT = 3306; - private static final String DB_TYPE = "Mysql"; + private String dbType = "Mysql"; + private OfficialComponent component = ComponentsDefine.MYSQL_JDBC_DRIVER; public MysqlURLParser(String url) { super(url); } + public MysqlURLParser(String url, String dbType, OfficialComponent component) { + super(url); + this.dbType = dbType; + this.component = component; + } + @Override protected URLLocation fetchDatabaseHostsIndexRange() { int hostLabelStartIndex = url.indexOf("//"); @@ -87,15 +95,15 @@ public class MysqlURLParser extends AbstractURLParser { sb.append(host).append(","); } } - return new ConnectionInfo(ComponentsDefine.MYSQL_JDBC_DRIVER, DB_TYPE, sb.substring(0, sb.length() - 1), fetchDatabaseNameFromURL()); + return new ConnectionInfo(component, dbType, sb.substring(0, sb.length() - 1), fetchDatabaseNameFromURL()); } else { String[] hostAndPort = hostSegment[0].split(":"); if (hostAndPort.length != 1) { - return new ConnectionInfo(ComponentsDefine.MYSQL_JDBC_DRIVER, DB_TYPE, hostAndPort[0], Integer.valueOf(hostAndPort[1]), fetchDatabaseNameFromURL(location - .endIndex())); + return new ConnectionInfo(component, dbType, hostAndPort[0], Integer.valueOf(hostAndPort[1]), fetchDatabaseNameFromURL(location + .endIndex())); } else { - return new ConnectionInfo(ComponentsDefine.MYSQL_JDBC_DRIVER, DB_TYPE, hostAndPort[0], DEFAULT_PORT, fetchDatabaseNameFromURL(location - .endIndex())); + return new ConnectionInfo(component, dbType, hostAndPort[0], DEFAULT_PORT, fetchDatabaseNameFromURL(location + .endIndex())); } } } diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParser.java index 905806588..12c6e042c 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParser.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParser.java @@ -30,6 +30,7 @@ public class URLParser { private static final String ORACLE_JDBC_URL_PREFIX = "jdbc:oracle"; private static final String H2_JDBC_URL_PREFIX = "jdbc:h2"; private static final String POSTGRESQL_JDBC_URL_PREFIX = "jdbc:postgresql"; + private static final String MARIADB_JDBC_URL_PREFIX = "jdbc:mariadb"; public static ConnectionInfo parser(String url) { ConnectionURLParser parser = null; @@ -42,6 +43,8 @@ public class URLParser { parser = new H2URLParser(url); } else if (lowerCaseUrl.startsWith(POSTGRESQL_JDBC_URL_PREFIX)) { parser = new PostgreSQLURLParser(url); + } else if (lowerCaseUrl.startsWith(MARIADB_JDBC_URL_PREFIX)) { + parser = new MariadbURLParser(url); } return parser.parse(); } diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/PreparedStatementParameterBuilderTest.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/PreparedStatementParameterBuilderTest.java new file mode 100644 index 000000000..11d6fa39c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/PreparedStatementParameterBuilderTest.java @@ -0,0 +1,109 @@ +/* + * 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.jdbc; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class PreparedStatementParameterBuilderTest { + private static Object[] PARAMETERS = new Object[]{"test", 1234}; + private static final String EMPTY_LIST = "[]"; + private PreparedStatementParameterBuilder builder; + + @Test + public void testDefaultBuilder() { + builder = new PreparedStatementParameterBuilder(); + assertThat(builder.build(), is(EMPTY_LIST)); + } + + @Test + public void testNullParameters() { + builder = new PreparedStatementParameterBuilder(); + builder.setParameters(null); + assertThat(builder.build(), is(EMPTY_LIST)); + } + + @Test + public void testMaxIndex() { + builder = new PreparedStatementParameterBuilder(); + Object[] parameters = new Object[]{ + "test", + 1234, + "abcd" + }; + builder.setParameters(parameters).setMaxIndex(2); + assertThat(builder.build(), is("[test,1234]")); + } + + @Test + public void testOutOfRangeOfMaxIndex() { + builder = new PreparedStatementParameterBuilder(); + builder.setParameters(PARAMETERS); + builder.setMaxIndex(3); + assertThat(builder.build(), is("[test,1234]")); + + builder.setMaxIndex(0); + assertThat(builder.build(), is(EMPTY_LIST)); + + builder.setMaxIndex(-1); + assertThat(builder.build(), is(EMPTY_LIST)); + } + + @Test + public void testParametersString() { + builder = new PreparedStatementParameterBuilder(); + Object[] parameters = new Object[]{ + "", + 1234, + 10.0, + null, + 'c', + true, + 0x100 + }; + builder.setParameters(parameters); + assertThat(builder.build(), is("[,1234,10.0,null,c,true,256]")); + } + + @Test + public void testMaxLength() { + builder = new PreparedStatementParameterBuilder(); + builder.setParameters(PARAMETERS); + builder.setMaxLength(10); + assertThat(builder.build(), is("[test,1234...")); + } + + @Test + public void testMaxLengthZero() { + builder = new PreparedStatementParameterBuilder(); + builder.setParameters(PARAMETERS); + builder.setMaxLength(0); + assertThat(builder.build(), is("[test,1234]")); + } + + @Test + public void testMaxLengthOutOfRange() { + builder = new PreparedStatementParameterBuilder(); + builder.setParameters(PARAMETERS); + builder.setMaxLength(20); + assertThat(builder.build(), is("[test,1234]")); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java index 50b9f5035..b832b45e1 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java @@ -152,4 +152,12 @@ public class URLParserTest { assertThat(connectionInfo.getDatabaseName(), is("sample")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:8084")); } + + @Test + public void testParseMariadbJDBCURLWithHost() { + ConnectionInfo connectionInfo = new URLParser().parser("jdbc:mariadb//primaryhost/test"); + assertThat(connectionInfo.getDBType(), is("Mariadb")); + assertThat(connectionInfo.getDatabaseName(), is("test")); + assertThat(connectionInfo.getDatabasePeer(), is("primaryhost:3306")); + } } diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/pom.xml new file mode 100644 index 000000000..0288fbe8e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/pom.xml @@ -0,0 +1,61 @@ + + + + + apm-sdk-plugin + org.apache.skywalking + 8.0.0-SNAPSHOT + + 4.0.0 + + apm-mariadb-2.x-plugin + jar + + mariadb-2.x-plugin + http://maven.apache.org + + + UTF-8 + 2.6.0 + + + + + org.mariadb.jdbc + mariadb-java-client + ${mariadb-java-client.version} + provided + + + org.apache.skywalking + apm-jdbc-commons + ${project.version} + provided + + + + + + + maven-deploy-plugin + + + + diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/Constants.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/Constants.java new file mode 100644 index 000000000..8e717a6d2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/Constants.java @@ -0,0 +1,25 @@ +/* + * 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.jdbc.mariadb.v2; + +import org.apache.skywalking.apm.agent.core.context.tag.StringTag; + +public class Constants { + public static final StringTag SQL_PARAMETERS = new StringTag("db.sql.parameters"); +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateCallableStatementInterceptor.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateCallableStatementInterceptor.java new file mode 100644 index 000000000..212b38834 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateCallableStatementInterceptor.java @@ -0,0 +1,49 @@ +/* + * 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.jdbc.mariadb.v2; + +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.plugin.jdbc.define.StatementEnhanceInfos; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; + +public class CreateCallableStatementInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) { + if (ret instanceof EnhancedInstance) { + ((EnhancedInstance) ret).setSkyWalkingDynamicField(new StatementEnhanceInfos((ConnectionInfo) objInst.getSkyWalkingDynamicField(), (String) allArguments[0], "CallableStatement")); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreatePreparedStatementInterceptor.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreatePreparedStatementInterceptor.java new file mode 100644 index 000000000..3b286a6eb --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreatePreparedStatementInterceptor.java @@ -0,0 +1,50 @@ +/* + * 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.jdbc.mariadb.v2; + +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.plugin.jdbc.define.StatementEnhanceInfos; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; + +public class CreatePreparedStatementInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) { + if (ret instanceof EnhancedInstance) { + ((EnhancedInstance) ret).setSkyWalkingDynamicField(new StatementEnhanceInfos((ConnectionInfo) objInst.getSkyWalkingDynamicField(), (String) allArguments[0], "PreparedStatement")); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateStatementInterceptor.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateStatementInterceptor.java new file mode 100644 index 000000000..5113af05a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateStatementInterceptor.java @@ -0,0 +1,50 @@ +/* + * 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.jdbc.mariadb.v2; + +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.plugin.jdbc.define.StatementEnhanceInfos; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; + +public class CreateStatementInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) { + if (ret instanceof EnhancedInstance) { + ((EnhancedInstance) ret).setSkyWalkingDynamicField(new StatementEnhanceInfos((ConnectionInfo) objInst.getSkyWalkingDynamicField(), "", "Statement")); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/PreparedStatementExecuteMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/PreparedStatementExecuteMethodsInterceptor.java new file mode 100644 index 000000000..c17d94a88 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/PreparedStatementExecuteMethodsInterceptor.java @@ -0,0 +1,95 @@ +/* + * 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.jdbc.mariadb.v2; + +import org.apache.skywalking.apm.agent.core.conf.Config; +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.context.trace.SpanLayer; +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.plugin.jdbc.PreparedStatementParameterBuilder; +import org.apache.skywalking.apm.plugin.jdbc.define.StatementEnhanceInfos; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; + +import static org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.Constants.SQL_PARAMETERS; + +public class PreparedStatementExecuteMethodsInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public final void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, MethodInterceptResult result) { + StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); + ConnectionInfo connectInfo = cacheObject.getConnectionInfo(); + if (connectInfo == null) { + return; + } + AbstractSpan span = ContextManager.createExitSpan(buildOperationName(connectInfo, method.getName(), cacheObject + .getStatementName()), connectInfo.getDatabasePeer()); + Tags.DB_TYPE.set(span, "sql"); + Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName()); + Tags.DB_STATEMENT.set(span, cacheObject.getSql()); + span.setComponent(connectInfo.getComponent()); + + if (Config.Plugin.MARIADB.TRACE_SQL_PARAMETERS) { + final Object[] parameters = cacheObject.getParameters(); + if (parameters != null && parameters.length > 0) { + int maxIndex = cacheObject.getMaxIndex(); + SQL_PARAMETERS.set(span, getParameterString(parameters, maxIndex)); + } + } + + SpanLayer.asDB(span); + } + + @Override + public final Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Object ret) { + StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); + if (cacheObject.getConnectionInfo() != null) { + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public final void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); + if (cacheObject.getConnectionInfo() != null) { + ContextManager.activeSpan().errorOccurred().log(t); + } + } + + private String buildOperationName(ConnectionInfo connectionInfo, String methodName, String statementName) { + return connectionInfo.getDBType() + "/JDBI/" + statementName + "/" + methodName; + } + + private String getParameterString(Object[] parameters, int maxIndex) { + return new PreparedStatementParameterBuilder() + .setParameters(parameters) + .setMaxIndex(maxIndex) + .setMaxLength(Config.Plugin.MARIADB.SQL_PARAMETERS_MAX_LENGTH) + .build(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/SetCatalogInterceptor.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/SetCatalogInterceptor.java new file mode 100644 index 000000000..8c25279c4 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/SetCatalogInterceptor.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.plugin.jdbc.mariadb.v2; + +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.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; + +public class SetCatalogInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) { + Object dynamicField = objInst.getSkyWalkingDynamicField(); + if (dynamicField instanceof ConnectionInfo) { + ((ConnectionInfo) dynamicField).setDatabaseName(String.valueOf(allArguments[0])); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) { + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/StatementExecuteMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/StatementExecuteMethodsInterceptor.java new file mode 100644 index 000000000..8f636ce82 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/StatementExecuteMethodsInterceptor.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.jdbc.mariadb.v2; + +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.context.trace.SpanLayer; +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.plugin.jdbc.define.StatementEnhanceInfos; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; + +public class StatementExecuteMethodsInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public final void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, MethodInterceptResult result) { + StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); + ConnectionInfo connectInfo = cacheObject.getConnectionInfo(); + if (connectInfo != null) { + AbstractSpan span = ContextManager.createExitSpan(buildOperationName(connectInfo, method.getName(), cacheObject + .getStatementName()), connectInfo.getDatabasePeer()); + Tags.DB_TYPE.set(span, "sql"); + Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName()); + String sql = allArguments.length > 0 ? (String) allArguments[0] : ""; + Tags.DB_STATEMENT.set(span, sql); + span.setComponent(connectInfo.getComponent()); + SpanLayer.asDB(span); + } + } + + @Override + public final Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Object ret) { + StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); + if (cacheObject.getConnectionInfo() != null) { + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public final void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); + if (cacheObject.getConnectionInfo() != null) { + ContextManager.activeSpan().errorOccurred().log(t); + } + } + + private String buildOperationName(ConnectionInfo connectionInfo, String methodName, String statementName) { + return connectionInfo.getDBType() + "/JDBI/" + statementName + "/" + methodName; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/ConnectionInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/ConnectionInstrumentation.java new file mode 100644 index 000000000..45ddf69ec --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/ConnectionInstrumentation.java @@ -0,0 +1,139 @@ +/* + * 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.jdbc.mariadb.v2.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.plugin.jdbc.define.Constants; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class ConnectionInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.mariadb.jdbc.MariaDbConnection"; + private static final String CREATE_STATEMENT_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.CreateStatementInterceptor"; + private static final String CREATE_PREPARED_STATEMENT_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.CreatePreparedStatementInterceptor"; + private static final String CREATE_CALLABLE_STATEMENT_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.CreateCallableStatementInterceptor"; + private static final String SET_CATALOG_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.SetCatalogInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(Constants.CREATE_STATEMENT_METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return CREATE_STATEMENT_INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(Constants.PREPARE_STATEMENT_METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return CREATE_PREPARED_STATEMENT_INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(Constants.PREPARE_CALL_METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return CREATE_CALLABLE_STATEMENT_INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(Constants.COMMIT_METHOD_NAME) + .or(named(Constants.ROLLBACK_METHOD_NAME)) + .or(named(Constants.CLOSE_METHOD_NAME)) + .or(named(Constants.RELEASE_SAVE_POINT_METHOD_NAME)); + } + + @Override + public String getMethodsInterceptor() { + return Constants.SERVICE_METHOD_INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("setCatalog"); + } + + @Override + public String getMethodsInterceptor() { + return SET_CATALOG_INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/DriverInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/DriverInstrumentation.java new file mode 100644 index 000000000..f59ef432c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/DriverInstrumentation.java @@ -0,0 +1,36 @@ +/* + * 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.jdbc.mariadb.v2.define; + +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.plugin.jdbc.define.AbstractDriverInstrumentation; + +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * {@link DriverInstrumentation} presents that skywalking intercepts {@link org.mariadb.jdbc.Driver}. + */ +public class DriverInstrumentation extends AbstractDriverInstrumentation { + private static final String ENHANCE_CLASS = "org.mariadb.jdbc.Driver"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementIgnoredSetterInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementIgnoredSetterInstrumentation.java new file mode 100644 index 000000000..17f528e93 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementIgnoredSetterInstrumentation.java @@ -0,0 +1,33 @@ +/* + * 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.jdbc.mariadb.v2.define; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.plugin.jdbc.PSSetterDefinitionOfJDBCInstrumentation; + +public class PreparedStatementIgnoredSetterInstrumentation extends PreparedStatementInstrumentation { + + @Override + public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new PSSetterDefinitionOfJDBCInstrumentation(true) + }; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementInstrumentation.java new file mode 100644 index 000000000..c1b1afe69 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementInstrumentation.java @@ -0,0 +1,78 @@ +/* + * 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.jdbc.mariadb.v2.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 static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch.byMultiClassMatch; + +public class PreparedStatementInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String CLIENT_SIDE_PREPARED_STATEMENT_CLASS_2_0_X = "org.mariadb.jdbc.MariaDbPreparedStatementClient"; + private static final String CLIENT_SIDE_PREPARED_STATEMENT_CLASS_2_4_X = "org.mariadb.jdbc.ClientSidePreparedStatement"; + private static final String SERVER_SIDE_PREPARED_STATEMENT_CLASS_2_0_X = "org.mariadb.jdbc.MariaDbPreparedStatementServer"; + private static final String SERVER_SIDE_PREPARED_STATEMENT_CLASS_2_4_X = "org.mariadb.jdbc.ServerSidePreparedStatement"; + private static final String PREPARED_STATEMENT_EXECUTE_METHODS_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.PreparedStatementExecuteMethodsInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byMultiClassMatch( + CLIENT_SIDE_PREPARED_STATEMENT_CLASS_2_0_X, + CLIENT_SIDE_PREPARED_STATEMENT_CLASS_2_4_X, + SERVER_SIDE_PREPARED_STATEMENT_CLASS_2_0_X, + SERVER_SIDE_PREPARED_STATEMENT_CLASS_2_4_X + ); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("execute") + .or(named("executeQuery")) + .or(named("executeUpdate")); + } + + @Override + public String getMethodsInterceptor() { + return PREPARED_STATEMENT_EXECUTE_METHODS_INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementNullSetterInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementNullSetterInstrumentation.java new file mode 100644 index 000000000..e4cb3ecb0 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementNullSetterInstrumentation.java @@ -0,0 +1,33 @@ +/* + * 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.jdbc.mariadb.v2.define; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.plugin.jdbc.JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint; + +public class PreparedStatementNullSetterInstrumentation extends PreparedStatementInstrumentation { + + @Override + public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint() + }; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementSetterInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementSetterInstrumentation.java new file mode 100644 index 000000000..7f58dfd42 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/PreparedStatementSetterInstrumentation.java @@ -0,0 +1,33 @@ +/* + * 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.jdbc.mariadb.v2.define; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.plugin.jdbc.PSSetterDefinitionOfJDBCInstrumentation; + +public class PreparedStatementSetterInstrumentation extends PreparedStatementInstrumentation { + + @Override + public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new PSSetterDefinitionOfJDBCInstrumentation(false) + }; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/StatementInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/StatementInstrumentation.java new file mode 100644 index 000000000..8d7bfbd15 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/define/StatementInstrumentation.java @@ -0,0 +1,71 @@ +/* + * 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.jdbc.mariadb.v2.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 static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class StatementInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + private static final String MARIADB_STATEMENT_CLASS_NAME = "org.mariadb.jdbc.MariaDbStatement"; + private static final String STATEMENT_EXECUTE_METHODS_INTERCEPTOR = "org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.StatementExecuteMethodsInterceptor"; + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("execute") + .or(named("executeQuery")) + .or(named("executeUpdate")) + .or(named("executeLargeUpdate")) + .or(named("executeBatch")) + .or(named("executeLargeBatch")); + } + + @Override + public String getMethodsInterceptor() { + return STATEMENT_EXECUTE_METHODS_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byName(MARIADB_STATEMENT_CLASS_NAME); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..65c37bf8e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,23 @@ +# 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. + +mariadb-2.x=org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.define.DriverInstrumentation +mariadb-2.x=org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.define.ConnectionInstrumentation +mariadb-2.x=org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.define.StatementInstrumentation +mariadb-2.x=org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.define.PreparedStatementInstrumentation +mariadb-2.x=org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.define.PreparedStatementSetterInstrumentation +mariadb-2.x=org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.define.PreparedStatementNullSetterInstrumentation +mariadb-2.x=org.apache.skywalking.apm.plugin.jdbc.mariadb.v2.define.PreparedStatementIgnoredSetterInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateCallableStatementInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateCallableStatementInterceptorTest.java new file mode 100644 index 000000000..5979c2cc5 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateCallableStatementInterceptorTest.java @@ -0,0 +1,68 @@ +/* + * 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.jdbc.mariadb.v2; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CreateCallableStatementInterceptorTest { + + private static final String SQL = "Select * from test"; + + private CreateCallableStatementInterceptor interceptor; + + @Mock + private EnhancedInstance ret; + + @Mock + private EnhancedInstance objectInstance; + + @Mock + private ConnectionInfo connectionInfo; + + @Before + public void setUp() { + interceptor = new CreateCallableStatementInterceptor(); + + when(objectInstance.getSkyWalkingDynamicField()).thenReturn(connectionInfo); + } + + @Test + public void testResultIsEnhanceInstance() { + interceptor.afterMethod(objectInstance, null, new Object[]{SQL}, null, ret); + verify(ret).setSkyWalkingDynamicField(Matchers.any()); + } + + @Test + public void testResultIsNotEnhanceInstance() { + interceptor.afterMethod(objectInstance, null, new Object[]{SQL}, null, new Object()); + verify(ret, times(0)).setSkyWalkingDynamicField(Matchers.any()); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreatePreparedStatementInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreatePreparedStatementInterceptorTest.java new file mode 100644 index 000000000..4141c0486 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreatePreparedStatementInterceptorTest.java @@ -0,0 +1,68 @@ +/* + * 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.jdbc.mariadb.v2; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CreatePreparedStatementInterceptorTest { + + private static final String SQL = "Select * from test"; + + private CreatePreparedStatementInterceptor interceptor; + + @Mock + private EnhancedInstance ret; + + @Mock + private EnhancedInstance objectInstance; + + @Mock + private ConnectionInfo connectionInfo; + + @Before + public void setUp() { + interceptor = new CreatePreparedStatementInterceptor(); + + when(objectInstance.getSkyWalkingDynamicField()).thenReturn(connectionInfo); + } + + @Test + public void testResultIsEnhanceInstance() { + interceptor.afterMethod(objectInstance, null, new Object[]{SQL}, null, ret); + verify(ret).setSkyWalkingDynamicField(Matchers.any()); + } + + @Test + public void testResultIsNotEnhanceInstance() { + interceptor.afterMethod(objectInstance, null, new Object[]{SQL}, null, new Object()); + verify(ret, times(0)).setSkyWalkingDynamicField(Matchers.any()); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateStatementInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateStatementInterceptorTest.java new file mode 100644 index 000000000..6292a4b9a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/CreateStatementInterceptorTest.java @@ -0,0 +1,68 @@ +/* + * 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.jdbc.mariadb.v2; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CreateStatementInterceptorTest { + + private static final String SQL = "Select * from test"; + + private CreateStatementInterceptor interceptor; + + @Mock + private EnhancedInstance ret; + + @Mock + private EnhancedInstance objectInstance; + + @Mock + private ConnectionInfo connectionInfo; + + @Before + public void setUp() { + interceptor = new CreateStatementInterceptor(); + + when(objectInstance.getSkyWalkingDynamicField()).thenReturn(connectionInfo); + } + + @Test + public void testResultIsEnhanceInstance() { + interceptor.afterMethod(objectInstance, null, new Object[]{SQL}, null, ret); + verify(ret).setSkyWalkingDynamicField(Matchers.any()); + } + + @Test + public void testResultIsNotEnhanceInstance() { + interceptor.afterMethod(objectInstance, null, new Object[]{SQL}, null, new Object()); + verify(ret, times(0)).setSkyWalkingDynamicField(Matchers.any()); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/PreparedStatementExecuteMethodsInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/PreparedStatementExecuteMethodsInterceptorTest.java new file mode 100644 index 000000000..874284246 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/PreparedStatementExecuteMethodsInterceptorTest.java @@ -0,0 +1,109 @@ +/* + * 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.jdbc.mariadb.v2; + +import org.apache.skywalking.apm.agent.core.conf.Config; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.jdbc.JDBCPreparedStatementSetterInterceptor; +import org.apache.skywalking.apm.plugin.jdbc.define.StatementEnhanceInfos; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; + +import java.lang.reflect.Method; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(PowerMockRunner.class) +@PowerMockRunnerDelegate(TracingSegmentRunner.class) +public class PreparedStatementExecuteMethodsInterceptorTest { + + private static final String SQL = "Select * from test where id = ?"; + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + + private PreparedStatementExecuteMethodsInterceptor serviceMethodInterceptor; + + private JDBCPreparedStatementSetterInterceptor preparedStatementSetterInterceptor; + + @Mock + private ConnectionInfo connectionInfo; + @Mock + private EnhancedInstance objectInstance; + @Mock + private Method method; + private StatementEnhanceInfos enhanceRequireCacheObject; + + @Before + public void setUp() { + Config.Plugin.MARIADB.TRACE_SQL_PARAMETERS = true; + preparedStatementSetterInterceptor = new JDBCPreparedStatementSetterInterceptor(); + serviceMethodInterceptor = new PreparedStatementExecuteMethodsInterceptor(); + + enhanceRequireCacheObject = new StatementEnhanceInfos(connectionInfo, SQL, "PreparedStatement"); + when(objectInstance.getSkyWalkingDynamicField()).thenReturn(enhanceRequireCacheObject); + when(method.getName()).thenReturn("executeQuery"); + when(connectionInfo.getComponent()).thenReturn(ComponentsDefine.MARIADB_JDBC); + when(connectionInfo.getDBType()).thenReturn("Mariadb"); + when(connectionInfo.getDatabaseName()).thenReturn("test"); + when(connectionInfo.getDatabasePeer()).thenReturn("localhost:3306"); + } + + @Test + public void testExecutePreparedStatement() throws Throwable { + preparedStatementSetterInterceptor.beforeMethod(objectInstance, method, new Object[]{1, "abcd"}, null, null); + preparedStatementSetterInterceptor.beforeMethod(objectInstance, method, new Object[]{2, "efgh"}, null, null); + + serviceMethodInterceptor.beforeMethod(objectInstance, method, new Object[]{SQL}, null, null); + serviceMethodInterceptor.afterMethod(objectInstance, method, new Object[]{SQL}, null, null); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment segment = segmentStorage.getTraceSegments().get(0); + assertThat(SegmentHelper.getSpans(segment).size(), is(1)); + AbstractTracingSpan span = SegmentHelper.getSpans(segment).get(0); + SpanAssert.assertLayer(span, SpanLayer.DB); + assertThat(span.getOperationName(), is("Mariadb/JDBI/PreparedStatement/")); + SpanAssert.assertTag(span, 0, "sql"); + SpanAssert.assertTag(span, 1, "test"); + SpanAssert.assertTag(span, 2, SQL); + SpanAssert.assertTag(span, 3, "[abcd,efgh]"); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/StatementExecuteMethodsInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/StatementExecuteMethodsInterceptorTest.java new file mode 100644 index 000000000..9b877bb6c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mariadb/v2/StatementExecuteMethodsInterceptorTest.java @@ -0,0 +1,99 @@ +/* + * 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.jdbc.mariadb.v2; + +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.jdbc.define.StatementEnhanceInfos; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; + +import java.lang.reflect.Method; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(PowerMockRunner.class) +@PowerMockRunnerDelegate(TracingSegmentRunner.class) +public class StatementExecuteMethodsInterceptorTest { + + private static final String SQL = "Select * from test"; + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + + private StatementExecuteMethodsInterceptor serviceMethodInterceptor; + + @Mock + private ConnectionInfo connectionInfo; + @Mock + private EnhancedInstance objectInstance; + @Mock + private Method method; + private StatementEnhanceInfos enhanceRequireCacheObject; + + @Before + public void setUp() { + serviceMethodInterceptor = new StatementExecuteMethodsInterceptor(); + + enhanceRequireCacheObject = new StatementEnhanceInfos(connectionInfo, SQL, "CallableStatement"); + when(objectInstance.getSkyWalkingDynamicField()).thenReturn(enhanceRequireCacheObject); + when(method.getName()).thenReturn("executeQuery"); + when(connectionInfo.getComponent()).thenReturn(ComponentsDefine.MARIADB_JDBC); + when(connectionInfo.getDBType()).thenReturn("Mariadb"); + when(connectionInfo.getDatabaseName()).thenReturn("test"); + when(connectionInfo.getDatabasePeer()).thenReturn("localhost:3306"); + } + + @Test + public void testExecuteStatement() { + serviceMethodInterceptor.beforeMethod(objectInstance, method, new Object[]{SQL}, null, null); + serviceMethodInterceptor.afterMethod(objectInstance, method, new Object[]{SQL}, null, null); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment segment = segmentStorage.getTraceSegments().get(0); + assertThat(SegmentHelper.getSpans(segment).size(), is(1)); + AbstractTracingSpan span = SegmentHelper.getSpans(segment).get(0); + SpanAssert.assertLayer(span, SpanLayer.DB); + assertThat(span.getOperationName(), is("Mariadb/JDBI/CallableStatement/")); + SpanAssert.assertTag(span, 0, "sql"); + SpanAssert.assertTag(span, 1, "test"); + SpanAssert.assertTag(span, 2, SQL); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/PreparedStatementExecuteMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/PreparedStatementExecuteMethodsInterceptor.java index 91b01862d..fa0613af9 100644 --- a/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/PreparedStatementExecuteMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/PreparedStatementExecuteMethodsInterceptor.java @@ -26,6 +26,7 @@ import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; 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.plugin.jdbc.PreparedStatementParameterBuilder; import org.apache.skywalking.apm.plugin.jdbc.define.StatementEnhanceInfos; import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; @@ -37,7 +38,7 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho @Override public final void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + Class[] argumentsTypes, MethodInterceptResult result) { StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); ConnectionInfo connectInfo = cacheObject.getConnectionInfo(); /** @@ -49,7 +50,7 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho if (connectInfo != null) { AbstractSpan span = ContextManager.createExitSpan(buildOperationName(connectInfo, method.getName(), cacheObject - .getStatementName()), connectInfo.getDatabasePeer()); + .getStatementName()), connectInfo.getDatabasePeer()); Tags.DB_TYPE.set(span, "sql"); Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName()); Tags.DB_STATEMENT.set(span, cacheObject.getSql()); @@ -59,11 +60,7 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho final Object[] parameters = cacheObject.getParameters(); if (parameters != null && parameters.length > 0) { int maxIndex = cacheObject.getMaxIndex(); - String parameterString = buildParameterString(parameters, maxIndex); - int sqlParametersMaxLength = Config.Plugin.MySQL.SQL_PARAMETERS_MAX_LENGTH; - if (sqlParametersMaxLength > 0 && parameterString.length() > sqlParametersMaxLength) { - parameterString = parameterString.substring(0, sqlParametersMaxLength) + "..."; - } + String parameterString = getParameterString(parameters, maxIndex); SQL_PARAMETERS.set(span, parameterString); } } @@ -74,7 +71,7 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho @Override public final Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, Object ret) throws Throwable { + Class[] argumentsTypes, Object ret) { StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); if (cacheObject.getConnectionInfo() != null) { ContextManager.stopSpan(); @@ -84,7 +81,7 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho @Override public final void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, Throwable t) { + Class[] argumentsTypes, Throwable t) { StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); if (cacheObject.getConnectionInfo() != null) { ContextManager.activeSpan().errorOccurred().log(t); @@ -95,18 +92,11 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho return connectionInfo.getDBType() + "/JDBI/" + statementName + "/" + methodName; } - private String buildParameterString(Object[] parameters, int maxIndex) { - String parameterString = "["; - boolean first = true; - for (int i = 0; i < maxIndex; i++) { - Object parameter = parameters[i]; - if (!first) { - parameterString += ","; - } - parameterString += parameter; - first = false; - } - parameterString += "]"; - return parameterString; + private String getParameterString(Object[] parameters, int maxIndex) { + return new PreparedStatementParameterBuilder() + .setParameters(parameters) + .setMaxIndex(maxIndex) + .setMaxLength(Config.Plugin.MySQL.SQL_PARAMETERS_MAX_LENGTH) + .build(); } } diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml index 2db840d29..1c9d9d249 100644 --- a/apm-sniffer/apm-sdk-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pom.xml @@ -88,6 +88,7 @@ lettuce-5.x-plugin avro-plugin finagle-6.25.x-plugin + mariadb-2.x-plugin pom diff --git a/apm-sniffer/apm-sdk-plugin/postgresql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/postgresql/PreparedStatementExecuteMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/postgresql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/postgresql/PreparedStatementExecuteMethodsInterceptor.java index 5a76cf3f9..151c37ecb 100755 --- a/apm-sniffer/apm-sdk-plugin/postgresql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/postgresql/PreparedStatementExecuteMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/postgresql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/postgresql/PreparedStatementExecuteMethodsInterceptor.java @@ -18,7 +18,6 @@ package org.apache.skywalking.apm.plugin.jdbc.postgresql; -import java.lang.reflect.Method; import org.apache.skywalking.apm.agent.core.conf.Config; import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.context.tag.StringTag; @@ -28,9 +27,12 @@ import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; 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.plugin.jdbc.PreparedStatementParameterBuilder; import org.apache.skywalking.apm.plugin.jdbc.define.StatementEnhanceInfos; import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; +import java.lang.reflect.Method; + /** * {@link PreparedStatementExecuteMethodsInterceptor} create the exit span when the client call the interceptor * methods. @@ -41,11 +43,11 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho @Override public final void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + Class[] argumentsTypes, MethodInterceptResult result) { StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); ConnectionInfo connectInfo = cacheObject.getConnectionInfo(); AbstractSpan span = ContextManager.createExitSpan(buildOperationName(connectInfo, method.getName(), cacheObject.getStatementName()), connectInfo - .getDatabasePeer()); + .getDatabasePeer()); Tags.DB_TYPE.set(span, "sql"); Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName()); Tags.DB_STATEMENT.set(span, cacheObject.getSql()); @@ -55,11 +57,7 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho final Object[] parameters = cacheObject.getParameters(); if (parameters != null && parameters.length > 0) { int maxIndex = cacheObject.getMaxIndex(); - String parameterString = buildParameterString(parameters, maxIndex); - int sqlParametersMaxLength = Config.Plugin.POSTGRESQL.SQL_PARAMETERS_MAX_LENGTH; - if (sqlParametersMaxLength > 0 && parameterString.length() > sqlParametersMaxLength) { - parameterString = parameterString.substring(0, sqlParametersMaxLength) + "..." + "]"; - } + String parameterString = getParameterString(parameters, maxIndex); SQL_PARAMETERS.set(span, parameterString); } } @@ -69,7 +67,7 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho @Override public final Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, Object ret) throws Throwable { + Class[] argumentsTypes, Object ret) { StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); if (cacheObject.getConnectionInfo() != null) { ContextManager.stopSpan(); @@ -79,7 +77,7 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho @Override public final void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, Throwable t) { + Class[] argumentsTypes, Throwable t) { StatementEnhanceInfos cacheObject = (StatementEnhanceInfos) objInst.getSkyWalkingDynamicField(); if (cacheObject.getConnectionInfo() != null) { ContextManager.activeSpan().errorOccurred().log(t); @@ -90,18 +88,11 @@ public class PreparedStatementExecuteMethodsInterceptor implements InstanceMetho return connectionInfo.getDBType() + "/JDBI/" + statementName + "/" + methodName; } - private String buildParameterString(Object[] parameters, int maxIndex) { - String parameterString = "["; - boolean first = true; - for (int i = 0; i < maxIndex; i++) { - Object parameter = parameters[i]; - if (!first) { - parameterString += ","; - } - parameterString += parameter; - first = false; - } - parameterString += "]"; - return parameterString; + private String getParameterString(Object[] parameters, int maxIndex) { + return new PreparedStatementParameterBuilder() + .setParameters(parameters) + .setMaxIndex(maxIndex) + .setMaxLength(Config.Plugin.POSTGRESQL.SQL_PARAMETERS_MAX_LENGTH) + .build(); } } diff --git a/docs/en/setup/service-agent/java-agent/README.md b/docs/en/setup/service-agent/java-agent/README.md index acd80bf6a..a1b4f6037 100755 --- a/docs/en/setup/service-agent/java-agent/README.md +++ b/docs/en/setup/service-agent/java-agent/README.md @@ -114,6 +114,8 @@ property key | Description | Default | `plugin.mysql.sql_parameters_max_length`|If set to positive number, the `db.sql.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem.|`512`| `plugin.postgresql.trace_sql_parameters`|If set to true, the parameters of the sql (typically `java.sql.PreparedStatement`) would be collected.|`false`| `plugin.postgresql.sql_parameters_max_length`|If set to positive number, the `db.sql.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem.|`512`| +`plugin.mariadb.trace_sql_parameters`|If set to true, the parameters of the sql (typically `java.sql.PreparedStatement`) would be collected.|`false`| +`plugin.mariadb.sql_parameters_max_length`|If set to positive number, the `db.sql.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem.|`512`| `plugin.solrj.trace_statement`|If true, trace all the query parameters(include deleteByIds and deleteByQuery) in Solr query request, default is false.|`false`| `plugin.solrj.trace_ops_params`|If true, trace all the operation parameters in Solr request, default is false.|`false`| `plugin.light4j.trace_handler_chain`|If true, trace all middleware/business handlers that are part of the Light4J handler chain for a request.|false| diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md index 735f18540..abb85332f 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -32,6 +32,7 @@ * [Sharding-JDBC](https://github.com/shardingjdbc/sharding-jdbc) 1.5.x * [ShardingSphere](https://github.com/apache/incubator-shardingsphere) 3.0.0, 4.0.0-RC1 * PostgreSQL Driver 8.x, 9.x, 42.x + * Mariadb Driver 2.x, 1.8 * RPC Frameworks * [Dubbo](https://github.com/alibaba/dubbo) 2.5.4 -> 2.6.0 * [Dubbox](https://github.com/dangdangdotcom/dubbox) 2.8.4 diff --git a/oap-server/server-bootstrap/src/main/resources/component-libraries.yml b/oap-server/server-bootstrap/src/main/resources/component-libraries.yml index 04d829842..9de8925b1 100755 --- a/oap-server/server-bootstrap/src/main/resources/component-libraries.yml +++ b/oap-server/server-bootstrap/src/main/resources/component-libraries.yml @@ -287,6 +287,12 @@ Undertow: Finagle: id: 85 languages: Java,Scala +Mariadb: + id: 86 + languages: Java +mariadb-jdbc: + id: 87 + languages: Java # .NET/.NET Core components # [3000, 4000) for C#/.NET only @@ -414,3 +420,4 @@ Component-Server-Mappings: pulsar-producer: Pulsar pulsar-consumer: Pulsar rest-high-level-client: Elasticsearch + mariadb-jdbc: Mariadb diff --git a/oap-server/server-core/src/test/resources/component-libraries.yml b/oap-server/server-core/src/test/resources/component-libraries.yml index 643b0b2b7..44a9d127a 100755 --- a/oap-server/server-core/src/test/resources/component-libraries.yml +++ b/oap-server/server-core/src/test/resources/component-libraries.yml @@ -251,6 +251,12 @@ AvroClient: Finagle: id: 85 languages: Java,Scala +Mariadb: + id: 86 + languages: Java +mariadb-jdbc: + id: 87 + languages: Java # .NET/.NET Core components @@ -352,3 +358,4 @@ Component-Server-Mappings: rest-high-level-client: Elasticsearch SolrJ: Solr cassandra-java-driver: Cassandra + mariadb-jdbc: Mariadb diff --git a/test/plugin/scenarios/mariadb-scenario/bin/startup.sh b/test/plugin/scenarios/mariadb-scenario/bin/startup.sh new file mode 100644 index 000000000..58858857e --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# 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. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/mariadb-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/mariadb-scenario/config/expectedData.yaml b/test/plugin/scenarios/mariadb-scenario/config/expectedData.yaml new file mode 100644 index 000000000..37e1b3465 --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/config/expectedData.yaml @@ -0,0 +1,119 @@ +# 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. +segmentItems: +- serviceName: mariadb-scenario + segmentSize: ge 1 + segments: + - segmentId: not null + spans: + - operationName: Mariadb/JDBI/Statement/execute + operationId: eq 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 87 + isError: false + spanType: Exit + peer: mariadb-server:3306 + skipAnalysis: 'false' + tags: + - {key: db.type, value: sql} + - {key: db.instance, value: test} + - key: db.statement + value: "CREATE TABLE test_table(\nid VARCHAR(1) PRIMARY KEY, \nvalue VARCHAR(10)\ + \ NOT NULL)" + - operationName: Mariadb/JDBI/PreparedStatement/execute + operationId: eq 0 + parentSpanId: 0 + spanId: 2 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 87 + isError: false + spanType: Exit + peer: mariadb-server:3306 + skipAnalysis: 'false' + tags: + - {key: db.type, value: sql} + - {key: db.instance, value: test} + - {key: db.statement, value: "INSERT INTO test_table(id, value) VALUES(?,?)"} + - operationName: Mariadb/JDBI/PreparedStatement/execute + operationId: eq 0 + parentSpanId: 0 + spanId: 3 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 87 + isError: false + spanType: Exit + peer: mariadb-server:3306 + skipAnalysis: 'false' + tags: + - {key: db.type, value: sql} + - {key: db.instance, value: test} + - {key: db.statement, value: "SELECT id, value FROM test_table WHERE id = ?"} + - operationName: Mariadb/JDBI/Statement/execute + operationId: eq 0 + parentSpanId: 0 + spanId: 4 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 87 + isError: false + spanType: Exit + peer: mariadb-server:3306 + skipAnalysis: 'false' + tags: + - {key: db.type, value: sql} + - {key: db.instance, value: test} + - {key: db.statement, value: "DROP table test_table"} + - operationName: Mariadb/JDBI/Connection/close + operationId: eq 0 + parentSpanId: 0 + spanId: 5 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 87 + isError: false + spanType: Exit + peer: mariadb-server:3306 + skipAnalysis: 'false' + tags: + - {key: db.type, value: sql} + - {key: db.instance, value: test} + - {key: db.statement, value: ''} + - operationName: /mariadb-scenario/case/mariadb-scenario + operationId: eq 0 + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + spanLayer: Http + isError: false + spanType: Entry + peer: '' + componentId: 1 + tags: + - {key: url, value: 'http://localhost:8080/mariadb-scenario/case/mariadb-scenario'} + - {key: http.method, value: GET} + logs: [] + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/mariadb-scenario/configuration.yml b/test/plugin/scenarios/mariadb-scenario/configuration.yml new file mode 100644 index 000000000..73e4c48c8 --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/configuration.yml @@ -0,0 +1,32 @@ +# 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. + +type: jvm +entryService: http://localhost:8080/mariadb-scenario/case/mariadb-scenario +healthCheck: http://localhost:8080/mariadb-scenario/case/healthCheck +startScript: ./bin/startup.sh +environment: +depends_on: + - mariadb-server +dependencies: + mariadb-server: + image: mariadb:10.5.2 + hostname: mariadb-server + expose: + - "3306" + environment: + - MYSQL_ROOT_PASSWORD=root + - MYSQL_DATABASE=test diff --git a/test/plugin/scenarios/mariadb-scenario/pom.xml b/test/plugin/scenarios/mariadb-scenario/pom.xml new file mode 100644 index 000000000..6e155ba16 --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/pom.xml @@ -0,0 +1,120 @@ + + + + + org.apache.skywalking.apm.testcase + mariadb-scenario + 1.0.0 + jar + + 4.0.0 + + + UTF-8 + 1.8 + + 2.6.0 + ${test.framework.version} + + 2.1.6.RELEASE + + + skywalking-mariadb-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot-version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + org.mariadb.jdbc + mariadb-java-client + ${test.framework.version} + + + + + mariadb-scenario + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + maven-compiler-plugin + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/mariadb-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/mariadb-scenario/src/main/assembly/assembly.xml new file mode 100644 index 000000000..54b00bcbe --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/mariadb-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/Application.java b/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/Application.java new file mode 100644 index 000000000..2c71b3bc9 --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/Application.java @@ -0,0 +1,39 @@ +/* + * 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.testcase.mariadb; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + private static final Logger logger = LogManager.getLogger(Application.class); + + public static void main(String[] args) { + try { + SpringApplication.run(Application.class, args); + } catch (Exception ex) { + logger.error("Application start error", ex); + throw ex; + } + } +} diff --git a/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/MariadbConfig.java b/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/MariadbConfig.java new file mode 100644 index 000000000..d4e873b0b --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/MariadbConfig.java @@ -0,0 +1,58 @@ +/* + * 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.testcase.mariadb; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class MariadbConfig { + private static Logger logger = LogManager.getLogger(MariadbConfig.class); + private static String url; + private static String userName; + private static String password; + + static { + InputStream inputStream = MariadbConfig.class.getClassLoader().getResourceAsStream("/jdbc.properties"); + Properties properties = new Properties(); + try { + properties.load(inputStream); + } catch (IOException e) { + logger.error("Failed to load config", e); + } + + url = properties.getProperty("mariadb.url"); + userName = properties.getProperty("mariadb.username"); + password = properties.getProperty("mariadb.password"); + } + + public static String getUrl() { + return url; + } + + public static String getUserName() { + return userName; + } + + public static String getPassword() { + return password; + } +} diff --git a/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/SQLExecutor.java b/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/SQLExecutor.java new file mode 100644 index 000000000..e56b33f8a --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/SQLExecutor.java @@ -0,0 +1,70 @@ +/* + * 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.testcase.mariadb; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; + +public class SQLExecutor implements AutoCloseable { + private static Logger logger = LogManager.getLogger(SQLExecutor.class); + private Connection connection; + + public SQLExecutor() throws SQLException { + try { + Class.forName("org.mariadb.jdbc.Driver"); + } catch (ClassNotFoundException ex) { + logger.error(ex); + } + connection = DriverManager.getConnection(MariadbConfig.getUrl(), MariadbConfig.getUserName(), MariadbConfig.getPassword()); + } + + public void execute(String sql) throws SQLException { + Statement statement = connection.createStatement(); + statement.execute(sql); + } + + public void insertData(String sql, String id, String value) throws SQLException { + PreparedStatement preparedStatement = connection.prepareStatement(sql); + preparedStatement.setString(1, id); + preparedStatement.setString(2, value); + preparedStatement.execute(); + } + + public void queryData(String sql, String id) throws SQLException { + PreparedStatement preparedStatement = connection.prepareStatement(sql); + preparedStatement.setString(1, id); + preparedStatement.execute(); + } + + public void closeConnection() throws SQLException { + if (this.connection != null) { + this.connection.close(); + } + } + + @Override + public void close() throws Exception { + closeConnection(); + } +} diff --git a/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/controller/CaseController.java b/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/controller/CaseController.java new file mode 100644 index 000000000..72136c53b --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/src/main/java/org/apache/skywalking/apm/testcase/mariadb/controller/CaseController.java @@ -0,0 +1,65 @@ +/* + * 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.testcase.mariadb.controller; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.skywalking.apm.testcase.mariadb.SQLExecutor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.SQLException; + +@RestController +@RequestMapping("/case") +public class CaseController { + + private static final Logger logger = LogManager.getLogger(CaseController.class); + + private static final String SUCCESS = "Success"; + + private static final String CREATE_TABLE_SQL = "CREATE TABLE test_table(\n" + "id VARCHAR(1) PRIMARY KEY, \n" + "value VARCHAR(10) NOT NULL)"; + private static final String INSERT_DATA_SQL = "INSERT INTO test_table(id, value) VALUES(?,?)"; + private static final String QUERY_DATA_SQL = "SELECT id, value FROM test_table WHERE id = ?"; + private static final String DELETE_DATA_SQL = "DELETE FROM test_table WHERE id=?"; + private static final String DROP_TABLE_SQL = "DROP table test_table"; + + @RequestMapping("/mariadb-scenario") + @ResponseBody + public String testcase() throws Exception { + try (SQLExecutor sqlExecute = new SQLExecutor()) { + sqlExecute.execute(CREATE_TABLE_SQL); + sqlExecute.insertData(INSERT_DATA_SQL, "1", "value"); + sqlExecute.queryData(QUERY_DATA_SQL, "1"); + sqlExecute.execute(DROP_TABLE_SQL); + } catch (Exception ex) { + logger.error("Failed to execute sql.", ex); + throw ex; + } + return SUCCESS; + } + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() throws Exception { + return SUCCESS; + } + +} diff --git a/test/plugin/scenarios/mariadb-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/mariadb-scenario/src/main/resources/application.yaml new file mode 100644 index 000000000..cb7ac89bb --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +# +# 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. +# +# +server: + port: 8080 + servlet: + context-path: /mariadb-scenario +logging: + config: classpath:log4j2.xml diff --git a/test/plugin/scenarios/mariadb-scenario/src/main/resources/jdbc.properties b/test/plugin/scenarios/mariadb-scenario/src/main/resources/jdbc.properties new file mode 100644 index 000000000..c494405b6 --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/src/main/resources/jdbc.properties @@ -0,0 +1,18 @@ +# 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 +# "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. +mariadb.url=jdbc:mariadb://mariadb-server:3306/test +mariadb.username=root +mariadb.password=root diff --git a/test/plugin/scenarios/mariadb-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/mariadb-scenario/src/main/resources/log4j2.xml new file mode 100644 index 000000000..b5cda5ae8 --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + diff --git a/test/plugin/scenarios/mariadb-scenario/support-version.list b/test/plugin/scenarios/mariadb-scenario/support-version.list new file mode 100644 index 000000000..693cd2bd4 --- /dev/null +++ b/test/plugin/scenarios/mariadb-scenario/support-version.list @@ -0,0 +1,27 @@ +# 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 +# "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. + +# lists your version here + +2.6.0 +2.5.4 +2.4.4 +2.3.0 +2.2.6 +2.1.2 +2.0.3 +1.8.0 +