diff --git a/CHANGES.md b/CHANGES.md index d0faa98a31..bc7ddff4e2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,9 @@ Release Notes. * Support Tomcat thread pool metric collect. * Remove plugin for ServiceComb Java Chassis 0.x * Add Guava EventBus plugin. +* Support Druid Connection pool metrics collecting. +* Support HikariCP Connection pool metrics collecting. +* Support Dbcp2 Connection pool metrics collecting. #### Documentation diff --git a/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/pom.xml index 0beac97c4d..1c86da838f 100644 --- a/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/pom.xml @@ -38,7 +38,11 @@ ${dbcp.version} provided + + org.apache.skywalking + apm-jdbc-commons + ${project.version} + provided + - - - \ No newline at end of file + diff --git a/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/PoolingJmxRegisterInterceptor.java b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/PoolingJmxRegisterInterceptor.java new file mode 100644 index 0000000000..6ea9892cab --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/PoolingJmxRegisterInterceptor.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.dbcp.v2; + +import org.apache.commons.dbcp2.BasicDataSourceMXBean; +import org.apache.skywalking.apm.agent.core.meter.MeterFactory; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * {@link PoolingJmxRegisterInterceptor} intercepted the method of DBCP jmxRegister register metric monitor. + */ +public class PoolingJmxRegisterInterceptor implements InstanceMethodsAroundInterceptor { + private static final String METER_NAME = "datasource"; + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (objInst.getSkyWalkingDynamicField() != null) { + BasicDataSourceMXBean basicDataSource = (BasicDataSourceMXBean) objInst; + String tagValue = (String) objInst.getSkyWalkingDynamicField(); + Map>> metricMap = getMetrics(); + metricMap.forEach((key, value) -> MeterFactory.gauge(METER_NAME, value.apply(basicDataSource)) + .tag("name", tagValue).tag("status", key).build()); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + + } + + private Map>> getMetrics() { + Map>> metricMap = new HashMap(); + metricMap.put("numActive", (BasicDataSourceMXBean basicDataSource) -> () -> (double) basicDataSource.getNumActive()); + metricMap.put("maxTotal", (BasicDataSourceMXBean basicDataSource) -> () -> (double) basicDataSource.getMaxTotal()); + metricMap.put("numIdle", (BasicDataSourceMXBean basicDataSource) -> () -> (double) (basicDataSource.getNumIdle())); + metricMap.put("maxWaitMillis", (BasicDataSourceMXBean basicDataSource) -> () -> (double) basicDataSource.getMaxWaitMillis()); + metricMap.put("maxIdle", (BasicDataSourceMXBean basicDataSource) -> () -> (double) basicDataSource.getMaxIdle()); + metricMap.put("minIdle", (BasicDataSourceMXBean basicDataSource) -> () -> (double) basicDataSource.getMinIdle()); + metricMap.put("initialSize", (BasicDataSourceMXBean basicDataSource) -> () -> (double) basicDataSource.getInitialSize()); + return metricMap; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/PoolingSetUrlInterceptor.java b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/PoolingSetUrlInterceptor.java new file mode 100644 index 0000000000..b2a9914ad5 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/PoolingSetUrlInterceptor.java @@ -0,0 +1,51 @@ +/* + * 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.dbcp.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.connectionurl.parser.URLParser; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; + +/** + * {@link PoolingSetUrlInterceptor} intercepted the method of DBCP set url Get parameters. + */ +public class PoolingSetUrlInterceptor implements InstanceMethodsAroundInterceptor { + private static final String METER_NAME = "datasource"; + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + ConnectionInfo connectionInfo = URLParser.parser((String) allArguments[0]); + String tagValue = connectionInfo.getDatabaseName() + "_" + connectionInfo.getDatabasePeer(); + objInst.setSkyWalkingDynamicField(tagValue); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/define/BasicDataSourceInstrumentation.java b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/define/BasicDataSourceInstrumentation.java index 2885905758..6e5bf93b82 100755 --- a/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/define/BasicDataSourceInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/dbcp/v2/define/BasicDataSourceInstrumentation.java @@ -31,10 +31,13 @@ /** * BasicDataSource provides a "one stop shopping" solution for database connection pool solution * basic requirements. BasicDataSource#getConnection() creates (if necessary) and return a connection. + * BasicDataSource#setUrl(String) set url */ public class BasicDataSourceInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { private static final String ENHANCE_CLASS = "org.apache.commons.dbcp2.BasicDataSource"; private static final String CONNECT_GET_INTERCEPTOR = "org.apache.skywalking.apm.plugin.dbcp.v2.PoolingGetConnectInterceptor"; + private static final String INTERCEPTOR_URL_CLASS = "org.apache.skywalking.apm.plugin.dbcp.v2.PoolingSetUrlInterceptor"; + private static final String INTERCEPTOR_CREATE_CLASS = "org.apache.skywalking.apm.plugin.dbcp.v2.PoolingJmxRegisterInterceptor"; @Override protected ClassMatch enhanceClass() { @@ -48,7 +51,7 @@ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[] { + return new InstanceMethodsInterceptPoint[]{ new InstanceMethodsInterceptPoint() { @Override public ElementMatcher getMethodsMatcher() { @@ -60,6 +63,38 @@ public String getMethodsInterceptor() { return CONNECT_GET_INTERCEPTOR; } + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("setUrl"); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_URL_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("jmxRegister"); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CREATE_CLASS; + } + @Override public boolean isOverrideArgs() { return false; diff --git a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/pom.xml index d0a034deb0..46f146c35c 100644 --- a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/pom.xml @@ -38,7 +38,11 @@ ${druid.version} provided + + org.apache.skywalking + apm-jdbc-commons + ${project.version} + provided + - - - \ No newline at end of file + diff --git a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/PoolingAddDruidDataSourceInterceptor.java b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/PoolingAddDruidDataSourceInterceptor.java new file mode 100644 index 0000000000..63a85409de --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/PoolingAddDruidDataSourceInterceptor.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.druid.v1; + +import com.alibaba.druid.pool.DruidDataSourceMBean; +import org.apache.skywalking.apm.agent.core.meter.MeterFactory; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor; +import org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser.URLParser; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * {@link PoolingAddDruidDataSourceInterceptor} intercepted the method of druid addDataSource and register metric monitor. + */ +public class PoolingAddDruidDataSourceInterceptor implements StaticMethodsAroundInterceptor { + private static final String METER_NAME = "datasource"; + + @Override + public void beforeMethod(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, MethodInterceptResult result) { + + } + + @Override + public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, Object ret) { + DruidDataSourceMBean druidDataSource = (DruidDataSourceMBean) allArguments[0]; + ConnectionInfo connectionInfo = URLParser.parser(druidDataSource.getUrl()); + String tagValue = connectionInfo.getDatabaseName() + "_" + connectionInfo.getDatabasePeer(); + Map>> metricMap = getMetrics(); + metricMap.forEach((key, value) -> MeterFactory.gauge(METER_NAME, value.apply(druidDataSource)) + .tag("name", tagValue).tag("status", key).build()); + return ret; + } + + @Override + public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, Throwable t) { + + } + + private Map>> getMetrics() { + Map>> metricMap = new HashMap(); + metricMap.put("activeCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getActiveCount()); + metricMap.put("poolingCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getPoolingCount()); + metricMap.put("idleCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) (druidDataSource.getPoolingCount() - druidDataSource.getActiveCount())); + metricMap.put("lockQueueLength", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getLockQueueLength()); + metricMap.put("maxWaitThreadCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getMaxWaitThreadCount()); + metricMap.put("commitCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getCommitCount()); + metricMap.put("connectCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getConnectCount()); + metricMap.put("connectError", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getConnectErrorCount()); + metricMap.put("createError", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getCreateErrorCount()); + return metricMap; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/define/DruidDataSourceInstrumentation.java b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/define/DruidDataSourceInstrumentation.java index 1a0468576d..ba8c3c77ca 100644 --- a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/define/DruidDataSourceInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/define/DruidDataSourceInstrumentation.java @@ -26,8 +26,8 @@ import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; /** @@ -94,4 +94,5 @@ public boolean isOverrideArgs() { public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() { return new StaticMethodsInterceptPoint[0]; } + } diff --git a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/define/DruidDataSourceStatManagerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/define/DruidDataSourceStatManagerInstrumentation.java new file mode 100644 index 0000000000..e383389c66 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/define/DruidDataSourceStatManagerInstrumentation.java @@ -0,0 +1,79 @@ +/* + * 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.druid.v1.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.StaticMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * Druid is a database connection pool from Alibaba inc. + *

