聊聊skywalking的mysql-plugin

本文主要研究一下skywalking的mysql-plugin


聊聊skywalking的mysql-plugin


skywalking-plugin.def

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

<code>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/<code>
  • skywalking的mysql-plugin提供了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

<code>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};
  }
}/<code>
  • AbstractMysqlInstrumentation继承了ClassEnhancePluginDefine,其witnessClasses返回的是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

<code>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);
  }
}/<methoddescription>/<code>
  • ConnectionImplCreateInstrumentation继承了AbstractMysqlInstrumentation;它使用org.apache.skywalking.apm.plugin.jdbc.mysql.v8.ConnectionCreateInterceptor增强了com.mysql.cj.jdbc.ConnectionImpl的getInstance方法

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

<code>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) {

  }
}/<code>
  • ConnectionCreateInterceptor实现了StaticMethodsAroundInterceptor接口,其afterMethod方法提取hostInfo解析为connectionInfo然后设置给ret的skyWalkingDynamicField

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

<code>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");
  }

}/<methoddescription>/<methoddescription>/<methoddescription>/<methoddescription>/<methoddescription>/<code>
  • ConnectionInstrumentation继承了AbstractMysqlInstrumentation,它增强的是com.mysql.cj.jdbc.ConnectionImpl类;它使用org.apache.skywalking.apm.plugin.jdbc.mysql.CreatePreparedStatementInterceptor增强其prepareStatement方法;它使用org.apache.skywalking.apm.plugin.jdbc.mysql.CreateCallableStatementInterceptor增强其prepareCall方法;它使用org.apache.skywalking.apm.plugin.jdbc.mysql.CreateStatementInterceptor增强其createStatement方法;它使用org.apache.skywalking.apm.plugin.jdbc.ConnectionServiceMethodInterceptor增强其commit、rollback、close、releaseSavepoint方法;它使用org.apache.skywalking.apm.plugin.jdbc.mysql.SetCatalogInterceptor增强其setCatalog方法

CreatePreparedStatementInterceptor

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

<code>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) {

  }
}/<code>
  • CreatePreparedStatementInterceptor实现了InstanceMethodsAroundInterceptor接口,其afterMethod方法会将StatementEnhanceInfos设置给ret的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

<code>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) {

  }
}/<code>
  • CreateCallableStatementInterceptor实现了InstanceMethodsAroundInterceptor接口,其afterMethod方法将StatementEnhanceInfos设置给ret的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

<code>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) {

  }
}/<code>
  • CreateStatementInterceptor实现了InstanceMethodsAroundInterceptor接口,其afterMethod方法将StatementEnhanceInfos设置给ret的skyWalkingDynamicField

ConnectionServiceMethodInterceptor

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

<code>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);
  }

}/<code>
  • ConnectionServiceMethodInterceptor实现了InstanceMethodsAroundInterceptor接口,其beforeMethod方法设置DB_TYPE、DB_INSTANCE、DB_STATEMENT;其afterMethod方法在connectInfo不为null时执行ContextManager.stopSpan();其handleMethodException方法执行ContextManager.activeSpan().errorOccurred().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

<code>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) {
  }
}/<code>
  • SetCatalogInterceptor实现了InstanceMethodsAroundInterceptor接口,其beforeMethod方法给类型为ConnectionInfo的dynamicField设置databaseName

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

<code>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);
  }

}/<methoddescription>/<code>
  • CallableInstrumentation继承了AbstractMysqlInstrumentation,它使用org.apache.skywalking.apm.plugin.jdbc.mysql.PreparedStatementExecuteMethodsInterceptor增强了com.mysql.cj.jdbc.CallableStatement的execute、executeQuery、executeUpdate方法

PreparedStatementExecuteMethodsInterceptor

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

<code>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;
  }
}/<code>
  • PreparedStatementExecuteMethodsInterceptor实现了InstanceMethodsAroundInterceptor接口,其beforeMethod方法设置DB_TYPE、DB_INSTANCE、DB_STATEMENT、SQL_PARAMETERS(Config.Plugin.MySQL.TRACE_SQL_PARAMETERS为true的话);其afterMethod方法在cacheObject.getConnectionInfo()不为null时执行ContextManager.stopSpan();其handleMethodException方法在cacheObject.getConnectionInfo()不为null时执行ContextManager.activeSpan().errorOccurred().log(t)

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

