├── .gitignore ├── README.md ├── pom.xml ├── sandbox-module ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── online │ └── test │ └── MySandBoxModule.java └── springboot-demo ├── pom.xml └── src └── main ├── java └── com │ └── online │ ├── MyApp.java │ └── test │ └── TestAdd.java └── resources ├── application.yml └── logback-spring.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | 11 | # Built application files 12 | *.apk 13 | *.ap_ 14 | 15 | # Files for the Dalvik VM 16 | *.dex 17 | 18 | # Java class files 19 | *.class 20 | 21 | 22 | # Generated files 23 | bin/ 24 | gen/ 25 | 26 | # Gradle files 27 | .gradle/ 28 | build/ 29 | */target 30 | /target 31 | 32 | # Local configuration file (sdk path, etc) 33 | local.properties 34 | 35 | # Proguard folder generated by Eclipse 36 | proguard/ 37 | 38 | # Log Files 39 | *.log 40 | 41 | # Others 42 | .idea/ 43 | *.iml 44 | 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arthas和jvm-sandbox对比简单使用 2 | https://www.cnblogs.com/ttzzyy/p/11414051.html 3 | # 1 阿里在线分析诊断工具Arthas(阿尔萨斯) 4 | 5 | > 参考: https://alibaba.github.io/arthas/ 6 | > 7 | > 参考: https://github.com/alibaba/arthas/blob/master/README_CN.md 8 | 9 | ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827143158532-445480955.png) 10 | 11 | # 2 阿里在线分析诊断工具Jvm-Sandbox(JVM沙盒) 12 | 13 | > 参考: https://github.com/alibaba/jvm-sandbox 14 | 15 | ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827151402817-1973943338.png) 16 | 17 | ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827143328058-1989224629.png) 18 | 19 | # 3 Arthas与Jvm-Sandbox比较 20 | 21 | > Arthas就像是一个剑冢,如果你希望得到一把宝剑就进去拿就好了! 它提供了各式各样的命令可以满足你的各项业务需求,上手成本较高。 22 | 23 | > Jvm-Sandbox就像是一个剑炉,其提供了Module的概念,每个Module都是一个AOP的实例,也就是一把剑,怎么样锻造取决于工匠,也就是你啦!它提供了模型,具体实现需要你自己编写,灵活性更高。上手成本相对较低 24 | 25 | - 下面将通过日常碰到的需要添加日志的需求,对两个开源项目的进行体验。 26 | 27 | # 4 添加日志案例 28 | 29 | > 项目地址: https://github.com/70416450/Online-debugging-demo 30 | 31 | - 针对springboot-demo项目 32 | 33 | 1. 使用 mvn clean install命令编译 34 | 2. 上传springboot-demo-1.0-SNAPSHOT.jar包到服务器上 35 | 3. 使用java -jar springboot-demo-1.0-SNAPSHOT.jar启动项目,看见如下效果 36 | 4. ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827164929719-2110338523.png) 37 | 38 | ## 4.1 Jvm-Sandbox体验 39 | 40 | ### 4.1.1 下载解压 41 | 42 | ``` 43 | # 下载最新版本的JVM-SANDBOX 44 | wget http://ompc.oss-cn-hangzhou.aliyuncs.com/jvm-sandbox/release/sandbox-stable-bin.zip 45 | 46 | # 解压 47 | unzip sandbox-stable-bin.zip 48 | ``` 49 | 50 | ### 4.1.2 挂载目标应用 51 | 52 | ``` 53 | # 进入沙箱执行脚本 54 | cd sandbox/bin 55 | 56 | # 常用命令!!! 57 | # 目标JVM进程93726(使用jps命令查看) 58 | ./sandbox.sh -p 93726 59 | #卸载沙箱 60 | ./sandbox.sh -p 93726 -S 61 | #查询沙箱 62 | ./sandbox.sh -p 93726 -l 63 | #刷新沙箱 64 | ./sandbox.sh -p 93726 -F 65 | #使用自定义module执行(my-sandbox-module:自定义的module名字,addLog自定义切入方法名字) 66 | ./sandbox.sh -p 93726 -d 'my-sandbox-module/addLog' 67 | 68 | #日志配置及查看 69 | #配置文件在 /sandbox/cfg/sandbox-logback.xml 70 | #默认日志路径 ~/logs/sandbox/sandbox.log 71 | ``` 72 | 73 | ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827165032663-117868859.png) 74 | 75 | - 针对springboot-demo项目 76 | 77 | 1. 使用 mvn clean compile assembly:single 打包,上传至sandbox/sandbox-module目录下 78 | 2. ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827160000561-1037081875.png) 79 | 3. 回到bin目录, 80 | - ./sandbox.sh -p 93726 -S 停止沙箱 81 | - ./sandbox.sh -p 93726 -F 刷新沙箱 82 | - ./sandbox.sh -p 93726 -l 查看沙箱 83 | - ./sandbox.sh -p 93726 -d 'my-sandbox-module/addLog' 使用自定义module执行(my-sandbox-module:自定义的module名字,addLog自定义切入方法名字) 84 | 4. ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827165434936-504091008.png) 85 | 5. 切换到springboot-demo项目日志查看 86 | 6. ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827165401592-260487226.png) 87 | 88 | 89 | 90 | ## 4.2 Arthas体验 91 | 92 | - 下载idea插件 Alibaba Cloud Toolkit https://plugins.jetbrains.com/plugin/11386-alibaba-cloud-toolkit/ 93 | 94 | - 添加服务器地址并打开Arthas监控 95 | 96 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827171248333-355201520.png) 97 | 98 | - 输入数字选择对应的进程 99 | 100 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827171600796-683294593.png) 101 | 102 | - 启动成功 103 | 104 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827171643318-1624308648.png) 105 | 106 | - **通过sc命令查找需要修改的class的ClassLoader** 107 | 108 | - ``` 109 | sc -d *TestAdd | grep classLoaderHash 110 | ``` 111 | 112 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827172322840-551859692.png) 113 | 114 | - 修改TestAdd 115 | 116 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827172620239-1121664723.png) 117 | 118 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827172745575-1609500150.png) 119 | 120 | - 将重新编译的class文件上传至服务器指定目录 121 | 122 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827172925575-1854637570.png) 123 | 124 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827173057958-723692068.png) 125 | 126 | - **再使用redefine命令重新加载新编译好的TestAdd.class**(注意hash码和需要替换的类路径) 127 | 128 | - ``` 129 | redefine -c 439f5b3d /usr/local/src/jvm-sandbox/test/TestAdd.class 130 | ``` 131 | 132 | - ![](https://img2018.cnblogs.com/blog/1235870/201908/1235870-20190827173328274-1772966599.png) 133 | 134 | - **注意点:** 135 | 136 | - **重启项目可以恢复** 137 | - **不允许新增加field/method** 138 | - **正在跑的函数,没有退出不能生效** 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.online 8 | demo 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | springboot-demo 13 | sandbox-module 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-parent 19 | 2.1.6.RELEASE 20 | 21 | 22 | 23 | 24 | 25 | org.projectlombok 26 | lombok 27 | true 28 | 29 | 30 | 31 | junit 32 | junit 33 | test 34 | 35 | 36 | 37 | org.slf4j 38 | slf4j-api 39 | 40 | 41 | ch.qos.logback 42 | logback-core 43 | 44 | 45 | ch.qos.logback 46 | logback-classic 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-aop 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /sandbox-module/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | demo 7 | com.online 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | sandbox-module 13 | 14 | 15 | com.alibaba.jvm.sandbox 16 | sandbox-api 17 | 1.2.0 18 | 19 | 20 | org.kohsuke.metainf-services 21 | metainf-services 22 | 1.7 23 | 24 | 25 | 26 | 27 | 28 | 29 | maven-assembly-plugin 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | jar-with-dependencies 38 | 39 | 40 | 41 | 42 | make-assembly 43 | package 44 | 45 | single 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /sandbox-module/src/main/java/com/online/test/MySandBoxModule.java: -------------------------------------------------------------------------------- 1 | package com.online.test; 2 | 3 | import com.alibaba.jvm.sandbox.api.Information; 4 | import com.alibaba.jvm.sandbox.api.Module; 5 | import com.alibaba.jvm.sandbox.api.annotation.Command; 6 | import com.alibaba.jvm.sandbox.api.listener.ext.Advice; 7 | import com.alibaba.jvm.sandbox.api.listener.ext.AdviceListener; 8 | import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder; 9 | import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.kohsuke.MetaInfServices; 12 | 13 | import javax.annotation.Resource; 14 | 15 | @MetaInfServices(Module.class) 16 | @Information(id = "my-sandbox-module")// 模块名,在指定挂载进程后通过-d指定模块,配合@Command注解来唯一确定方法 17 | @Slf4j 18 | public class MySandBoxModule implements Module { 19 | 20 | @Resource 21 | private ModuleEventWatcher moduleEventWatcher; 22 | 23 | @Command("addLog")// 模块命令名 24 | public void addLog() { 25 | new EventWatchBuilder(moduleEventWatcher) 26 | .onClass("com.online.test.TestAdd")// 想要对 TestAdd 这个类进行切面 27 | .onBehavior("test")// 想要对上面类的 test 方法进行切面 28 | .onWatch(new AdviceListener() { 29 | //对方法执行之前执行 30 | @Override 31 | protected void before(Advice advice) throws Throwable { 32 | log.info("sandbox切入成功!!!!!!"); 33 | //获取方法的所有参数 34 | Object[] parameterArray = advice.getParameterArray(); 35 | if (parameterArray != null) { 36 | for (Object po : parameterArray) { 37 | log.info("形参类型为:" + po.getClass().getName() + "!!!!!!!"); 38 | log.info("形参值为:" + po + "!!!!!!!"); 39 | } 40 | } 41 | } 42 | }); 43 | } 44 | } -------------------------------------------------------------------------------- /springboot-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | demo 7 | com.online 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | springboot-demo 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-logging 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-test 29 | test 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 39 | 1.8 40 | 1.8 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-maven-plugin 46 | 47 | 48 | 49 | repackage 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /springboot-demo/src/main/java/com/online/MyApp.java: -------------------------------------------------------------------------------- 1 | package com.online; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MyApp { 8 | public static void main(String[] args) { 9 | SpringApplication.run(MyApp.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /springboot-demo/src/main/java/com/online/test/TestAdd.java: -------------------------------------------------------------------------------- 1 | package com.online.test; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.ApplicationArguments; 5 | import org.springframework.boot.ApplicationRunner; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.concurrent.atomic.LongAdder; 9 | 10 | 11 | @Slf4j 12 | @Component 13 | public class TestAdd implements ApplicationRunner { 14 | 15 | private final LongAdder longAdder = new LongAdder(); 16 | 17 | @Override 18 | public void run(ApplicationArguments args) throws Exception { 19 | while (true) { 20 | longAdder.add(1); 21 | try { 22 | Thread.sleep(2000); 23 | } catch (InterruptedException e) { 24 | e.printStackTrace(); 25 | } 26 | test(longAdder); 27 | } 28 | } 29 | 30 | private void test(LongAdder longAdder) { 31 | log.info("Test------->" + longAdder); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /springboot-demo/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | server: 3 | port: 8080 4 | 5 | spring: 6 | profiles: 7 | active: dev 8 | -------------------------------------------------------------------------------- /springboot-demo/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ${PATTERN} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | INFO 29 | ACCEPT 30 | DENY 31 | 32 | 33 | 34 | ${TEST_FILE_PATH}/info.%d{yyyy-MM-dd}.log 35 | 36 | 100 37 | 38 | 39 | ${PATTERN} 40 | 41 | 42 | 43 | 44 | 45 | WARN 46 | ACCEPT 47 | DENY 48 | 49 | 50 | 51 | ${TEST_FILE_PATH}/warn.%d{yyyy-MM-dd}.log 52 | 53 | 100 54 | 55 | 56 | ${PATTERN} 57 | 58 | 59 | 60 | 61 | 62 | ERROR 63 | 64 | 65 | 66 | 67 | ${TEST_FILE_PATH}/error.%d{yyyy-MM-dd}.log 68 | 69 | 100 70 | 71 | 72 | ${PATTERN} 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | WARN 87 | ACCEPT 88 | DENY 89 | 90 | 91 | 92 | ${PRO_FILE_PATH}/warn.%d{yyyy-MM-dd}.log 93 | 94 | 100 95 | 96 | 97 | ${PATTERN} 98 | 99 | 100 | 101 | 102 | 103 | ERROR 104 | 105 | 106 | 107 | 108 | ${PRO_FILE_PATH}/error.%d{yyyy-MM-dd}.log 109 | 110 | 100 111 | 112 | 113 | ${PATTERN} 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | --------------------------------------------------------------------------------