Talk about MySQL plugin of skywalking

Posted by gj6kings on Sun, 15 Mar 2020 04:13:11 +0100

order

This paper mainly studies MySQL plugin of skywalking

skywalking-plugin.def

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/resources/skywalking-plugin.def

mysql-8.x=org.apache.skywalking.apm.plugin.jdbc.mysql.v8.define.ConnectionImplCreateInstrumentation
mysql-8.x=org.apache.skywalking.apm.plugin.jdbc.mysql.v8.define.ConnectionInstrumentation
mysql-8.x=org.apache.skywalking.apm.plugin.jdbc.mysql.v8.define.CallableInstrumentation
mysql-8.x=org.apache.skywalking.apm.plugin.jdbc.mysql.v8.define.PreparedStatementInstrumentation
mysql-8.x=org.apache.skywalking.apm.plugin.jdbc.mysql.v8.define.StatementInstrumentation
mysql-8.x=org.apache.skywalking.apm.plugin.jdbc.mysql.v8.define.PreparedStatementSetterInstrumentation
mysql-8.x=org.apache.skywalking.apm.plugin.jdbc.mysql.v8.define.PreparedStatementNullSetterInstrumentation
mysql-8.x=org.apache.skywalking.apm.plugin.jdbc.mysql.v8.define.PreparedStatementIgnoredSetterInstrumentation
  • skywalking's MySQL plugin provides several enhancements, such as ConnectionImplCreateInstrumentation, ConnectionInstrumentation, CallableInstrumentation, PreparedStatementInstrumentation, StatementInstrumentation, PreparedStatementSetterInstrumentation, PreparedStatementNullSetterInstrumentation, PreparedStatementIgnoredSetterInstrumentation

AbstractMysqlInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/AbstractMysqlInstrumentation.java

public abstract class AbstractMysqlInstrumentation extends ClassEnhancePluginDefine {

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return null;
    }
    @Override
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
        return null;
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return null;
    }


    @Override
    protected String[] witnessClasses() {
        return new String[]{Constants.WITNESS_MYSQL_8X_CLASS};
    }
}
  • AbstractMysqlInstrumentation inherits ClassEnhancePluginDefine, and its switchclasses returns com.mysql.cj.interceptors.QueryInterceptor

ConnectionImplCreateInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/ConnectionImplCreateInstrumentation.java

public class ConnectionImplCreateInstrumentation extends AbstractMysqlInstrumentation {

    private static final String JDBC_ENHANCE_CLASS = "com.mysql.cj.jdbc.ConnectionImpl";

    private static final String CONNECT_METHOD = "getInstance";


    @Override
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
        return new StaticMethodsInterceptPoint[] {
            new StaticMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named(CONNECT_METHOD);
                }

                @Override
                public String getMethodsInterceptor() {
                    return "org.apache.skywalking.apm.plugin.jdbc.mysql.v8.ConnectionCreateInterceptor";
                }

                @Override
                public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }

    @Override
    protected ClassMatch enhanceClass() {
        return byName(JDBC_ENHANCE_CLASS);
    }
}
  • ConnectionImplCreateInstrumentation inherits AbstractMysqlInstrumentation; it uses org.apache.skywalking.apm.plugin.jdbc.mysql.v8.ConnectionCreateInterceptor to enhance getInstance method of com.mysql.cj.jdbc.ConnectionImpl

ConnectionCreateInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/ConnectionCreateInterceptor.java

public class ConnectionCreateInterceptor implements StaticMethodsAroundInterceptor {


    @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) {
        if (ret instanceof EnhancedInstance) {
            final HostInfo hostInfo = (HostInfo) allArguments[0];
            ConnectionInfo connectionInfo = URLParser.parser(hostInfo.getDatabaseUrl());
            ((EnhancedInstance) ret).setSkyWalkingDynamicField(connectionInfo);
        }
        return ret;
    }

    @Override
    public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Throwable t) {

    }
}
  • ConnectionCreateInterceptor implements StaticMethodsAroundInterceptor interface, whose afterMethod extracts hostInfo and resolves it to connectionInfo, and then sets it to skyWalkingDynamicField of ret

ConnectionInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/ConnectionInstrumentation.java

public class ConnectionInstrumentation extends AbstractMysqlInstrumentation {

    @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named(org.apache.skywalking.apm.plugin.jdbc.define.Constants.PREPARE_STATEMENT_METHOD_NAME);
                }

                @Override public String getMethodsInterceptor() {
                    return org.apache.skywalking.apm.plugin.jdbc.mysql.Constants.CREATE_PREPARED_STATEMENT_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            },
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named(org.apache.skywalking.apm.plugin.jdbc.define.Constants.PREPARE_CALL_METHOD_NAME);
                }

                @Override public String getMethodsInterceptor() {
                    return org.apache.skywalking.apm.plugin.jdbc.mysql.Constants.CREATE_CALLABLE_STATEMENT_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            },
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named(org.apache.skywalking.apm.plugin.jdbc.define.Constants.CREATE_STATEMENT_METHOD_NAME).and(takesArguments(2));
                }

                @Override public String getMethodsInterceptor() {
                    return org.apache.skywalking.apm.plugin.jdbc.mysql.Constants.CREATE_STATEMENT_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            },
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named(org.apache.skywalking.apm.plugin.jdbc.define.Constants.COMMIT_METHOD_NAME).or(named(org.apache.skywalking.apm.plugin.jdbc.define.Constants.ROLLBACK_METHOD_NAME)).or(named(org.apache.skywalking.apm.plugin.jdbc.define.Constants.CLOSE_METHOD_NAME)).or(named(org.apache.skywalking.apm.plugin.jdbc.define.Constants.RELEASE_SAVE_POINT_METHOD_NAME));
                }

                @Override public String getMethodsInterceptor() {
                    return org.apache.skywalking.apm.plugin.jdbc.define.Constants.SERVICE_METHOD_INTERCEPT_CLASS;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            },
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named("setCatalog");
                }

                @Override public String getMethodsInterceptor() {
                    return org.apache.skywalking.apm.plugin.jdbc.mysql.Constants.SET_CATALOG_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };

    }


    @Override protected ClassMatch enhanceClass() {
        return byName("com.mysql.cj.jdbc.ConnectionImpl");
    }

}
  • ConnectionInstrumentation inherits AbstractMysqlInstrumentation, which enhances the com.mysql.cj.jdbc.ConnectionImpl class; it uses org.apache.skywalking.apm.plugin.jdbc.mysql.CreatePreparedStatementInterceptor to enhance its prepareStatement method; it uses org.apache.skywalking.apm.plugin.jdbc.createcallablestatementinterceptor to enhance its prepareCall party Method; it uses org.apache.skywalking.apm.plugin.jdbc.mysql.CreateStatementInterceptor to enhance its createStatement method; it uses org.apache.skywalking.apm.plugin.jdbc.ConnectionServiceMethodInterceptor to enhance its commit, rollback, close, releaseSavepoint method; it uses org.apache.skywalking.apm.plugin.jdbc.setcatalogintercepor to enhance its setca Talog method

CreatePreparedStatementInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/CreatePreparedStatementInterceptor.java

public class CreatePreparedStatementInterceptor implements InstanceMethodsAroundInterceptor {
    @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 (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) {

    }
}
  • CreatePreparedStatementInterceptor implements the instancemethods aroundinterceptor interface, whose afterMethod method will set statementenhanceinfo to ret's skyWalkingDynamicField

CreateCallableStatementInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/CreateCallableStatementInterceptor.java

public class CreateCallableStatementInterceptor implements InstanceMethodsAroundInterceptor {
    @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 (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) {

    }
}
  • CreateCallableStatementInterceptor implements the instancemethods aroundinterceptor interface, whose afterMethod method sets statementenhanceinfo to ret's skyWalkingDynamicField

CreateStatementInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/CreateStatementInterceptor.java

public class CreateStatementInterceptor implements InstanceMethodsAroundInterceptor {
    @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 (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) {

    }
}
  • CreateStatementInterceptor implements the instancemethods aroundinterceptor interface, whose afterMethod method sets StatementEnhanceInfos to ret's skyWalkingDynamicField

ConnectionServiceMethodInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/ConnectionServiceMethodInterceptor.java

