Talk about skywalking's AbstractPlatformTransactionManagerInstrumentation

Keywords: Programming Spring Apache Java

order

This paper mainly studies the AbstractPlatformTransactionManagerInstrumentation of skywalking

AbstractPlatformTransactionManagerInstrumentation

skywalking-6.6.0/apm-sniffer/optional-plugins/optional-spring-plugins/spring-tx-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/transaction/define/AbstractPlatformTransactionManagerInstrumentation.java

public class AbstractPlatformTransactionManagerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {

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

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
            new InstanceMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named("getTransaction");
                }

                @Override
                public String getMethodsInterceptor() {
                    return "org.apache.skywalking.apm.plugin.spring.transaction.GetTransactionMethodInterceptor";
                }

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

                @Override
                public String getMethodsInterceptor() {
                    return "org.apache.skywalking.apm.plugin.spring.transaction.EndTransactionMethodInterceptor";
                }

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

    @Override
    public ClassMatch enhanceClass() {
        return byName("org.springframework.transaction.support.AbstractPlatformTransactionManager");
    }
}
  • AbstractPlatformTransactionManagerInstrumentation inherits classinstancemethods enhanceplugindefine, and its getinstancemethods interceptpoints method creates an array of instancemethods interceptpoint. The getMethodsMatcher of one instancemethods interceptpoint is named("getTransaction"), and getmethodsinterpoint returns org.apache.skywalking.apm.plugin.spring.tra Nsaction.gettransactionmethodinterceptor; getMethodsMatcher of another instancemethodinterceptpoint is named("commit").or(named("rollback"), getMethodsInterceptor returns org.apache.skywalking.apm.plugin.spring.transaction.EndTransactionMethodInterceptor; its enhanceClass method returns byname ("org. Springframework. Transaction. Support. ABS tractPlatformTransactionManager")

ClassInstanceMethodsEnhancePluginDefine

skywalking-6.6.0/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassInstanceMethodsEnhancePluginDefine.java

public abstract class ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {

    /**
     * @return null, means enhance no static methods.
     */
    @Override
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
        return null;
    }

}
  • Classinstancemethods enhanceplugindefine inherits ClassEnhancePluginDefine, whose getstaticmethodsinterpoints method returns null

ClassEnhancePluginDefine

skywalking-6.6.0/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java

