1. 创建一个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>com.log.trace</groupId>
    <artifactId>agent</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>

        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8.0</version>
            <scope>system</scope>
            <systemPath>C:\\Program Files\\Java\\jdk1.8.0_181\\lib\\tools.jar</systemPath>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>9.4</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.85</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <!-- maven 打包集成插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <descriptorRefs>
                        <!-- 将依赖一起打包到 JAR -->
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <!--自动添加META-INF/MANIFEST.MF -->
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                        <manifestEntries>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <Main-Class>com.log.trace.agent.MyJavaAgent</Main-Class>
                            <Premain-Class>com.log.trace.agent.MyJavaAgent</Premain-Class>
                            <Agent-Class>com.log.trace.agent.MyJavaAgent</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 创建package

com.log.trace.agent

3. 创建MyAgent.java

package com.log.trace.agent;

import java.lang.instrument.Instrumentation;

public class MyJavaAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        MyClassFileTransformer transformer = new MyClassFileTransformer();
        inst.addTransformer(transformer, true);
    }
}

4. 创建MyInterceptor.java

自定义拦截逻辑方法beforeRequest

package com.log.trace.agent;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


public class MyInterceptor {
    public static void beforeRequest(Object request, Object response) {
        System.out.println("beforeRequest Intercepting HTTP request headers");
        System.out.println(request);
        System.out.println(response);
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        final String requestedId = httpServletRequest.getHeader("X-Requested-ID");
        // 打印客户端提交的X-Requested-ID
        // 当请求报错时,可根据此X-Requested-ID 搜索相关日志
        // 关键词前后300行  grep "X-Requested-ID" test.log -C 300
        // 关键词之后300行  grep "X-Requested-ID" test.log -A 300
        System.out.println("X-Requested-ID ====> " + requestedId);
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 增加一个特别地响应请求头
        httpServletResponse.addHeader("Interceptor", "Interceptor request");
        httpServletResponse.setContentType("text/plain;charset=UTF-8");
        final PrintWriter writer;
        try {
            writer = httpServletResponse.getWriter();
            writer.write("拦截了的返回");
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. 创建MyClassFileTransformer.java

每个http请求都会经过类

org.springframework.web.servlet.DispatcherServlet的doDispatch方法

在doDispatch方法执行之前,执行自定义的拦截逻辑方法beforeRequest

package com.log.trace.agent;

import org.objectweb.asm.*;

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

public class MyClassFileTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(
            ClassLoader loader,
            String className,
            Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain,
            byte[] classfileBuffer) {

        if (className.equals("org/springframework/web/servlet/DispatcherServlet")) {
            System.out.println("====> Transforming DispatcherServlet");

            ClassReader cr = new ClassReader(classfileBuffer);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);

            ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
                @Override
                public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                    MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);

                    // Inject interceptor logic before doDispatch method in DispatcherServlet
                    if("doDispatch".equals(name)) {
                        return new MethodVisitor(Opcodes.ASM7, mv) {
                            @Override
                            public void visitCode() {
                                try {
                                    // 加载doDispatch方法的第一个参数
                                    // 如果varIndex是0,代表org/springframework/web/servlet/DispatcherServlet那个类的this
                                    mv.visitVarInsn(Opcodes.ALOAD, 1);
                                    mv.visitVarInsn(Opcodes.ALOAD, 2);
                                    // 执行MyInterceptor.beforeRequest 方法
                                    mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(MyInterceptor.class), "beforeRequest",
                                            Type.getMethodDescriptor(MyInterceptor.class.getMethod("beforeRequest", Object.class, Object.class)), false);
                                } catch (NoSuchMethodException e) {
                                    e.printStackTrace();
                                }
                                super.visitCode();
                            }
                        };
                    }
                    return mv;
                }
            };

            cr.accept(cv, 0);
            return cw.toByteArray();
        }

        return classfileBuffer;
    }
}

6. springboot服务启动参数增加-javaagent

  1. 编译javaagent工程生成javaagent jar包
  2. springboot服务启动参数增加-javaagent参数
-javaagent:C:\Users\test\Documents\java-workspace\log-trace-mock-agent\target\agent-1.0-SNAPSHOT-jar-with-dependencies.jar

7. 向springboot服务发送http请求

拦截成功

响应头有特殊标识

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