<code>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);
  }
}/<methoddescription>/<code>
  • PreparedStatementInstrumentation继承了AbstractMysqlInstrumentation,它使用org.apache.skywalking.apm.plugin.jdbc.mysql.PreparedStatementExecuteMethodsInterceptor增强了com.mysql.cj.jdbc.ClientPreparedStatement的execute、executeQuery、executeUpdate、executeLargeUpdate方法

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

<code>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);
  }
}/<methoddescription>/<code>
  • StatementInstrumentation继承了AbstractMysqlInstrumentation,它使用org.apache.skywalking.apm.plugin.jdbc.mysql.StatementExecuteMethodsInterceptor增强了com.mysql.cj.jdbc.StatementImpl的execute、executeQuery、executeUpdate、executeLargeUpdate、executeBatchInternal、executeUpdateInternal、executeQuery、executeBatch方法

StatementExecuteMethodsInterceptor

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

<code>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;
  }
}/<code>
  • StatementExecuteMethodsInterceptor实现了InstanceMethodsAroundInterceptor接口,其beforeMethod方法设置DB_TYPE、DB_INSTANCE、DB_STATEMENT;其afterMethod方法在cacheObject.getConnectionInfo()不为null时执行ContextManager.stopSpan();其handleMethodException方法在cacheObject.getConnectionInfo()不为null时执行ContextManager.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

<code>public class PreparedStatementSetterInstrumentation extends PreparedStatementInstrumentation { 


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

}/<code>
  • PreparedStatementSetterInstrumentation继承了PreparedStatementInstrumentation,其getInstanceMethodsInterceptPoints方法返回的是PSSetterDefinitionOfJDBCInstrumentation(false)

PSSetterDefinitionOfJDBCInstrumentation

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

<code>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;

  }
}/<string>/<methoddescription>/<methoddescription>/<code>
  • PSSetterDefinitionOfJDBCInstrumentation实现了InstanceMethodsInterceptPoint接口,其getMethodsMatcher方法根据ignorable使用PS_IGNORABLE_SETTERS还是PS_SETTERS去构造matcher以及methodsInterceptor

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

<code>public class PreparedStatementNullSetterInstrumentation extends PreparedStatementInstrumentation {

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

}/<code>
  • PreparedStatementNullSetterInstrumentation继承了PreparedStatementInstrumentation,其getInstanceMethodsInterceptPoints方法返回的是JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint

JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint

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

<code>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;
  }
}/<methoddescription>/<code>
  • JDBCPreparedStatementNullSetterInstanceMethodsInterceptPoint实现了InstanceMethodsInterceptPoint接口,它使用org.apache.skywalking.apm.plugin.jdbc.JDBCPreparedStatementNullSetterInterceptor增强setNull方法

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

<code>public class PreparedStatementIgnoredSetterInstrumentation extends PreparedStatementInstrumentation {

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

}/<code>
  • PreparedStatementIgnoredSetterInstrumentation继承了PreparedStatementInstrumentation,其getInstanceMethodsInterceptPoints方法返回的是PSSetterDefinitionOfJDBCInstrumentation(true)

小结

skywalking的mysql-plugin提供了ConnectionImplCreateInstrumentation、ConnectionInstrumentation、CallableInstrumentation、PreparedStatementInstrumentation、StatementInstrumentation、PreparedStatementSetterInstrumentation、PreparedStatementNullSetterInstrumentation、PreparedStatementIgnoredSetterInstrumentation这几个增强

doc

  • ConnectionImplCreateInstrumentation
  • ConnectionInstrumentation
  • CallableInstrumentation
  • PreparedStatementInstrumentation
  • StatementInstrumentation
  • PreparedStatementSetterInstrumentation
  • PreparedStatementNullSetterInstrumentation
  • PreparedStatementIgnoredSetterInstrumentation


分享到:


相關文章: