利用Java Agent 做Spring MVC Controller 层的出参入参打印日志

news/2024/11/12 14:04:28/

许多开发使用的spring aop来做切面 今天尝试一下使用JVM层面的切面

1、建立 agent jar工程 

创建工程 logging-agent 使用POM为 javassist 日志

如下:使用了字节码 javassist

如果想处理springaop 代理的请增加依赖否则就不需要

   <dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.21</version> <!-- 确保使用最新版本 --></dependency><!-- AspectJ Weaver --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version> <!-- 确保使用最新版本 --></dependency>

为了方便使用fastJSON 做序列化

完整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>org.example</groupId><artifactId>logging-agent</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.28.0-GA</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.21</version> <!-- 确保使用最新版本 --></dependency><!-- AspectJ Weaver --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version> <!-- 确保使用最新版本 --></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version> <!-- 确保使用最新版本 --></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><executions><execution><id>attach-sources</id><goals><goal>jar</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>org.logging.LoggingAgent</mainClass></transformer></transformers></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.1.0</version><configuration><archive><!--自动添加META-INF/MANIFEST.MF --><manifest><addClasspath>true</addClasspath></manifest><manifestEntries><Premain-Class>org.logging.LoggingAgent</Premain-Class><Agent-Class>org.logging.LoggingAgent</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></archive></configuration></plugin></plugins></build>
</project>

2、建立一个Agent类

