SourceCode Java

Java自定义Agent实现优雅的统计接口耗时

Posted on 2021-07-29,4 min read

JavaAgent简介

  • 关于JavaAgent的简介建议参考这篇博客,本文不做过多讲解。

方法

  • 创建一个maven项目,pom文件如下(可以添加,下面是基本骨架):

    <?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>cn.gloduck</groupId>
        <artifactId>GAagent</artifactId>
        <version>1.0</version>
    
        <dependencies>
            <!--javassist依赖-->
            <dependency>
                <groupId>org.javassist</groupId>
                <artifactId>javassist</artifactId>
                <version>3.28.0-GA</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <!--打包插件,可以避免修改MANIFEST.MF-->
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>3.3.0</version>
                    <configuration>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                        <archive>
                            <manifestEntries>
                                <!--下面两个设置自定义的Agent,否则-javaagent启动时会报错-->
                                <Agent-Class>cn.gloduck.agent.CostAgent</Agent-Class>
                                <Premain-Class>cn.gloduck.agent.CostAgent</Premain-Class>
                                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                <Can-Retransform-Classes>true</Can-Retransform-Classes>
                            </manifestEntries>
                        </archive>
                    </configuration>
    
                    <executions>
                        <execution>
                            <!--设置打包时候执行插件-->
                            <id>make-assembly</id>
                            <phase>package</phase>
                            <goals>
                                <goal>single</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    
  • 创建入口Class,具体参考文章。

    package cn.gloduck.agent;
    
    import java.lang.instrument.Instrumentation;
    
    public class CostAgent {
        public static void premain(String agentArgs, Instrumentation inst) {
            System.out.println("载入耗时统计Agent成功,参数为:" + agentArgs);
            inst.addTransformer(new CostTransformer(agentArgs));
        }
    
        public static void premain(String agentArgs) {
        }
    }
    
  • 然后就是具体的一个类,使用javassist修改字节码

    package cn.gloduck.agent;
    
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    
    import java.io.ByteArrayInputStream;
    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.IllegalClassFormatException;
    import java.security.ProtectionDomain;
    
    public class CostTransformer implements ClassFileTransformer {
        private String manageClassPath;
    
        public CostTransformer(String manageClassPath) {
            this.manageClassPath = manageClassPath;
        }
    
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            if (!className.startsWith(manageClassPath)) {
                return classfileBuffer;
            }
            CtClass cl = null;
            try {
                ClassPool pool = ClassPool.getDefault();
                cl = pool.makeClass(new ByteArrayInputStream(classfileBuffer));
                for (CtMethod declaredMethod : cl.getDeclaredMethods()) {
                    // 创建一个本地变量
                    declaredMethod.addLocalVariable("start", CtClass.longType);
                    declaredMethod.insertBefore("start = System.currentTimeMillis();");
                    String methodName = declaredMethod.getLongName();
                    declaredMethod.insertAfter("System.out.println(\"" + methodName + " 耗时为: \" + (System" +
                            ".currentTimeMillis() - start));");
                }
    
                return cl.toBytecode();
            } catch (Exception e) {
    
            }
            return classfileBuffer;
        }
    }
    
  • 然后使用mvn package进行打包。

使用

  • 启动的时候在vm参数中添加:

    -javaagent:<jarpath>[=<options>]
    

注意点

如果出现Could not find goal 'assembly' in plugin org.apache.maven.plugins:maven-assembly-plugin: among available goals help, single则点击 Plugins - assembly - assembly:single


下一篇: Java实现长链接转换短链接→

loading...