[#2496] Add datasource metric collection

Datasource metric collection support in hikaricp.
This commit is contained in:
koo.taejin
2017-03-07 18:03:18 +09:00
committed by Jaehong Kim
parent 0e3ceb3661
commit 7bc4425353
10 changed files with 433 additions and 34 deletions
@@ -31,7 +31,7 @@ public final class CommonsDbcpConstants {
public static final String ACCESSOR_DATASOURCE_MONITOR = "com.navercorp.pinpoint.plugin.commons.dbcp.DataSourceMonitorAccessor";
public static final String INTERCEPTOR_CONSTRUCTOR = "com.navercorp.pinpoint.plugin.commons.dbcp.interceptor.DataSourceConstructorInterceptor";
public static final String INTERCEPTOR_CLOSE = "com.navercorp.pinpoint.plugin.commons.dbcp.interceptor.DataSourceCloseInterceptor";
public static final String INTERCEPTOR_CLOSE = "com.navercorp.pinpoint.plugin.commons.dbcp.interceptor.DataSourceCloseInterceptor";
public static final String INTERCEPTOR_GET_CONNECTION = "com.navercorp.pinpoint.plugin.commons.dbcp.interceptor.DataSourceGetConnectionInterceptor";
public static final String INTERCEPTOR_CLOSE_CONNECTION = "com.navercorp.pinpoint.plugin.commons.dbcp.interceptor.DataSourceCloseConnectionInterceptor";
@@ -0,0 +1,27 @@
/*
* Copyright 2017 NAVER Corp.
*
* Licensed 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 com.navercorp.pinpoint.plugin.hikaricp;
/**
* @author Taejin Koo
*/
public interface DataSourceMonitorAccessor {
void _$PINPOINT$_setDataSourceMonitor(HikariCpDataSourceMonitor dataSourceMonitor);
HikariCpDataSourceMonitor _$PINPOINT$_getDataSourceMonitor();
}
@@ -0,0 +1,39 @@
/*
* Copyright 2017 NAVER Corp.
*
* Licensed 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 com.navercorp.pinpoint.plugin.hikaricp;
import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.common.trace.ServiceTypeFactory;
/**
* @author Taejin Koo
*/
public final class HikariCpConstants {
public static final String SCOPE = "HIKARICP_SCOPE";
public static final ServiceType SERVICE_TYPE = ServiceTypeFactory.of(6060, "HIKARICP");
public static final String ACCESSOR_DATASOURCE_MONITOR = "com.navercorp.pinpoint.plugin.hikaricp.DataSourceMonitorAccessor";
public static final String INTERCEPTOR_CONSTRUCTOR = "com.navercorp.pinpoint.plugin.hikaricp.interceptor.DataSourceConstructorInterceptor";
public static final String INTERCEPTOR_CLOSE = "com.navercorp.pinpoint.plugin.hikaricp.interceptor.DataSourceCloseInterceptor";
public static final String INTERCEPTOR_GET_CONNECTION = "com.navercorp.pinpoint.plugin.hikaricp.interceptor.DataSourceGetConnectionInterceptor";
public static final String INTERCEPTOR_CLOSE_CONNECTION = "com.navercorp.pinpoint.plugin.hikaricp.interceptor.DataSourceCloseConnectionInterceptor";
}
@@ -0,0 +1,124 @@
/*
* Copyright 2017 NAVER Corp.
*
* Licensed 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 com.navercorp.pinpoint.plugin.hikaricp;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.bootstrap.plugin.monitor.DataSourceMonitor;
import com.navercorp.pinpoint.common.trace.ServiceType;
import java.lang.reflect.Method;
/**
* @author Taejin Koo
*/
public class HikariCpDataSourceMonitor implements DataSourceMonitor {
private final PLogger logger = PLoggerFactory.getLogger(getClass());
private final String jdbcUrl;
private final Object object;
private final Method getActiveConnectionsMethod;
private final Method getTotalConnectionsMethod;
public HikariCpDataSourceMonitor(Object object, String jdbcUrl) {
this.object = object;
this.jdbcUrl = jdbcUrl;
try {
this.getActiveConnectionsMethod = getActiveConnectionsMethod(object);
this.getTotalConnectionsMethod = getTotalConnectionsMethod(object);
} catch (Exception e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
private volatile boolean closed = false;
private Method getActiveConnectionsMethod(Object object) throws NoSuchMethodException {
Method getActiveConnectionsMethod = object.getClass().getMethod("getActiveConnections");
if (getActiveConnectionsMethod == null) {
throw new IllegalArgumentException("object must has getActiveConnections method");
}
Class<?> returnType = getActiveConnectionsMethod.getReturnType();
if (int.class != returnType) {
throw new IllegalArgumentException("invalid return type. expected:int, actual:" + returnType);
}
return getActiveConnectionsMethod;
}
private Method getTotalConnectionsMethod(Object object) throws NoSuchMethodException {
Method getTotalConnections = object.getClass().getMethod("getTotalConnections");
if (getTotalConnections == null) {
throw new IllegalArgumentException("object must has getTotalConnections method");
}
Class<?> returnType = getTotalConnections.getReturnType();
if (int.class != returnType) {
throw new IllegalArgumentException("invalid return type. expected:int, actual:" + returnType);
}
return getTotalConnections;
}
@Override
public ServiceType getServiceType() {
return HikariCpConstants.SERVICE_TYPE;
}
@Override
public String getUrl() {
return jdbcUrl;
}
@Override
public int getActiveConnectionSize() {
try {
Object result = getActiveConnectionsMethod.invoke(object);
return (Integer) result;
} catch (Exception e) {
logger.info("failed while executing getActiveConnectionSize()");
}
return -1;
}
@Override
public int getMaxConnectionSize() {
try {
Object result = getTotalConnectionsMethod.invoke(object);
return (Integer) result;
} catch (Exception e) {
logger.info("failed while executing getActiveConnectionSize()");
}
return -1;
}
@Override
public boolean isDisabled() {
return closed;
}
public void close() {
closed = true;
}
}
@@ -26,7 +26,7 @@ public class HikariCpMetadataProvider implements TraceMetadataProvider {
@Override
public void setup(TraceMetadataSetupContext context) {
context.addServiceType(HikariCpPlugin.HIKARICP_SERVICE_TYPE);
context.addServiceType(HikariCpConstants.SERVICE_TYPE);
}
}
@@ -18,6 +18,7 @@ package com.navercorp.pinpoint.plugin.hikaricp;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod;
import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate;
@@ -26,8 +27,6 @@ import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin;
import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext;
import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.common.trace.ServiceTypeFactory;
import java.security.ProtectionDomain;
@@ -36,8 +35,6 @@ import java.security.ProtectionDomain;
*/
public class HikariCpPlugin implements ProfilerPlugin, TransformTemplateAware {
public static final ServiceType HIKARICP_SERVICE_TYPE = ServiceTypeFactory.of(6060, "HIKARICP");
public static final String HIKARICP_SCOPE = "HIKARICP_SCOPE";
private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
private HikariCpConfig config;
@@ -56,6 +53,20 @@ public class HikariCpPlugin implements ProfilerPlugin, TransformTemplateAware {
if (config.isProfileClose()) {
addPoolGuardConnectionWrapperTransformer();
}
addHikariPoolTransformer();
}
private void addBasicDataSourceTransformer() {
transformTemplate.transform("com.zaxxer.hikari.HikariDataSource", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
target.addInterceptor(HikariCpConstants.INTERCEPTOR_GET_CONNECTION);
return target.toBytecode();
}
});
}
private void addPoolGuardConnectionWrapperTransformer() {
@@ -66,17 +77,12 @@ public class HikariCpPlugin implements ProfilerPlugin, TransformTemplateAware {
transformTemplate.transform("com.zaxxer.hikari.proxy.ConnectionProxy", new ConnectionTransformCallback());
}
private void addBasicDataSourceTransformer() {
transformTemplate.transform("com.zaxxer.hikari.HikariDataSource", new TransformCallback() {
private void addHikariPoolTransformer() {
// 1.3.7 ~ 2.6.x (without 2.3.x)
transformTemplate.transform("com.zaxxer.hikari.pool.HikariPool", new HikariPoolTransformCallback());
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
target.addInterceptor("com.navercorp.pinpoint.plugin.hikaricp.interceptor.DataSourceGetConnectionInterceptor");
return target.toBytecode();
}
});
// 2.3.x
transformTemplate.transform("com.zaxxer.hikari.pool.BaseHikariPool", new HikariPoolTransformCallback());
}
@Override
@@ -84,14 +90,60 @@ public class HikariCpPlugin implements ProfilerPlugin, TransformTemplateAware {
this.transformTemplate = transformTemplate;
}
static class ConnectionTransformCallback implements TransformCallback {
private static class ConnectionTransformCallback implements TransformCallback {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
target.addInterceptor("com.navercorp.pinpoint.plugin.hikaricp.interceptor.DataSourceCloseInterceptor");
target.addInterceptor(HikariCpConstants.INTERCEPTOR_CLOSE_CONNECTION);
return target.toBytecode();
}
}
private static class HikariPoolTransformCallback implements TransformCallback {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
if (isAvailableDataSourceMonitor(target)) {
// ~ 2.4.0
InstrumentMethod constructor = target.getConstructor("com.zaxxer.hikari.HikariConfig", "java.lang.String", "java.lang.String");
if (constructor != null) {
addDataSourceMonitorInterceptor(target, constructor);
return target.toBytecode();
}
// 2.4.1 ~
constructor = target.getConstructor("com.zaxxer.hikari.HikariConfig");
if (constructor != null) {
addDataSourceMonitorInterceptor(target, constructor);
return target.toBytecode();
}
}
return target.toBytecode();
}
private boolean isAvailableDataSourceMonitor(InstrumentClass target) {
InstrumentMethod getActiveConnectionsMethod = target.getDeclaredMethod("getActiveConnections");
if (getActiveConnectionsMethod == null || !int.class.getName().equals(getActiveConnectionsMethod.getReturnType())) {
return false;
}
InstrumentMethod getTotalConnectionsMethod = target.getDeclaredMethod("getTotalConnections");
if (getTotalConnectionsMethod == null || !int.class.getName().equals(getTotalConnectionsMethod.getReturnType())) {
return false;
}
return true;
}
private void addDataSourceMonitorInterceptor(InstrumentClass target, InstrumentMethod constructor) throws InstrumentException {
target.addField(HikariCpConstants.ACCESSOR_DATASOURCE_MONITOR);
constructor.addInterceptor(HikariCpConstants.INTERCEPTOR_CONSTRUCTOR);
constructor.addInterceptor(HikariCpConstants.INTERCEPTOR_CLOSE);
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2017 NAVER Corp.
*
* Licensed 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 com.navercorp.pinpoint.plugin.hikaricp.interceptor;
import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethod;
import com.navercorp.pinpoint.plugin.hikaricp.HikariCpConstants;
/**
* @author Taejin Koo
*/
@Scope(HikariCpConstants.SCOPE)
@TargetMethod(name="close")
public class DataSourceCloseConnectionInterceptor extends SpanEventSimpleAroundInterceptorForPlugin {
public DataSourceCloseConnectionInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
super(traceContext, descriptor);
}
@Override
public void doInBeforeTrace(SpanEventRecorder recorder, final Object target, Object[] args) {
}
@Override
public void doInAfterTrace(SpanEventRecorder trace, Object target, Object[] args, Object result, Throwable throwable) {
trace.recordServiceType(HikariCpConstants.SERVICE_TYPE);
trace.recordApi(getMethodDescriptor());
trace.recordException(throwable);
}
}
@@ -17,33 +17,47 @@
package com.navercorp.pinpoint.plugin.hikaricp.interceptor;
import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin;
import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethod;
import com.navercorp.pinpoint.plugin.hikaricp.HikariCpPlugin;
import com.navercorp.pinpoint.bootstrap.plugin.monitor.DataSourceMonitorRegistry;
import com.navercorp.pinpoint.plugin.hikaricp.DataSourceMonitorAccessor;
import com.navercorp.pinpoint.plugin.hikaricp.HikariCpConstants;
import com.navercorp.pinpoint.plugin.hikaricp.HikariCpDataSourceMonitor;
/**
* @author Taejin Koo
*/
@Scope(HikariCpPlugin.HIKARICP_SCOPE)
@TargetMethod(name="close")
public class DataSourceCloseInterceptor extends SpanEventSimpleAroundInterceptorForPlugin {
@Scope(HikariCpConstants.SCOPE)
@TargetMethod(name="shutdown")
public class DataSourceCloseInterceptor implements AroundInterceptor {
public DataSourceCloseInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
super(traceContext, descriptor);
private final TraceContext traceContext;
private final DataSourceMonitorRegistry dataSourceMonitorRegistry;
private final MethodDescriptor methodDescriptor;
public DataSourceCloseInterceptor(TraceContext traceContext, DataSourceMonitorRegistry dataSourceMonitorRegistry, MethodDescriptor methodDescriptor) {
this.traceContext = traceContext;
this.dataSourceMonitorRegistry = dataSourceMonitorRegistry;
this.methodDescriptor = methodDescriptor;
}
@Override
public void doInBeforeTrace(SpanEventRecorder recorder, final Object target, Object[] args) {
public void before(Object target, Object[] args) {
if ((target instanceof DataSourceMonitorAccessor)) {
HikariCpDataSourceMonitor dataSourceMonitor = ((DataSourceMonitorAccessor) target)._$PINPOINT$_getDataSourceMonitor();
if (dataSourceMonitor != null) {
((DataSourceMonitorAccessor) target)._$PINPOINT$_setDataSourceMonitor(null);
dataSourceMonitor.close();
dataSourceMonitorRegistry.unregister(dataSourceMonitor);
}
}
}
@Override
public void doInAfterTrace(SpanEventRecorder trace, Object target, Object[] args, Object result, Throwable throwable) {
trace.recordServiceType(HikariCpPlugin.HIKARICP_SERVICE_TYPE);
trace.recordApi(getMethodDescriptor());
trace.recordException(throwable);
public void after(Object target, Object[] args, Object result, Throwable throwable) {
}
}
@@ -0,0 +1,94 @@
/*
* Copyright 2017 NAVER Corp.
*
* Licensed 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 com.navercorp.pinpoint.plugin.hikaricp.interceptor;
import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.bootstrap.plugin.monitor.DataSourceMonitorRegistry;
import com.navercorp.pinpoint.bootstrap.util.InterceptorUtils;
import com.navercorp.pinpoint.plugin.hikaricp.DataSourceMonitorAccessor;
import com.navercorp.pinpoint.plugin.hikaricp.HikariCpConstants;
import com.navercorp.pinpoint.plugin.hikaricp.HikariCpDataSourceMonitor;
import java.lang.reflect.Method;
/**
* @author Taejin Koo
*/
@Scope(HikariCpConstants.SCOPE)
public class DataSourceConstructorInterceptor implements AroundInterceptor {
private final PLogger logger = PLoggerFactory.getLogger(getClass());
private final TraceContext traceContext;
private final DataSourceMonitorRegistry dataSourceMonitorRegistry;
private final MethodDescriptor methodDescriptor;
public DataSourceConstructorInterceptor(TraceContext traceContext, DataSourceMonitorRegistry dataSourceMonitorRegistry, MethodDescriptor methodDescriptor) {
this.traceContext = traceContext;
this.dataSourceMonitorRegistry = dataSourceMonitorRegistry;
this.methodDescriptor = methodDescriptor;
}
@Override
public void before(Object target, Object[] args) {
}
@Override
public void after(Object target, Object[] args, Object result, Throwable throwable) {
if (!InterceptorUtils.isSuccess(throwable)) {
return;
}
if (args.length >= 1) {
try {
String jdbcUrl = getJdbcUrl(args[0]);
HikariCpDataSourceMonitor dataSourceMonitor = new HikariCpDataSourceMonitor(target, jdbcUrl);
dataSourceMonitorRegistry.register(dataSourceMonitor);
if (target instanceof DataSourceMonitorAccessor) {
((DataSourceMonitorAccessor) target)._$PINPOINT$_setDataSourceMonitor(dataSourceMonitor);
}
} catch (Exception e) {
logger.info("failed while creating HikariCpDataSourceMonitor. message:{}", e.getMessage(), e);
}
}
}
private String getJdbcUrl(Object object) {
try {
if (object == null) {
return null;
}
Method getJdbcUrl = object.getClass().getMethod("getJdbcUrl");
if (getJdbcUrl == null) {
return null;
}
return String.valueOf(getJdbcUrl.invoke(object));
} catch (Exception e) {
logger.info("failed while executing getJdbcUrl(). message:{}", e.getMessage(), e);
}
return null;
}
}
@@ -23,12 +23,12 @@ import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterce
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethod;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethods;
import com.navercorp.pinpoint.plugin.hikaricp.HikariCpPlugin;
import com.navercorp.pinpoint.plugin.hikaricp.HikariCpConstants;
/**
* @author Taejin Koo
*/
@Scope(HikariCpPlugin.HIKARICP_SCOPE)
@Scope(HikariCpConstants.SCOPE)
@TargetMethods({
@TargetMethod(name="getConnection"),
@TargetMethod(name="getConnection", paramTypes={"java.lang.String", "java.lang.String"})
@@ -45,7 +45,7 @@ public class DataSourceGetConnectionInterceptor extends SpanEventSimpleAroundInt
@Override
public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) {
recorder.recordServiceType(HikariCpPlugin.HIKARICP_SERVICE_TYPE);
recorder.recordServiceType(HikariCpConstants.SERVICE_TYPE);
if (args == null) {
// getConnection() without any arguments
recorder.recordApi(getMethodDescriptor());