[강운덕] [LUCYSUS-1744] oracle jdbc 프로파일링 추가 - statment도 구현해야 함.

git-svn-id: http://svn.bds.nhncorp.com/pe/hippo-tomcat-profiler/trunk@1728 84d0f5b1-2673-498c-a247-62c4ff18d310
This commit is contained in:
Woonduk Kang
2013-05-30 08:00:55 +00:00
parent 858f2cf78b
commit 75e4e02e83
6 changed files with 216 additions and 70 deletions
@@ -29,9 +29,7 @@ import com.profiler.modifier.db.mysql.MySQLNonRegisteringDriverModifier;
import com.profiler.modifier.db.mysql.MySQLPreparedStatementJDBC4Modifier;
import com.profiler.modifier.db.mysql.MySQLPreparedStatementModifier;
import com.profiler.modifier.db.mysql.MySQLStatementModifier;
import com.profiler.modifier.db.oracle.OraclePreparedStatementModifier;
import com.profiler.modifier.db.oracle.OracleResultSetModifier;
import com.profiler.modifier.db.oracle.OracleStatementModifier;
import com.profiler.modifier.db.oracle.*;
import com.profiler.modifier.method.MethodModifier;
import com.profiler.modifier.servlet.FilterModifier;
import com.profiler.modifier.servlet.HttpServletModifier;
@@ -189,16 +187,20 @@ public class DefaultModifierRegistry implements ModifierRegistry {
}
private void addOracleDriver() {
Modifier oracleDriverModifier = new OracleDriverModifier(byteCodeInstrumentor, agent);
addModifier(oracleDriverModifier);
// TODO oracle의 경우 connection에 대한 impl이 없음. 확인필요.
Modifier mssqlConnectionModifier = new PhysicalConnectionModifier(byteCodeInstrumentor, agent);
addModifier(mssqlConnectionModifier);
//
Modifier oraclePreparedStatementModifier = new OraclePreparedStatementModifier(byteCodeInstrumentor, agent);
addModifier(oraclePreparedStatementModifier);
Modifier oracleStatement = new OracleStatementModifier(byteCodeInstrumentor, agent);
addModifier(oracleStatement);
Modifier oracleResultSetModifier = new OracleResultSetModifier(byteCodeInstrumentor, agent);
addModifier(oracleResultSetModifier);
//
// Modifier oracleStatement = new OracleStatementModifier(byteCodeInstrumentor, agent);
// addModifier(oracleStatement);
//
// Modifier oracleResultSetModifier = new OracleResultSetModifier(byteCodeInstrumentor, agent);
// addModifier(oracleResultSetModifier);
}
private void addCubridDriver() {
@@ -68,6 +68,8 @@ public class PreparedStatementExecuteQueryInterceptor implements SimpleAroundInt
trace.recordApi(descriptor);
// trace.recordApi(apiId);
// clean 타이밍을 변경해야 될듯 하다.
// clearParameters api가 따로 있으나, 구지 캡쳐 하지 않아도 될듯함.시간남으면 하면 좋기는 함.
// ibatis 등에서 확인해봐도 cleanParameters 의 경우 대부분의 경우 일부러 호출하지 않음.
clean(target);
@@ -0,0 +1,57 @@
package com.profiler.modifier.db.oracle;
import com.profiler.Agent;
import com.profiler.interceptor.Interceptor;
import com.profiler.interceptor.bci.ByteCodeInstrumentor;
import com.profiler.interceptor.bci.InstrumentClass;
import com.profiler.interceptor.bci.InstrumentException;
import com.profiler.logging.Logger;
import com.profiler.logging.LoggerFactory;
import com.profiler.modifier.AbstractModifier;
import com.profiler.modifier.db.interceptor.DriverConnectInterceptor;
import java.security.ProtectionDomain;
/**
*
*/
public class OracleDriverModifier extends AbstractModifier {
// oracle.jdbc.driver
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public OracleDriverModifier(ByteCodeInstrumentor byteCodeInstrumentor, Agent agent) {
super(byteCodeInstrumentor, agent);
}
public String getTargetClass() {
return "oracle/jdbc/driver/OracleDriver";
}
public byte[] modify(ClassLoader classLoader, String javassistClassName, ProtectionDomain protectedDomain, byte[] classFileBuffer) {
if (logger.isInfoEnabled()) {
logger.info("Modifing. " + javassistClassName);
}
this.byteCodeInstrumentor.checkLibrary(classLoader, javassistClassName);
try {
InstrumentClass mysqlConnection = byteCodeInstrumentor.getClass(javassistClassName);
Interceptor createConnection = new DriverConnectInterceptor();
String[] params = new String[]{
"java.lang.String", "java.util.Properties"
};
mysqlConnection.addInterceptor("connect", params, createConnection);
printClassConvertComplete(javassistClassName);
return mysqlConnection.toBytecode();
} catch (InstrumentException e) {
if (logger.isWarnEnabled()) {
logger.warn(this.getClass().getSimpleName() + " modify fail. Cause:" + e.getMessage(), e);
}
return null;
}
}
}
@@ -1,89 +1,89 @@
package com.profiler.modifier.db.oracle;
import com.profiler.Agent;
import com.profiler.DefaultAgent;
import com.profiler.config.ProfilerConstant;
import com.profiler.interceptor.Interceptor;
import com.profiler.interceptor.bci.ByteCodeInstrumentor;
import com.profiler.interceptor.bci.InstrumentClass;
import com.profiler.interceptor.bci.InstrumentException;
import com.profiler.interceptor.bci.NotFoundInstrumentException;
import com.profiler.logging.Logger;
import com.profiler.logging.LoggerFactory;
import com.profiler.modifier.AbstractModifier;
import com.profiler.trace.DatabaseRequestTracer;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import com.profiler.modifier.db.interceptor.PreparedStatementBindVariableInterceptor;
import com.profiler.modifier.db.interceptor.PreparedStatementExecuteQueryInterceptor;
import com.profiler.util.JavaAssistUtils;
import com.profiler.util.PreparedStatementUtils;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import com.profiler.logging.Logger;
import java.util.List;
public class OraclePreparedStatementModifier extends AbstractModifier {
private final Logger logger = LoggerFactory.getLogger(OraclePreparedStatementModifier.class);
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public OraclePreparedStatementModifier(ByteCodeInstrumentor byteCodeInstrumentor, Agent agent) {
super(byteCodeInstrumentor, agent);
}
public String getTargetClass() {
return "oracle/jdbc/driver/OraclePreparedStatement";
return "oracle/jdbc/driver/OraclePreparedStatementWrapper";
}
public byte[] modify(ClassLoader classLoader, String javassistClassName, ProtectionDomain protectedDomain, byte[] classFileBuffer) {
if (logger.isInfoEnabled()) {
logger.info("Modifing. " + javassistClassName);
}
this.byteCodeInstrumentor.checkLibrary(classLoader, javassistClassName);
return changeMethod(javassistClassName, classFileBuffer);
}
private byte[] changeMethod(String javassistClassName, byte[] classfileBuffer) {
try {
CtClass cc = null;
updateSetInternalMethod(cc);
updateExecuteMethod(cc);
updateConstructor(cc);
InstrumentClass preparedStatement = byteCodeInstrumentor.getClass(javassistClassName);
printClassConvertComplete(javassistClassName);
Interceptor execute = new PreparedStatementExecuteQueryInterceptor();
preparedStatement.addInterceptor("execute", null, execute);
Interceptor executeQuery = new PreparedStatementExecuteQueryInterceptor();
preparedStatement.addInterceptor("executeQuery", null, executeQuery);
Interceptor executeUpdate = new PreparedStatementExecuteQueryInterceptor();
preparedStatement.addInterceptor("executeUpdate", null, executeUpdate);
return cc.toBytecode();
} catch (Exception e) {
preparedStatement.addTraceVariable("__url", "__setUrl", "__getUrl", "java.lang.Object");
preparedStatement.addTraceVariable("__sql", "__setSql", "__getSql", "java.lang.Object");
preparedStatement.addTraceVariable("__bindValue", "__setBindValue", "__getBindValue", "java.util.Map", "java.util.Collections.synchronizedMap(new java.util.HashMap());");
bindVariableIntercept(preparedStatement, classLoader, protectedDomain);
return preparedStatement.toBytecode();
} catch (InstrumentException e) {
if (logger.isWarnEnabled()) {
logger.warn(e.getMessage(), e);
logger.warn(this.getClass().getSimpleName() + " modify fail. Cause:" + e.getMessage(), e);
}
return null;
}
}
private void bindVariableIntercept(InstrumentClass preparedStatement, ClassLoader classLoader, ProtectionDomain protectedDomain) throws InstrumentException {
List<Method> bindMethod = PreparedStatementUtils.findBindVariableSetMethod();
Interceptor interceptor = new PreparedStatementBindVariableInterceptor();
int interceptorId = -1;
for (Method method : bindMethod) {
String methodName = method.getName();
String[] parameterType = JavaAssistUtils.getParameterType(method.getParameterTypes());
try {
if (interceptorId == -1) {
interceptorId = preparedStatement.addInterceptor(methodName, parameterType, interceptor);
} else {
preparedStatement.reuseInterceptor(methodName, parameterType, interceptorId);
}
} catch (NotFoundInstrumentException e) {
// bind variable setter메소드를 못찾을 경우는 그냥 경고만 표시, 에러 아님.
if (logger.isTraceEnabled()) {
logger.trace("bindVariable api not found. Cause:" + e.getMessage(), e);
}
}
}
return null;
}
private void updateSetInternalMethod(CtClass cc) throws Exception {
CtClass[] params1 = new CtClass[2];
params1[0] = null;
params1[1] = null;
CtMethod serviceMethod1 = cc.getDeclaredMethod("setStringInternal", params1);
serviceMethod1.insertBefore("{" + DatabaseRequestTracer.FQCN + ".putSqlParam($1,$2); }");
// CtClass[] params2 = new CtClass[2];
// params2[0] = classPool.getCtClass("int");
// params2[1] = classPool.getCtClass("byte[]");
// CtMethod serviceMethod2 = cc.getDeclaredMethod("setInternal",
// params2);
//
// serviceMethod2.insertBefore("{" +
// RequestDataTracer.FQCN +
// ".putSqlParam($1,$2); {");
}
private void updateConstructor(CtClass cc) throws Exception {
CtConstructor[] constructorList = cc.getConstructors();
for (CtConstructor constructor : constructorList) {
CtClass params[] = constructor.getParameterTypes();
if (params.length == 6) {
constructor.insertBefore("{" + DatabaseRequestTracer.FQCN + ".putSqlQuery(" + ProfilerConstant.REQ_DATA_TYPE_DB_QUERY + ",$2); }");
}
}
}
private void updateExecuteMethod(CtClass cc) throws Exception {
CtMethod method = cc.getDeclaredMethod("execute", null);
method.insertAfter("{" + DatabaseRequestTracer.FQCN + ".put(" + ProfilerConstant.REQ_DATA_TYPE_DB_EXECUTE_QUERY + "); }");
}
}
@@ -0,0 +1,82 @@
package com.profiler.modifier.db.oracle;
import com.profiler.Agent;
import com.profiler.interceptor.Interceptor;
import com.profiler.interceptor.bci.ByteCodeInstrumentor;
import com.profiler.interceptor.bci.InstrumentClass;
import com.profiler.interceptor.bci.InstrumentException;
import com.profiler.interceptor.bci.Type;
import com.profiler.logging.Logger;
import com.profiler.logging.LoggerFactory;
import com.profiler.modifier.AbstractModifier;
import com.profiler.modifier.db.interceptor.ConnectionCloseInterceptor;
import com.profiler.modifier.db.interceptor.PreparedStatementCreateInterceptor;
import com.profiler.modifier.db.interceptor.StatementCreateInterceptor;
import com.profiler.modifier.db.interceptor.TransactionInterceptor;
import java.security.ProtectionDomain;
public class PhysicalConnectionModifier extends AbstractModifier {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public PhysicalConnectionModifier(ByteCodeInstrumentor byteCodeInstrumentor, Agent agent) {
super(byteCodeInstrumentor, agent);
}
public String getTargetClass() {
// T4C , T2C (OCI: T2C를 상속해서 만듬) 의 최상위 구현체가 있으나,
// 해당 클래스는 PhysicalConnection 를 base로 하므로 PhysicalConnection 만 해도 될듯하다.
return "oracle/jdbc/driver/PhysicalConnection";
}
public byte[] modify(ClassLoader classLoader, String javassistClassName, ProtectionDomain protectedDomain, byte[] classFileBuffer) {
if (logger.isInfoEnabled()) {
logger.info("Modifing. " + javassistClassName);
}
this.byteCodeInstrumentor.checkLibrary(classLoader, javassistClassName);
try {
InstrumentClass oracleConnection = byteCodeInstrumentor.getClass(javassistClassName);
oracleConnection.addTraceVariable("__url", "__setUrl", "__getUrl", "java.lang.Object");
// 해당 Interceptor를 공통클래스 만들경우 system에 로드해야 된다.
// Interceptor createConnection = new ConnectionCreateInterceptor();
// String[] params = new String[] {
// "java.lang.String", "int", "java.util.Properties", "java.lang.String", "java.lang.String"
// };
// mysqlConnection.addInterceptor("getInstance", params, createConnection);
Interceptor closeConnection = new ConnectionCloseInterceptor();
oracleConnection.addInterceptor("close", null, closeConnection, Type.before);
Interceptor createStatement = new StatementCreateInterceptor();
oracleConnection.addInterceptor("createStatement", null, createStatement, Type.after);
Interceptor preparedStatement = new PreparedStatementCreateInterceptor();
oracleConnection.addInterceptor("prepareStatement", new String[]{"java.lang.String"}, preparedStatement);
Interceptor setAutocommit = new TransactionInterceptor();
oracleConnection.addInterceptor("setAutoCommit", new String[]{"boolean"}, setAutocommit);
Interceptor commit = new TransactionInterceptor();
oracleConnection.addInterceptor("commit", null, commit);
Interceptor rollback = new TransactionInterceptor();
oracleConnection.addInterceptor("rollback", null, rollback);
printClassConvertComplete(javassistClassName);
return oracleConnection.toBytecode();
} catch (InstrumentException e) {
if (logger.isWarnEnabled()) {
logger.warn(this.getClass().getSimpleName() + " modify fail. Cause:" + e.getMessage(), e);
}
return null;
}
}
}
@@ -18,12 +18,13 @@ import com.profiler.util.NumberUtils;
public class StandardHostValveInvokeInterceptor implements SimpleAroundInterceptor, ByteCodeMethodDescriptorSupport, TraceContextSupport {
private final Logger logger = LoggerFactory.getLogger(StandardHostValveInvokeInterceptor.class.getName());
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final boolean isDebug = logger.isInfoEnabled();
private MethodDescriptor descriptor;
private TraceContext traceContext;
// private ContainerAcceptor acceptor = new ContainerAcceptor(logger, ServiceType.TOMCAT);
@Override
public void before(Object target, Object[] args) {
@@ -37,6 +38,8 @@ public class StandardHostValveInvokeInterceptor implements SimpleAroundIntercept
HttpServletRequest request = (HttpServletRequest) args[0];
String requestURL = request.getRequestURI();
String remoteAddr = request.getRemoteAddr();
String port = Integer.toString(request.getServerPort());
String endPoint = request.getServerName() + ":" + port;
// remote call에 sampling flag가 설정되어있을 경우는 샘플링 대상으로 삼지 않는다.
boolean sampling = samplingEnable(request);
@@ -83,8 +86,8 @@ public class StandardHostValveInvokeInterceptor implements SimpleAroundIntercept
trace.recordServiceType(ServiceType.TOMCAT);
trace.recordRpcName(requestURL);
int port = request.getServerPort();
trace.recordEndPoint(request.getServerName() + ((port > 0) ? ":" + port : ""));
trace.recordEndPoint(endPoint);
trace.recordRemoteAddr(remoteAddr);
// 서버 맵을 통계정보에서 조회하려면 remote로 호출되는 WAS의 관계를 알아야해서 부모의 application name을 전달받음.