转载

Java调用链跟踪关键技术(二)Javaagent

一、Javaagent

网上关于Javaagent的介绍很多,请找度娘和谷兄。唯一提的一点是字节码注入比较好用的是bytebuddy,封装度很高,使用简单。

二、代码样例

以下为关键代码样例,可以依样画瓢自行改造。

1.编写agent入口

package com.javashizhan.trace;

import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isSetter;
import static net.bytebuddy.matcher.ElementMatchers.nameContainsIgnoreCase;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWithIgnoreCase;
import static net.bytebuddy.matcher.ElementMatchers.not;

import java.lang.instrument.Instrumentation;

import com.javashizhan.trace.interceptor.AbstractJunction;
import com.javashizhan.trace.interceptor.ProtectiveShieldMatcher;
import com.javashizhan.trace.interceptor.TraceInterceptor;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;

public class TraceAgent {
    
    public static void premain(String arguments, Instrumentation instrumentation) { 
        new AgentBuilder.Default()
        .type(buildMatch())
        .transform((builder, type, classLoader, module) ->
        builder.method(ElementMatchers.any())
                        .intercept(MethodDelegation.to(TraceInterceptor.class)) // 拦截器
        ).installOn(instrumentation);
    }
    
    public static ElementMatcher<? super TypeDescription> buildMatch() {
        ElementMatcher.Junction judge = new AbstractJunction<NamedElement>() {
            @Override
            public boolean matches(NamedElement target) {
                return true;
            }
        };
        judge = judge.and(not(isInterface())).and(not(isSetter()))
                .and(nameStartsWithIgnoreCase("io.spring"))
                .and(not(nameContainsIgnoreCase("util")))
                .and(not(nameContainsIgnoreCase("interceptor")));
        judge = judge.and(not(isSetter()));
        return new ProtectiveShieldMatcher(judge);
    }

}

2.拦截器类TraceInterceptor.java

package com.javashizhan.trace.interceptor;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;

import com.javashizhan.trace.domain.CallMethod;
import com.javashizhan.trace.TraceWrapper;
import com.javashizhan.trace.collector.DBCollector;
import com.javashizhan.trace.domain.Trace;
import com.javashizhan.trace.domain.TraceRecord;

import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

public class TraceInterceptor {
    
    @RuntimeType
    public static Object intercept(@Origin Method method,
        @SuperCall Callable<?> callable) throws Exception {
        
        before(method);
        
        try {
            return callable.call();
        } finally {
            after();
        }
    }
    
    public static void after() {
        Trace trace = TraceWrapper.getTrace(); //Trace类,可自行实现,不是关键
        
        if (null != trace) {
            if (trace.callMethodSize() > 0) {
                
                CallMethod callMethod = trace.pop();
                
                if (null != callMethod && callMethod.isTraceFlag()) {
                    
                    callMethod.calculateCostTime();
                    trace.addTraceRecord(new TraceRecord(callMethod));
                    
                }
                
                if (trace.callMethodSize() == 0) {
                    List<TraceRecord> traceRecordList = trace.getAllTraceRecord();
                    if (null != traceRecordList && traceRecordList.size() > 0) {
                        DBCollector collector = new DBCollector(traceRecordList);
                        new Thread(collector).start();
                        TraceWrapper.destory();
                    }
                }
            }
        }
    }
    
    private static void before(Method method) {
        Trace trace = TraceWrapper.getTrace();
        
        CallMethod callMethod = new CallMethod(method);
        if (isInnerClass(callMethod)) {    //spring中有很多内部类,可以去掉
            callMethod.setTraceFlag(false);
        } else {
            callMethod.setTraceFlag(true);
        }
        
        //不管是否跟踪都放进去
        trace.push(callMethod);
    }
    
    private static boolean isInnerClass(CallMethod callMethod) {
         return callMethod.getClassName().indexOf('$') > -1;
    }
    
    
}

3.AbstractJunction.java

package com.javashizhan.trace.interceptor;

import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatcher.Junction;
import net.bytebuddy.matcher.ElementMatcher.Junction.Conjunction;
import net.bytebuddy.matcher.ElementMatcher.Junction.Disjunction;

public abstract class AbstractJunction<V> implements ElementMatcher.Junction<V> {
    @Override
    public <U extends V> Junction<U> and(ElementMatcher<? super U> other) {
        return new Conjunction<U>(this, other);
    }

    @Override
    public <U extends V> Junction<U> or(ElementMatcher<? super U> other) {
        return new Disjunction<U>(this, other);
    }
}

4.ProtectiveShieldMatcher.java

package com.javashizhan.trace.interceptor;

import net.bytebuddy.matcher.ElementMatcher;

public class ProtectiveShieldMatcher<T> extends ElementMatcher.Junction.AbstractBase<T> {

    private final ElementMatcher<? super T> matcher;

    public ProtectiveShieldMatcher(ElementMatcher<? super T> matcher) {
        this.matcher = matcher;
    }

    public boolean matches(T target) {
        try {
            return this.matcher.matches(target);
        } catch (Throwable t) {
            //logger.warn(t, "Byte-buddy occurs exception when match type.");
            return false;
        }
    }
}

二、pom文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>trace</groupId>
  <artifactId>chain</artifactId>
  <version>0.0.1-SNAPSHOT</version>
    
  <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <java.version>1.8</java.version>
        <!-- <spring-cloud.version>Finchley.SR1</spring-cloud.version>  -->
    </properties>

    <dependencies>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>2.7.9</version>
        </dependency>
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.16</version>
        </dependency>
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                  implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <manifestEntries>
                                        <!--  <Premain-Class>com.undergrowth.secure.SecurityAgent</Premain-Class> -->
                                       <!-- <Premain-Class>com.undergrowth.agent.AgentToString</Premain-Class>-->
                                        <Premain-Class>com.javashizhan.trace.TraceAgent</Premain-Class>
                                    </manifestEntries>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
                <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
                <plugin>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.7.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-project-info-reports-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

三、在Java应用中添加启动参数

1.先将agent工程打成jar包

2.在要使用agent的Java应用中添加如下VM启动参数

-javaagent:D:/MyApp/apache-skywalking-apm-bin/agent/chain-0.0.1-SNAPSHOT.jar

注意自行替换jar包路径。

end.

加入《Java栈实战营》知识星球,参与讨论,更多实战代码分享,不就是几斤苹果,几个荣耀道具的事吗!

Java调用链跟踪关键技术(二)Javaagent

https://t.zsxq.com/RNzfi2j
原文  https://segmentfault.com/a/1190000019965969
正文到此结束
Loading...