+ * DruidDataSource provides a "one stop" solution for database connection pool solution + * basic requirements. DruidDataSourceStatManager.addDataSource(Object dataSource, String name) + */ +public class DruidDataSourceStatManagerInstrumentation extends ClassStaticMethodsEnhancePluginDefine { + private static final String ENHANCE_CLASS = "com.alibaba.druid.stat.DruidDataSourceStatManager"; + private static final String ENHANCE_METHOD = "addDataSource"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.druid.v1.PoolingAddDruidDataSourceInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + + @Override + public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() { + return new StaticMethodsInterceptPoint[]{ + new StaticMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(ENHANCE_METHOD).and(takesArguments(Object.class, String.class)); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/resources/skywalking-plugin.def index b0746b3d81..95ed76e17b 100644 --- a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/resources/skywalking-plugin.def @@ -15,4 +15,5 @@ # limitations under the License. druid-1.x=org.apache.skywalking.apm.plugin.druid.v1.define.DruidPooledConnectionInstrumentation -druid-1.x=org.apache.skywalking.apm.plugin.druid.v1.define.DruidDataSourceInstrumentation \ No newline at end of file +druid-1.x=org.apache.skywalking.apm.plugin.druid.v1.define.DruidDataSourceInstrumentation +druid-1.x=org.apache.skywalking.apm.plugin.druid.v1.define.DruidDataSourceStatManagerInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/pom.xml index 363b3235ce..cc403a9c3f 100644 --- a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/pom.xml @@ -39,6 +39,11 @@ ${hikaricp.version} provided + + org.apache.skywalking + apm-jdbc-commons + ${project.version} + provided + - - \ No newline at end of file + diff --git a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/PoolingSealInterceptor.java b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/PoolingSealInterceptor.java new file mode 100644 index 0000000000..7be4b562c2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/PoolingSealInterceptor.java @@ -0,0 +1,87 @@ +/* + * 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.hikaricp; + +import com.zaxxer.hikari.HikariConfigMXBean; +import com.zaxxer.hikari.HikariDataSource; +import com.zaxxer.hikari.HikariPoolMXBean; +import org.apache.skywalking.apm.agent.core.meter.MeterFactory; +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.connectionurl.parser.URLParser; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * {@link PoolingSealInterceptor} intercepted the method of HikariCP getting connection. + */ +public class PoolingSealInterceptor implements InstanceMethodsAroundInterceptor { + + private static final String METER_NAME = "datasource"; + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + + HikariDataSource hikariDataSource = (HikariDataSource) objInst; + ConnectionInfo connectionInfo = URLParser.parser(hikariDataSource.getJdbcUrl()); + String tagValue = connectionInfo.getDatabaseName() + "_" + connectionInfo.getDatabasePeer(); + final Map>> poolMetricMap = getPoolMetrics(); + final Map>> metricConfigMap = getConfigMetrics(); + poolMetricMap.forEach((key, value) -> MeterFactory.gauge(METER_NAME, value.apply(hikariDataSource.getHikariPoolMXBean())) + .tag("name", tagValue).tag("status", key).build()); + metricConfigMap.forEach((key, value) -> MeterFactory.gauge(METER_NAME, value.apply(hikariDataSource)) + .tag("name", tagValue).tag("status", key).build()); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + + } + + private Map>> getPoolMetrics() { + final Map>> poolMetricMap = new HashMap(); + poolMetricMap.put("activeConnections", (HikariPoolMXBean hikariPoolMXBean) -> () -> (double) hikariPoolMXBean.getActiveConnections()); + poolMetricMap.put("totalConnections", (HikariPoolMXBean hikariPoolMXBean) -> () -> (double) hikariPoolMXBean.getTotalConnections()); + poolMetricMap.put("idleConnections", (HikariPoolMXBean hikariPoolMXBean) -> () -> (double) hikariPoolMXBean.getIdleConnections()); + poolMetricMap.put("threadsAwaitingConnection", (HikariPoolMXBean hikariPoolMXBean) -> () -> (double) hikariPoolMXBean.getThreadsAwaitingConnection()); + return poolMetricMap; + } + + private Map>> getConfigMetrics() { + final Map>> metricConfigMap = new HashMap(); + metricConfigMap.put("connectionTimeout", (HikariConfigMXBean hikariConfigMXBean) -> () -> (double) hikariConfigMXBean.getConnectionTimeout()); + metricConfigMap.put("validationTimeout", (HikariConfigMXBean hikariConfigMXBean) -> () -> (double) hikariConfigMXBean.getValidationTimeout()); + metricConfigMap.put("idleTimeout", (HikariConfigMXBean hikariConfigMXBean) -> () -> (double) hikariConfigMXBean.getIdleTimeout()); + metricConfigMap.put("leakDetectionThreshold", (HikariConfigMXBean hikariConfigMXBean) -> () -> (double) hikariConfigMXBean.getLeakDetectionThreshold()); + metricConfigMap.put("minimumIdle", (HikariConfigMXBean hikariConfigMXBean) -> () -> (double) hikariConfigMXBean.getMinimumIdle()); + metricConfigMap.put("maximumPoolSize", (HikariConfigMXBean hikariConfigMXBean) -> () -> (double) hikariConfigMXBean.getMaximumPoolSize()); + return metricConfigMap; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/define/HikariDataSourceInstrumentation.java b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/define/HikariDataSourceInstrumentation.java index 6273cfd308..9cc51116ed 100644 --- a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/define/HikariDataSourceInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/define/HikariDataSourceInstrumentation.java @@ -41,6 +41,7 @@ public class HikariDataSourceInstrumentation extends ClassInstanceMethodsEnhance private static final String ENHANCE_CLASS = "com.zaxxer.hikari.HikariDataSource"; private static final String ENHANCE_METHOD = "getConnection"; private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.hikaricp.PoolingGetConnectInterceptor"; + private static final String SEAL_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.hikaricp.PoolingSealInterceptor"; @Override protected ClassMatch enhanceClass() { @@ -82,6 +83,22 @@ public String getMethodsInterceptor() { return INTERCEPTOR_CLASS; } + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("seal"); + } + + @Override + public String getMethodsInterceptor() { + return SEAL_INTERCEPTOR_CLASS; + } + @Override public boolean isOverrideArgs() { return false; diff --git a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/resources/skywalking-plugin.def index 80d38e88c5..1741344aaa 100644 --- a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/resources/skywalking-plugin.def @@ -15,4 +15,4 @@ # limitations under the License. hikaricp-3.x/4.x=org.apache.skywalking.apm.plugin.hikaricp.define.HikariProxyConnectionInstrumentation -hikaricp-3.x/4.x=org.apache.skywalking.apm.plugin.hikaricp.define.HikariDataSourceInstrumentation \ No newline at end of file +hikaricp-3.x/4.x=org.apache.skywalking.apm.plugin.hikaricp.define.HikariDataSourceInstrumentation diff --git a/test/plugin/pom.xml b/test/plugin/pom.xml index d4811bf950..b9df3c6bfa 100644 --- a/test/plugin/pom.xml +++ b/test/plugin/pom.xml @@ -131,6 +131,9 @@ ${project.build.sourceDirectory} ${project.build.testSourceDirectory} +scenarios/dbcp-2.x-scenario +scenarios/druid-1.x-scenario +scenarios/druid-1.x **/*.properties, diff --git a/test/plugin/scenarios/dbcp-2.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/dbcp-2.x-scenario/config/expectedData.yaml index 2971dbdc03..1cfc8dc337 100755 --- a/test/plugin/scenarios/dbcp-2.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/dbcp-2.x-scenario/config/expectedData.yaml @@ -221,3 +221,79 @@ segmentItems: tags: - {key: url, value: 'http://localhost:8080/dbcp-2.x-scenario/case/dbcp-2.x-scenario'} - {key: http.method, value: GET} +meterItems: +- serviceName: dbcp-2.x-scenario + meterSize: 12 + meters: + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: numActive} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maxTotal} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: numIdle} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maxWaitMillis} + singleValue: ge -1 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maxIdle} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: minIdle} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: initialSize} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: core_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: max_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: active_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: queue_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 diff --git a/test/plugin/scenarios/druid-1.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/druid-1.x-scenario/config/expectedData.yaml index 68c84a560b..a20d9baea7 100644 --- a/test/plugin/scenarios/druid-1.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/druid-1.x-scenario/config/expectedData.yaml @@ -203,3 +203,91 @@ segmentItems: tags: - {key: url, value: 'http://localhost:8080/druid-1.x-scenario/case/druid-1.x-scenario'} - {key: http.method, value: GET} +meterItems: +- serviceName: druid-1.x-scenario + meterSize: 14 + meters: + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: activeCount} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: poolingCount} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: idleCount} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: lockQueueLength} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maxWaitThreadCount} + singleValue: ge -1 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: commitCount} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: connectCount} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: connectError} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: createError} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: core_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: max_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: active_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: queue_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 diff --git a/test/plugin/scenarios/hikaricp-scenario/config/expectedData.yaml b/test/plugin/scenarios/hikaricp-scenario/config/expectedData.yaml index 678d32a63d..afc4c3c5cc 100644 --- a/test/plugin/scenarios/hikaricp-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/hikaricp-scenario/config/expectedData.yaml @@ -203,3 +203,97 @@ segmentItems: tags: - {key: url, value: 'http://localhost:8080/hikaricp-scenario/case/hikaricp-scenario'} - {key: http.method, value: GET} +meterItems: +- serviceName: hikaricp-scenario + meterSize: 15 + meters: + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: activeConnections} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: totalConnections} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: idleConnections} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: threadsAwaitingConnection} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: connectionTimeout} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: validationTimeout} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: idleTimeout} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: leakDetectionThreshold} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: minimumIdle} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maximumPoolSize} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: core_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: max_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: active_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: queue_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0