public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePluginDefine {
    private static final ILog logger = LogManager.getLogger(ClassEnhancePluginDefine.class);

    /**
     * New field name.
     */
    public static final String CONTEXT_ATTR_NAME = "_$EnhancedClassField_ws";

    /**
     * Begin to define how to enhance class. After invoke this method, only means definition is finished.
     *
     * @param typeDescription target class description
     * @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
     * @return new byte-buddy's builder for further manipulation.
     */
    @Override
    protected DynamicType.Builder<?> enhance(TypeDescription typeDescription,
        DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
        EnhanceContext context) throws PluginException {
        newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);

        newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);

        return newClassBuilder;
    }

    /**
     * Enhance a class to intercept constructors and class instance methods.
     *
     * @param typeDescription target class description
     * @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
     * @return new byte-buddy's builder for further manipulation.
     */
    private DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
        DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
        EnhanceContext context) throws PluginException {
        ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints();
        InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = getInstanceMethodsInterceptPoints();
        String enhanceOriginClassName = typeDescription.getTypeName();
        boolean existedConstructorInterceptPoint = false;
        if (constructorInterceptPoints != null && constructorInterceptPoints.length > 0) {
            existedConstructorInterceptPoint = true;
        }
        boolean existedMethodsInterceptPoints = false;
        if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) {
            existedMethodsInterceptPoints = true;
        }

        /**
         * nothing need to be enhanced in class instance, maybe need enhance static methods.
         */
        if (!existedConstructorInterceptPoint && !existedMethodsInterceptPoints) {
            return newClassBuilder;
        }

        /**
         * Manipulate class source code.<br/>
         *
         * new class need:<br/>
         * 1.Add field, name {@link #CONTEXT_ATTR_NAME}.
         * 2.Add a field accessor for this field.
         *
         * And make sure the source codes manipulation only occurs once.
         *
         */
        if (!context.isObjectExtended()) {
            newClassBuilder = newClassBuilder.defineField(CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
                .implement(EnhancedInstance.class)
                .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
            context.extendObjectCompleted();
        }

        /**
         * 2. enhance constructors
         */
        if (existedConstructorInterceptPoint) {
            for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {
                if (isBootstrapInstrumentation()) {
                    newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()).intercept(SuperMethodCall.INSTANCE
                        .andThen(MethodDelegation.withDefaultConfiguration()
                            .to(BootstrapInstrumentBoost.forInternalDelegateClass(constructorInterceptPoint.getConstructorInterceptor()))
                        )
                    );
                } else {
                    newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()).intercept(SuperMethodCall.INSTANCE
                        .andThen(MethodDelegation.withDefaultConfiguration()
                            .to(new ConstructorInter(constructorInterceptPoint.getConstructorInterceptor(), classLoader))
                        )
                    );
                }
            }
        }

        /**
         * 3. enhance instance methods
         */
        if (existedMethodsInterceptPoints) {
            for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : instanceMethodsInterceptPoints) {
                String interceptor = instanceMethodsInterceptPoint.getMethodsInterceptor();
                if (StringUtil.isEmpty(interceptor)) {
                    throw new EnhanceException("no InstanceMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
                }
                ElementMatcher.Junction<MethodDescription> junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());
                if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {
                    junction = junction.and(ElementMatchers.<MethodDescription>isDeclaredBy(typeDescription));
                }
                if (instanceMethodsInterceptPoint.isOverrideArgs()) {
                    if (isBootstrapInstrumentation()) {
                        newClassBuilder =
                            newClassBuilder.method(junction)
                                .intercept(
                                    MethodDelegation.withDefaultConfiguration()
                                        .withBinders(
                                            Morph.Binder.install(OverrideCallable.class)
                                        )
                                        .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor))
                                );
                    } else {
                        newClassBuilder =
                            newClassBuilder.method(junction)
                                .intercept(
                                    MethodDelegation.withDefaultConfiguration()
                                        .withBinders(
                                            Morph.Binder.install(OverrideCallable.class)
                                        )
                                        .to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader))
                                );
                    }
                } else {
                    if (isBootstrapInstrumentation()) {
                        newClassBuilder =
                            newClassBuilder.method(junction)
                                .intercept(
                                    MethodDelegation.withDefaultConfiguration()
                                        .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor))
                                );
                    } else {
                        newClassBuilder =
                            newClassBuilder.method(junction)
                                .intercept(
                                    MethodDelegation.withDefaultConfiguration()
                                        .to(new InstMethodsInter(interceptor, classLoader))
                                );
                    }
                }
            }
        }

        return newClassBuilder;
    }

    /**
     * Enhance a class to intercept class static methods.
     *
     * @param typeDescription target class description
     * @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
     * @return new byte-buddy's builder for further manipulation.
     */
    private DynamicType.Builder<?> enhanceClass(TypeDescription typeDescription,
        DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader) throws PluginException {
        StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = getStaticMethodsInterceptPoints();
        String enhanceOriginClassName = typeDescription.getTypeName();
        if (staticMethodsInterceptPoints == null || staticMethodsInterceptPoints.length == 0) {
            return newClassBuilder;
        }

        for (StaticMethodsInterceptPoint staticMethodsInterceptPoint : staticMethodsInterceptPoints) {
            String interceptor = staticMethodsInterceptPoint.getMethodsInterceptor();
            if (StringUtil.isEmpty(interceptor)) {
                throw new EnhanceException("no StaticMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
            }

            if (staticMethodsInterceptPoint.isOverrideArgs()) {
                if (isBootstrapInstrumentation()) {
                    newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                        .intercept(
                            MethodDelegation.withDefaultConfiguration()
                                .withBinders(
                                    Morph.Binder.install(OverrideCallable.class)
                                )
                                .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor))
                        );
                } else {
                    newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                        .intercept(
                            MethodDelegation.withDefaultConfiguration()
                                .withBinders(
                                    Morph.Binder.install(OverrideCallable.class)
                                )
                                .to(new StaticMethodsInterWithOverrideArgs(interceptor))
                        );
                }
            } else {
                if (isBootstrapInstrumentation()) {
                    newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                        .intercept(
                            MethodDelegation.withDefaultConfiguration()
                                .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor))
                        );
                } else {
                    newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                        .intercept(
                            MethodDelegation.withDefaultConfiguration()
                                .to(new StaticMethodsInter(interceptor))
                        );
                }
            }

        }

        return newClassBuilder;
    }
}
  • ClassEnhancePluginDefine inherits AbstractClassEnhancePluginDefine. Its enhancemethod first implements enhanceClass(typeDescription, newClassBuilder, classLoader), then enhanceInstance(typeDescription, newClassBuilder, classLoader, context), and finally return the newClassBuilder; enhanceClass method to traverse the staticmethodsinterpoints, and enhance the newClassBuilder through the newClassBuilder. Method (isstatic(). And (staticmethodsinterpoint. Getmethodsmatcher()). Intercept method; enhanceInstance method is mainly used to enhance the context? Attr? Name field, constructors, and instance methods

GetTransactionMethodInterceptor

skywalking-6.6.0/apm-sniffer/optional-plugins/optional-spring-plugins/spring-tx-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/transaction/GetTransactionMethodInterceptor.java

public class GetTransactionMethodInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
                             MethodInterceptResult result) throws Throwable {
        if (allArguments[0] == null) {
            AbstractSpan span = ContextManager.createLocalSpan(Constants.OPERATION_NAME_SPRING_TRANSACTION_NO_TRANSACTION_DEFINITION_GIVEN);
            span.setComponent(ComponentsDefine.SPRING_TX);
            return;
        }
        TransactionDefinition definition = (TransactionDefinition) allArguments[0];
        AbstractSpan span = ContextManager.createLocalSpan(Constants.OPERATION_NAME_SPRING_TRANSACTION_GET_TRANSACTION_METHOD + buildOperationName(definition.getName()));
        span.tag(Constants.TAG_SPRING_TRANSACTION_ISOLATION_LEVEL, String.valueOf(definition.getIsolationLevel()));
        span.tag(Constants.TAG_SPRING_TRANSACTION_PROPAGATION_BEHAVIOR, String.valueOf(definition.getPropagationBehavior()));
        span.tag(Constants.TAG_SPRING_TRANSACTION_TIMEOUT, String.valueOf(definition.getTimeout()));
        span.setComponent(ComponentsDefine.SPRING_TX);
    }

    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
                              Object ret) throws Throwable {
        ContextManager.stopSpan();
        return ret;
    }

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

    private String buildOperationName(String transactionDefinitionName) {
        if (!Config.Plugin.SpringTransaction.SIMPLIFY_TRANSACTION_DEFINITION_NAME) {
            return transactionDefinitionName;
        }
        String[] ss = transactionDefinitionName.split("\\.");

        int simplifiedLength = ss.length - 2;
        if (simplifiedLength < 0) {
            return transactionDefinitionName;
        }
        StringBuilder name = new StringBuilder();
        for (int i = 0; i < ss.length - 1; i++) {
            name.append(i < simplifiedLength ? ss[i].charAt(0) : ss[i]).append(".");
        }
        return name.append(ss[ss.length - 1]).toString();
    }
}
  • GetTransactionMethodInterceptor implements the instancemethods aroundinterceptor interface. Its beforeMethod method will get the TransactionDefinition, then create span through ContextManager.createLocalSpan, and then set constants.tag'spring'transaction'isolation'level, constants.tag'spring'transaction'promotion'behavior, constants.tag'spring'transaction'timeout These tags; afterMethod methods execute ContextManager.stopSpan(); handleMethodException methods execute contextmanager. Activespan(). Erroreoccurred(). Log (T)