java">package org.logging;import com.alibaba.fastjson.JSON;
import javassist.*;
import org.springframework.aop.framework.AopProxyUtils;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.*;public class LoggingAgent {public static void premain(String agentArgs, Instrumentation inst) {inst.addTransformer(new LoggingTransformer());}static Set<Class<?>> skipLogClassSet = new HashSet<>();static Map<Class<?>, Boolean> decisionSkipClassMap = new HashMap<>();static {skipLogClassSet.add(forName("org.apache.catalina.connector.RequestFacade"));skipLogClassSet.add(forName("javax.servlet.ServletRequest"));skipLogClassSet.add(forName("javax.servlet.ServletResponse"));}public static Class<?> forName(String clazz) {try {return Class.forName(clazz);} catch (ClassNotFoundException e) {e.printStackTrace();}return Void.class;}public static String toJSONString(Object[] a) {if (a == null)return "null";int iMax = a.length - 1;if (iMax == -1)return "[]";StringBuilder b = new StringBuilder();b.append('[');for (int i = 0; ; i++) {
//            b.append(String.valueOf(a[i]));Class<?> clazz = a[i].getClass();System.out.println(clazz);if (!decisionSkipClassMap.containsKey(clazz)) {// 检查类是否实现了每个接口for (Class<?> interfaceClass : skipLogClassSet) {if (interfaceClass.isAssignableFrom(clazz)) {System.out.println("myObject 的类实现了 " + interfaceClass.getName() + " 接口");decisionSkipClassMap.put(clazz, true);} else {System.out.println("myObject 的类没有实现 " + interfaceClass.getName() + " 接口");if (decisionSkipClassMap.containsKey(clazz) && decisionSkipClassMap.get(clazz)) {//nothingSystem.out.println("myObject 的类没有实现 " + interfaceClass.getName() + " 接口 但是实现了其他忽略接口");} else {decisionSkipClassMap.put(clazz, interfaceClass.isAssignableFrom(clazz));}}}}if (!decisionSkipClassMap.get(clazz)) {b.append(JSON.toJSONString(a[i]));}if (i == iMax)return b.append(']').toString();b.append(", ");}}public static class LoggingTransformer implements ClassFileTransformer {@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {// 忽略不需要处理的类if (!className.startsWith("com/xdx/interfaces/facade")) {return null;}// Skip Spring CGLIB proxy classesif (className.contains("$$EnhancerBySpringCGLIB$$")) {return null;}// Skip Spring CGLIB FastClass proxy classesif (className.contains("$$FastClassBySpringCGLIB$$")) {return null;}try {String finalClassName = getFinalClassName(className.replace("/", "."));ClassPool pool = ClassPool.getDefault();pool.insertClassPath(new ClassClassPath(this.getClass()));CtClass ctClass = pool.get(finalClassName);// 获取类的所有声明字段CtField[] fields = ctClass.getDeclaredFields();String logger = null;// 打印字段的名称和类型for (CtField field : fields) {System.out.println("Field Name: " + field.getName() + ", Field Type: " + field.getType().getName());if ("org.slf4j.Logger".equals(field.getType().getName())) {logger = field.getName();}}for (CtMethod method : ctClass.getDeclaredMethods()) {try {addLogging(method, logger);} catch (Exception e) {System.err.println("Failed to instrument method: " + method.getName());e.printStackTrace();}}return ctClass.toBytecode();} catch (NotFoundException e) {// Log exception and continue without instrumentationSystem.err.println("Class not found: " + className + " - " + e.getMessage());} catch (Exception e) {e.printStackTrace();}return classfileBuffer;}private String getFinalClassName(String className) {if (className.contains("$$EnhancerBySpringCGLIB$$") || className.contains("$$FastClassBySpringCGLIB$$")) {try {Class<?> proxyClass = Class.forName(className);Class<?> targetClass = AopProxyUtils.ultimateTargetClass(proxyClass);return targetClass.getName();} catch (ClassNotFoundException e) {// Handle exception and return the original classNamee.printStackTrace();}}return className;}private void addLogging(CtMethod method, String logger) throws CannotCompileException {if (Objects.isNull(logger)) {addLogging2(method);} else {addLogging1(method, logger);}}private void addLogging1(CtMethod method, String logger) throws CannotCompileException {
//            method.insertBefore("System.out.println(\"[ENTER] Method: " + method.getName() + " Arguments: \" + java.util.Arrays.toString($args));");method.insertBefore(logger + ".info(\"[ENTER1] Method: " + method.getName() + " Arguments: \" +org.logging.LoggingAgent.toJSONString($args));");
//            method.insertBefore("log.info(org.logging.LoggingAgent.toJSONString($args));");method.insertAfter(logger + ".info(\"[EXIT1] Method: " + method.getName() + " Return: \" + $_);");
//            method.insertBefore(logger + ".info(\"[EXIT] Method: " + method.getName() + " Return: \" + org.logging.LoggingAgent.toJSONString($_));");}private void addLogging2(CtMethod method) throws CannotCompileException {method.insertBefore("org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());"+ "logger.info(\"[ENTER2] Method: " + method.getName() + " Arguments: \" +  org.logging.LoggingAgent.toJSONString($args));");
//            method.insertBefore("org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());" +
//                    "logger.info(\"[ENTER] Method: " + method.getName() + " Arguments: \" + java.util.Arrays.toString($args));");method.insertAfter("logger.info(\"[EXIT2] Method: " + method.getName() + " Return: \" + $_);", true);//src:表示插入的代码,这是一段 Java 源代码的 String。
//            asFinally:这是一个 boolean 值,决定了插入的代码块是在正常返回后执行,还是在方法的正常返回和异常返回后都执行。
//            method.insertAfter("org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());" +
//                    "logger.info(\"[EXIT] Method: " + method.getName() + " Return: \" + logger.info(org.logging.LoggingAgent.toJSONString($_)));", true);}private void addLogging3(CtMethod method) throws CannotCompileException {method.insertBefore("System.out.println(\"[ENTER] Method: " + method.getName() + " Arguments: \" + java.util.Arrays.toString($args));");method.insertAfter("System.out.println(\"[EXIT] Method: " + method.getName() + " Return: \" + $_);");}}
}

3、打agent jar

4、创建一个demoWeb工程

-javaagent:D:\code\logging-agent\target\logging-agent-1.0-SNAPSHOT.jar

为了能调试需要增加jar 


http://www.ppmy.cn/news/1545847.html

相关文章

汽车共享服务管理:SpringBoot专业解决方案

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

WordPress 2024主题实例镜像

目录 隐藏 1 WordPress 2024主题实例镜像启用的插件 2 WordPress 2024主题实例镜像截图 WordPress 2024主题实例镜像启用的插件 WordPress 2024主题实例镜像启用了2024主题&#xff0c;配置了&#xff1a; Akismet 反垃圾评论插件 Admin Notices Manager仪表盘通知隐藏…

外包干了5年,技术退步太明显了。。。。。

先说一下自己的情况&#xff0c;本科生生&#xff0c;19年通过校招进入杭州某软件公司&#xff0c;干了差不多五年的功能测试&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了五年的功能测试&#xff0c;已经…

Linux可视化工具cockpit

引言 Cockpit 是一个开源项目&#xff0c;它提供了一个基于 Web 的界面&#xff0c;用于管理和监控 Linux 服务器。这个项目最初由 Red Hat 发起&#xff0c;但现在已经得到了更广泛的社区支持。Cockpit 的设计目标是让系统管理员能够轻松地进行各种任务&#xff0c;包括查看系…

深度学习注意力机制类型总结pytorch实现代码

一、注意力机制的基本原理 在深度学习中&#xff0c;注意力机制&#xff08;Attention Mechanism&#xff09;已经成为一种重要的技术。意力机制通过动态调整模型的注意力权重&#xff0c;来突出重要信息&#xff0c;忽略不重要的信息&#xff0c;大大提高了模型的效果 注意力…

linux服务器通过手机USB共享网络

一、背景&#xff1a; 现网交付时&#xff0c;客户机房设备未接入互联网&#xff0c;需要联网拉去软件包时&#xff0c;通过手机USB共享网络给服务器。 二、测试环境&#xff1a; ubuntu、centos 三、操作步骤&#xff1a; 1、 确认networkmanager服务开启&#xff08;cento…

命令行工具PowerShell使用体验

命令行工具PowerShell使用 PowerShell是微软开发的一种面向对象的命令行Shell和脚本语言环境&#xff0c;它允许用户通过命令行的方式管理操作系统。相较于传统CMD&#xff0c;PowerShell增加了面向对象的程序设计框架&#xff0c;拥有更强大的功能和扩展性。使用PowerShell可…

若Git子模块的远端地址发生了变化本地应该怎么调整

文章目录 前言git submodule 相关命令解决方案怎么保存子模块的版本呢总结 前言 这个问题复杂在既有Git又有子模块&#xff0c;本身Git的门槛就稍微高一点&#xff0c;再加上子模块的运用&#xff0c;一旦出现这种远端地址发生修改的情况会让人有些懵&#xff0c;不知道怎么处…