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
- 编译javaagent工程生成javaagent jar包
- 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请求
拦截成功
响应头有特殊标识