EndTransactionMethodInterceptor

skywalking-6.6.0/apm-sniffer/optional-plugins/optional-spring-plugins/spring-tx-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/transaction/EndTransactionMethodInterceptor.java

public class EndTransactionMethodInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
                             MethodInterceptResult result) throws Throwable {
        AbstractSpan span = ContextManager.createLocalSpan(Constants.OPERATION_NAME_SPRING_TRANSACTION_PREFIX + method.getName());
        TransactionStatus status = (TransactionStatus) allArguments[0];
        span.tag(Constants.TAG_SPRING_TRANSACTION_IS_NEW_TRANSACTION, String.valueOf(status.isNewTransaction()));
        span.tag(Constants.TAG_SPRING_TRANSACTION_HAS_SAVEPOINT, String.valueOf(status.hasSavepoint()));
        span.tag(Constants.TAG_SPRING_TRANSACTION_ROLLBACK_ONLY, String.valueOf(status.isRollbackOnly()));
        span.tag(Constants.TAG_SPRING_TRANSACTION_IS_COMPLETED, String.valueOf(status.isCompleted()));
        span.setComponent(ComponentsDefine.SPRING_TX);
    }

    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
                              Object ret) throws Throwable {
        ContextManager.stopSpan();
        return ret;
    }

    @Override
    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
                                      Class<?>[] argumentsTypes, Throwable t) {
        ContextManager.activeSpan().errorOccurred().log(t);
    }
}
  • The EndTransactionMethodInterceptor implements the instancemethods aroundinterceptor interface. Its beforeMethod method creates a span through ContextManager.createLocalSpan, obtains the TransactionStatus, and then sets constants.tag'spring'transaction'is'new'transaction, constants.tag'spring'transaction'has'savepoint, constants.tag'spring'transaction'rollback'only, Co Nstants. Tag? Spring? Transaction? Is? Completed; its afterMethod method executes ContextManager.stopSpan(); its handleMethodException method executes contextmanager. Activespan(). Erroreoccurred(). Log (T)

Summary

AbstractPlatformTransactionManagerInstrumentation inherits classinstancemethods enhanceplugindefine, and its getinstancemethods interceptpoints method creates an array of instancemethods interceptpoint. The getMethodsMatcher of one instancemethods interceptpoint is named("getTransaction"), and getmethodsinterpoint returns org.apache.skywalking.apm.plugin.spring.tra Nsaction.gettransactionmethodinterceptor; getMethodsMatcher of another instancemethodinterceptpoint is named("commit").or(named("rollback"), getMethodsInterceptor returns org.apache.skywalking.apm.plugin.spring.transaction.EndTransactionMethodInterceptor; its enhanceClass method returns byname ("org. Springframework. Transaction. Support. ABS tractPlatformTransactionManager")

doc

Posted by YodaOfCamarilla on Wed, 04 Mar 2020 06:42:11 -0800