JAVA-Agent探針列印方法執行的返回值

yvioo發表於2024-05-02

1、建立一個maven專案,不要用springboot的

引入依賴

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.29.0-GA</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.24</version>
        </dependency>

其中 javassist這個依賴必須的 其他是程式碼邏輯用到的 根據自己需要來

設定打包外掛

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <!--將所有依賴都打入同一個jar包中-->
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <!--指定java agent相關配置檔案-->
                    <archive>
                        <manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
                    </archive>
                </configuration>
            </plugin>

編寫主類程式碼

JavaAgent.java

import java.lang.instrument.Instrumentation;

/**
 * 攔截列印方法的返回值
 */
public class JavaAgent {

    public static void premain(String agentArgs, Instrumentation inst) {
        inst.addTransformer(new ClassPreProcessorAgentAdapter(), true);
    }

}

agentArgs:可以透過這個把一些引數傳進來

ClassPreProcessorAgentAdapter.java

import javassist.*;
import org.apache.commons.lang3.StringUtils;

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

/**
 * 列印方法返回值 具體邏輯
 */
public class ClassPreProcessorAgentAdapter implements ClassFileTransformer {


    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {


        //是我們自己寫的類才列印
        if (StringUtils.isBlank(className)) {
            return classfileBuffer;
        }

        //將類名路徑 / 換成 .
        String classPkgName = className.replace('/', '.');

        //如果不是我們寫的類 直接返回 這裡自定義邏輯
        if (!classPkgName.contains("IndexController")) {
            return classfileBuffer;
        }
        final ClassPool pool = ClassPool.getDefault();

        try {
            CtClass ctClass = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));
            //匹配類的路徑是我們要的才列印
            if (ctClass.getName().replace('/', '.').startsWith("com.example.demotest")) {
                for (CtMethod method : ctClass.getDeclaredMethods()) {
                    if (method.hasAnnotation(org.springframework.web.bind.annotation.GetMapping.class)) {
                        // 這句話最重要 列印返回值的內容
                        method.insertAfter("System.out.println(\"Controller Method Returned: \" + $_);");
                    }
                }
                return ctClass.toBytecode();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("premain load Class:" + className);
        return classfileBuffer;
    }


}

以上只是虛擬碼 而且只有請求上有@GetMappting的註解才會列印

然後resource下放檔案

MANIFEST.MF

Manifest-Version: 1.0
Specification-Title: Log Agent
Specification-Version: 0.0.1
Specification-Vendor: LogAgent
Implementation-Title: log.agent
Implementation-Version: 0.0.1
Implementation-Vendor: LogAgent
Premain-Class: com.demo.agent.JavaAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

Premain-Class:這個就是我們寫的premain的類的全路徑 我這裡是JavaAgent的類路徑 根據自己的來

最後一行要空一行 ,這個不能少

最後打包,會生成一個 jar-with-dependencies.jar結尾的jar包,然後我們透過啟動的時候命令引用進去即可

-javaagent:D:\jar包路徑\agent-jar-with-dependencies.jar

IDEA的話是在那個啟動類的 VM options裡面設定

如果要除錯程式碼的話 要把啟動的專案和我們agent的專案放在一個工程底下,這樣idea就能自動斷點到

相關文章