javaagent统计方法执行耗时

新建一个maven工程,命名为javaagent,其中pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
    <artifactId>javaagent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifestEntries>
                            <!--<main-Class>demo.Main</main-Class>-->
                            <Agent-Class>demo.Agent</Agent-Class>
                            <Premain-Class>demo.Agent</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.22.0-GA</version>
        </dependency>
    </dependencies>

</project>

在src/main/java目录下新建一个package命名为demo,在demo包下创建两个java类,分别命名为Agent.java和MyTransformer.java。

Agent.java内容如下:

package demo;

import java.lang.instrument.Instrumentation;

public class Agent {
	public static void premain(String agentArgs, Instrumentation inst) {
		inst.addTransformer(new MyTransformer(agentArgs));
	}

	public static void premain(String agentArgs) {
	}
}

MyTransformer.java内容如下:

package demo;

import javassist.*;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.*;

public class MyTransformer implements ClassFileTransformer {
	private final static Map<String, List<String>> methodMap = new HashMap<String, List<String>>();

	public MyTransformer() {
	}
	public MyTransformer(String agentArgs) {
		System.out.println("Load successfully from args!");
		add(agentArgs);
	}

	private void add(String methodString) {
		String className = methodString.substring(0, methodString.lastIndexOf("."));
		String methodName = methodString.substring(methodString.lastIndexOf(".") + 1);
		List<String> list = methodMap.get(className);
		if (list == null) {
			list = new ArrayList<String>();
			methodMap.put(className, list);
		}
		list.add(methodName);
	}

	@Override
	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
	                        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
		className = className.replace("/", ".");
		if (methodMap.containsKey(className)) {
			CtClass ctclass = null;
			try {
				ctclass = ClassPool.getDefault().get(className);
				CtField startTime = new CtField(CtClass.intType, "startTime", ctclass);
				ctclass.addField(startTime);
				CtField endTime = new CtField(CtClass.intType, "endTime", ctclass);
				ctclass.addField(endTime);

				for (String methodName : methodMap.get(className)) {
					CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);
					ctmethod.insertBefore("startTime = System.currentTimeMillis();");
					String endSrc = "endTime = System.currentTimeMillis();";
					endSrc += "System.out.println(\"The method "+ctmethod.getName()+" cost time:\"+(endTime-startTime)+\"ms\");";
					ctmethod.insertAfter(endSrc);
				}
				return ctclass.toBytecode();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}

使用mvn命令打包,执行命令:mvn assembly:assembly

打包成功后target目录下回出现javaagent-1.0-SNAPSHOT.jar和javaagent-1.0-SNAPSHOT-jar-with-dependencies.jar,javaagent-1.0-SNAPSHOT-jar-with-dependencies.jar包里包含依赖包里面的class。

这个时候javaagent的jar包已经打包好了,选择一个springboot项目,在启动时添加javaagent则可以统计方法执行耗时,启动命令:

java -javaagent:D:\javaagent\target\javaagent-1.0-SNAPSHOT-jar-with-dependencies.jar=com.example.demo.DemoController.demo -jar demo-0.0.1-SNAPSHOT.jar

其中com.example.demo.DemoController.demo是需要统计的方法,格式为类“全限定名.方法名”的形式,即这里监控的是类com.example.demo.DemoController里面的demo方法。

启动后访问demo方法,则会在控制台输出该方法的执行耗时:

Load successfully from args!

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v1.5.22.RELEASE)

2021-03-04 15:26:13.970  INFO 14772 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication v0.0.1-SNAPSHOT on SZA191211513A with PID 14772 (D:\project\demo\target\demo-0.0.1-SNAPSHOT.jar started by z30006418 in D:\project\demo\target)
2021-03-04 15:26:13.974  INFO 14772 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-03-04 15:26:14.042  INFO 14772 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@77a567e1: startup date [Thu Mar 04 15:26:14 CST 2021]; root of context hierarchy
2021-03-04 15:26:15.371  INFO 14772 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
021-03-04 15:26:24.152  INFO 14772 --- [ttp-nio--exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2021-03-04 15:26:24.153  INFO 14772 --- [ttp-nio--exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2021-03-04 15:26:24.168  INFO 14772 --- [ttp-nio--exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 15 ms
The method demo cost time:0ms

 

Guess you like

Origin blog.csdn.net/qq_36779082/article/details/114369247