天天看點

基于JavaAgent的全鍊路監控三《ByteBuddy操作監控方法位元組碼》

  • 基于JavaAgent的全鍊路監控一《嗨!JavaAgent》
  • 基于JavaAgent的全鍊路監控二《通過位元組碼增加監控執行耗時》
  • 基于JavaAgent的全鍊路監控三《ByteBuddy操作監控方法位元組碼》
  • 基于JavaAgent的全鍊路監控四《JVM記憶體與GC資訊》
  • 基于JavaAgent的全鍊路監控五《ThreadLocal鍊路追蹤》
  • 基于JavaAgent的全鍊路監控六《開發應用級監控》

案例簡述

在第二章中我們已經可以監控方法執行耗時,雖然它能完成我們一些基本需要,但是為了增強代碼的擴充性,我們需要使用位元組碼操作工具ByteBuddy來幫助我們實作更完善的監控程式。

Byte Buddy is a code generation and manipulation library for creating and modifying Java classes during the runtime of a Java application and without the help of a compiler. Other than the code generation utilities that ship with the Java Class Library, Byte Buddy allows the creation of arbitrary classes and is not limited to implementing interfaces for the creation of runtime proxies. Furthermore, Byte Buddy offers a convenient API for changing classes either manually, using a Java agent or during a build.

環境準備

1、IntelliJ IDEA Community Edition

2、jdk1.8.0_45 64位

配置資訊(路徑相關修改為自己的)

1、配置位置:Run/Debug Configurations -> VM options

2、配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-03\target\itstack-demo-agent-03-1.0.0-SNAPSHOT.jar=testargs

代碼示例

itstack-demo-agent-03
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo.agent
    │   │       ├── MethodCostTime.java
    │   │	    └── MyAgent.java
    │	└── resources
    │       └── META-INF
    │           └── MANIFEST.MF 	
    └── test
         └── java
             └── org.itstack.demo.test
                 └── ApiTest.java
           
pom.xml (引入ByteBuddy并打入到Agent包中)
<properties>
    <!-- Build args -->
	<argline>-Xms512m -Xmx512m</argline>
	<skip_maven_deploy>false</skip_maven_deploy>
	<updateReleaseInfo>true</updateReleaseInfo>
	<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
	<maven.test.skip>true</maven.test.skip>
	<!-- 自定義MANIFEST.MF -->
	<maven.configuration.manifestFile>src/main/resources/META-INF/MANIFEST.MF</maven.configuration.manifestFile>
</properties>

<dependencies>
	<dependency>
		<groupId>javassist</groupId>
		<artifactId>javassist</artifactId>
		<version>3.12.1.GA</version>
		<type>jar</type>
	</dependency>
	<dependency>
		<groupId>net.bytebuddy</groupId>
		<artifactId>byte-buddy</artifactId>
		<version>1.8.20</version>
	</dependency>
	<dependency>
		<groupId>net.bytebuddy</groupId>
		<artifactId>byte-buddy-agent</artifactId>
		<version>1.8.20</version>
	</dependency>
</dependencies>

<!-- 将javassist包打包到Agent中 -->
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<artifactSet>
			<includes>
				<include>javassist:javassist:jar:</include>
				<include>net.bytebuddy:byte-buddy:jar:</include>
                <include>net.bytebuddy:byte-buddy-agent:jar:</include>
			</includes>
		</artifactSet>
	</configuration>
</plugin>      
           
MethodCostTime.java
/**
 * 部落格:http://itstack.org
 * 論壇:http://bugstack.cn
 * 公衆号:bugstack蟲洞棧  {擷取學習源碼}
 * create by fuzhengwei on 2019
 */
public class MethodCostTime {

    @RuntimeType
    public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable) throws Exception {
        long start = System.currentTimeMillis();
        try {
            // 原有函數執行
            return callable.call();
        } finally {
            System.out.println(method + " 方法耗時: " + (System.currentTimeMillis() - start) + "ms");
        }
    }

}
           
MyAgent.java
/**
 * javaagent
 * 部落格:http://itstack.org
 * 論壇:http://bugstack.cn
 * 公衆号:bugstack蟲洞棧  {擷取學習源碼}
 * create by fuzhengwei on 2019
 */
public class MyAgent {

    //JVM 首先嘗試在代理類上調用以下方法
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("this is my agent:" + agentArgs);
        
        AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
            return builder
                    .method(ElementMatchers.any()) // 攔截任意方法
                    .intercept(MethodDelegation.to(MethodCostTime.class)); // 委托
        };

        AgentBuilder.Listener listener = new AgentBuilder.Listener() {
            @Override
            public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

            @Override
            public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {

            }

            @Override
            public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

            @Override
            public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {

            }

            @Override
            public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

        };

        new AgentBuilder
                .Default()
                .type(ElementMatchers.nameStartsWith("org.itstack.demo.test")) // 指定需要攔截的類
                .transform(transformer)
                .with(listener)
                .installOn(inst);
    }


    //如果代理類沒有實作上面的方法,那麼 JVM 将嘗試調用該方法
    public static void premain(String agentArgs) {
    }

}
           
MANIFEST.MF
Manifest-Version: 1.0
Premain-Class: org.itstack.demo.agent.MyAgent
Can-Redefine-Classes: true
           
ApiTest.java
/**
 * 部落格:http://itstack.org
 * 論壇:http://bugstack.cn
 * 公衆号:bugstack蟲洞棧  {擷取學習源碼}
 * create by fuzhengwei on 2019
 *
 * VM options:
 * -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-03\target\itstack-demo-agent-03-1.0.0-SNAPSHOT.jar=testargs
 */
public class ApiTest {

    public static void main(String[] args) throws InterruptedException {
        ApiTest apiTest = new ApiTest();
        apiTest.echoHi();
    }

    private void echoHi() throws InterruptedException {
        System.out.println("hi agent");
        Thread.sleep((long) (Math.random() * 500));
    }

}
           
this is my agent:testargs
hi agent
private void org.itstack.demo.test.ApiTest.echoHi() throws java.lang.InterruptedException 方法耗時: 329ms
public static void org.itstack.demo.test.ApiTest.main(java.lang.String[]) throws java.lang.InterruptedException 方法耗時: 329ms

Process finished with exit code 0