public class ConnectionServiceMethodInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public final void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes,
        MethodInterceptResult result) throws Throwable {
        ConnectionInfo connectInfo = (ConnectionInfo)objInst.getSkyWalkingDynamicField();
        if (connectInfo != null) {
            AbstractSpan span = ContextManager.createExitSpan(connectInfo.getDBType() + "/JDBI/Connection/" + method.getName(), connectInfo.getDatabasePeer());
            Tags.DB_TYPE.set(span, "sql");
            Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName());
            Tags.DB_STATEMENT.set(span, "");
            span.setComponent(connectInfo.getComponent());
            SpanLayer.asDB(span);
        }
    }

    @Override
    public final Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes,
        Object ret) throws Throwable {
        ConnectionInfo connectInfo = (ConnectionInfo)objInst.getSkyWalkingDynamicField();
        if (connectInfo != null) {
            ContextManager.stopSpan();
        }
        return ret;
    }

    @Override public final void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, Throwable t) {
        ContextManager.activeSpan().errorOccurred().log(t);
    }

}
  • ConnectionServiceMethodInterceptor implements the instancemethods aroundinterceptor interface. Its beforeMethod method sets db'type, db'instance and db'statement. Its afterMethod method executes ContextManager.stopSpan() when connectInfo is not null, and its handleMethodException method executes contextmanager. Activespan(). Erroreoccurred(). Log (T)

SetCatalogInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/SetCatalogInterceptor.java

public class SetCatalogInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
        MethodInterceptResult result) throws Throwable {
        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) throws Throwable {
        return ret;
    }

    @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, Throwable t) {
    }
}
  • SetCatalogInterceptor implements the instancemethods aroundinterceptor interface. Its beforeMethod method method sets the databaseName to the dynamicField of type ConnectionInfo

CallableInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/CallableInstrumentation.java

public class CallableInstrumentation extends AbstractMysqlInstrumentation {
    private static final String ENHANCE_CLASS = "com.mysql.cj.jdbc.CallableStatement";
    private static final String SERVICE_METHOD_INTERCEPTOR = org.apache.skywalking.apm.plugin.jdbc.mysql.Constants.PREPARED_STATEMENT_EXECUTE_METHODS_INTERCEPTOR;

    @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named("execute")
                        .or(named("executeQuery"))
                        .or(named("executeUpdate"));
                }

                @Override public String getMethodsInterceptor() {
                    return SERVICE_METHOD_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }

    @Override protected ClassMatch enhanceClass() {
        return byName(ENHANCE_CLASS);
    }

}
  • CallableInstrumentation inherits AbstractMysqlInstrumentation, which uses org.apache.skywalking.apm.plugin.jdbc.mysql.PreparedStatementExecuteMethodsInterceptor to enhance the execute, executeQuery, and executeUpdate methods of com.mysql.cj.jdbc.CallableStatement

PreparedStatementExecuteMethodsInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/PreparedStatementExecuteMethodsInterceptor.java

public class PreparedStatementExecuteMethodsInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public final void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes,
        MethodInterceptResult result) throws Throwable {
        StatementEnhanceInfos cacheObject = (StatementEnhanceInfos)objInst.getSkyWalkingDynamicField();
        ConnectionInfo connectInfo = cacheObject.getConnectionInfo();
        /**
         * For avoid NPE. In this particular case, Execute sql inside the {@link com.mysql.jdbc.ConnectionImpl} constructor,
         * before the interceptor sets the connectionInfo.
         *
         * @see JDBCDriverInterceptor#afterMethod(EnhancedInstance, Method, Object[], Class[], Object)
         */
        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());
            Tags.DB_STATEMENT.set(span, cacheObject.getSql());
            span.setComponent(connectInfo.getComponent());

            if (Config.Plugin.MySQL.TRACE_SQL_PARAMETERS) {
                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) + "...";
                    }
                    SQL_PARAMETERS.set(span, parameterString);
                }
            }

            SpanLayer.asDB(span);
        }
    }

    @Override
    public final Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes,
        Object ret) throws Throwable {
        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 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;
    }
}
  • PreparedStatementExecuteMethodsInterceptor implements the InstanceMethodsAroundInterceptor interface, and its beforeMethod method sets db'type, db'instance, db'statement, sql'parameters (if config.plugin.mysql.trace'sql'parameters is true); its afterMethod executes ContextManager.stopSpan(); its handleMethodException() when cacheObject.getConnectionInfo() is not null The eption method executes contextmanager. Activespan(). Erroreoccurred(). Log (T) when cacheObject.getConnectionInfo() is not null

PreparedStatementInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/PreparedStatementInstrumentation.java

public class PreparedStatementInstrumentation extends AbstractMysqlInstrumentation {

    private static final String PREPARED_STATEMENT_CLASS_NAME = "com.mysql.cj.jdbc.ClientPreparedStatement";
    private static final String PREPARED_STATEMENT_SERVER_SIDE_CLASS_NAME = "com.mysql.cj.jdbc.ServerPreparedStatement";

    private static final String SERVICE_METHOD_INTERCEPTOR =  Constants.PREPARED_STATEMENT_EXECUTE_METHODS_INTERCEPTOR;

    @Override public final ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named("execute")
                        .or(named("executeQuery"))
                        .or(named("executeUpdate"))
                        .or(named("executeLargeUpdate"));
                }

                @Override public String getMethodsInterceptor() {
                    return SERVICE_METHOD_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }

    @Override protected ClassMatch enhanceClass() {
        return byMultiClassMatch(PREPARED_STATEMENT_CLASS_NAME, PREPARED_STATEMENT_SERVER_SIDE_CLASS_NAME);
    }
}
  • PreparedStatementInstrumentation inherits AbstractMysqlInstrumentation, which uses org.apache.skywalking.apm.plugin.jdbc.mysql.preparedstatementexecutemethods interceptor to enhance the execute, executeQuery, executeUpdate, executelageupdate methods of com.mysql.cj.jdbc.ClientPreparedStatement

StatementInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/StatementInstrumentation.java

public class StatementInstrumentation extends AbstractMysqlInstrumentation {
    private static final String SERVICE_METHOD_INTERCEPTOR =  org.apache.skywalking.apm.plugin.jdbc.mysql.Constants.STATEMENT_EXECUTE_METHODS_INTERCEPTOR;
    public static final String MYSQL8_STATEMENT_CLASS_NAME = "com.mysql.cj.jdbc.StatementImpl";

    @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named("execute")
                        .or(named("executeQuery"))
                        .or(named("executeUpdate"))
                        .or(named("executeLargeUpdate"))
                        .or(named("executeBatchInternal"))
                        .or(named("executeUpdateInternal"))
                        .or(named("executeQuery"))
                        .or(named("executeBatch"));
                }

                @Override public String getMethodsInterceptor() {
                    return SERVICE_METHOD_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }

    @Override protected ClassMatch enhanceClass() {
        return byName(MYSQL8_STATEMENT_CLASS_NAME);
    }
}
  • StatementInstrumentation inherits AbstractMysqlInstrumentation, which uses org.apache.skywalking.apm.plugin.jdbc.mysql.StatementExecuteMethodsInterceptor to enhance the execution, executeQuery, executeUpdate, executelageupdate, executebattinternal, executeUpdateInternal, executeQuery, executeBatch methods of com.mysql.cj.jdbc.StatementImpl

StatementExecuteMethodsInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptor.java

public class StatementExecuteMethodsInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public final void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes,
        MethodInterceptResult result) throws Throwable {
        StatementEnhanceInfos cacheObject = (StatementEnhanceInfos)objInst.getSkyWalkingDynamicField();
        ConnectionInfo connectInfo = cacheObject.getConnectionInfo();
        /**
         * To protected the code occur NullPointException. because mysql execute system sql when constructor method in
         * {@link com.mysql.jdbc.ConnectionImpl} class executed. but the interceptor set the connection Info after
         * the constructor method executed.
         *
         * @see JDBCDriverInterceptor#afterMethod(EnhancedInstance, Method, Object[], Class[], Object)
         */
        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());

            /**
             * The first argument of all intercept method in `com.mysql.jdbc.StatementImpl` class is SQL, except the
             * `executeBatch` method that the jdbc plugin need to trace, because of this method argument size is zero.
             */
            String sql = "";
            if (allArguments.length > 0) {
                sql = (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) throws Throwable {
        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;
    }
}
  • StatementExecuteMethodsInterceptor implements the InstanceMethodsAroundInterceptor interface. Its beforeMethod method sets db'type, db'instance and db'statement. Its afterMethod method executes ContextManager.stopSpan() when cacheObject.getConnectionInfo() is not null. Its handleMethodException method executes contextmanager when cacheObject.getConnectionInfo() is not null activeSpan().errorOccurred().log(t)

PreparedStatementSetterInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/PreparedStatementSetterInstrumentation.java

public class PreparedStatementSetterInstrumentation extends PreparedStatementInstrumentation {

    @Override
    public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new PSSetterDefinitionOfJDBCInstrumentation(false)
        };
    }

}
  • PreparedStatementSetterInstrumentation inherits PreparedStatementInstrumentation, whose getinstancemethods interceptpoints method returns pssetterdefinitionofjdbc instrumentation (false)

PSSetterDefinitionOfJDBCInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/PSSetterDefinitionOfJDBCInstrumentation.java

public class PSSetterDefinitionOfJDBCInstrumentation implements InstanceMethodsInterceptPoint {
    private final boolean ignorable;

    public PSSetterDefinitionOfJDBCInstrumentation(boolean ignorable) {
        this.ignorable = ignorable;
    }

    @Override
    public ElementMatcher<MethodDescription> getMethodsMatcher() {
        ElementMatcher.Junction<MethodDescription> matcher = none();

        if (Config.Plugin.MySQL.TRACE_SQL_PARAMETERS || Config.Plugin.POSTGRESQL.TRACE_SQL_PARAMETERS) {
            final Set<String> setters = ignorable ? PS_IGNORABLE_SETTERS : PS_SETTERS;
            for (String setter : setters) {
                matcher = matcher.or(named(setter));
            }
        }

        return matcher;
    }

    @Override
    public String getMethodsInterceptor() {
        return ignorable
            ? Constants.PREPARED_STATEMENT_IGNORABLE_SETTER_METHODS_INTERCEPTOR
            : Constants.PREPARED_STATEMENT_SETTER_METHODS_INTERCEPTOR;
    }

    @Override
    public boolean isOverrideArgs() {
        return false;
    }
}
  • Pssetterdefinitionofjdbc instrumentation implements the instancemethods interceptpoint interface, and its getMethodsMatcher method constructs the matcher and methodsinterpoint according to whether PS ﹤ ignorable ﹤ sets or PS ﹤ sets can be used for ignorable

PreparedStatementNullSetterInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/PreparedStatementNullSetterInstrumentation.java

public class PreparedStatementNullSetterInstrumentation extends PreparedStatementInstrumentation {

    @Override
    public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint()
        };
    }

}
  • PreparedStatementNullSetterInstrumentation inherits PreparedStatementInstrumentation, whose getinstancemethods interceptpoints method returns JDBC preparedstatementnullsetterinstancemethods interceptpoint

JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint.java

public final class JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint implements InstanceMethodsInterceptPoint {
    @Override
    public ElementMatcher<MethodDescription> getMethodsMatcher() {
        return named("setNull");
    }

    @Override
    public String getMethodsInterceptor() {
        return Constants.PREPARED_STATEMENT_NULL_SETTER_METHODS_INTERCEPTOR;
    }

    @Override
    public boolean isOverrideArgs() {
        return false;
    }
}
  • JDBC preparedstatementnullsetterinstancemethods interceptpoint implements the instancemethods interceptpoint interface, which uses org.apache.skywalking.apm.plugin.jdbc.jdbc.jdbc preparedstatementnullsetterinterinterceptor to enhance the setNull method

PreparedStatementIgnoredSetterInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/v8/define/PreparedStatementIgnoredSetterInstrumentation.java

public class PreparedStatementIgnoredSetterInstrumentation extends PreparedStatementInstrumentation {

    @Override
    public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new PSSetterDefinitionOfJDBCInstrumentation(true)
        };
    }

}
  • PreparedStatementIgnoredSetterInstrumentation inherits PreparedStatementInstrumentation, whose getinstancemethods interceptpoints method returns pssetterdefinitionofjdbc instrumentation (true)

Summary

skywalking's MySQL plugin provides several enhancements, such as ConnectionImplCreateInstrumentation, ConnectionInstrumentation, CallableInstrumentation, PreparedStatementInstrumentation, StatementInstrumentation, PreparedStatementSetterInstrumentation, PreparedStatementNullSetterInstrumentation, PreparedStatementIgnoredSetterInstrumentation

doc

Topics: Programming MySQL JDBC Apache Java