
负责7K+应用,100K+机器的Spring Boot微服务技术落地,关注开发体验,微服务,APM,应用诊断技术。Dubbo/Arthas开源。 github: https://github.com/hengyunabc
缘起 最近看到一个很流行的标题,《开源XX年,star XXX,我是如何坚持的》。 看到这样的标题,忽然发觉Arthas从2018年9月开源以来,刚好一年了,正好在这个秋高气爽的时节做下总结和回顾。 Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。 Github: https://github.com/alibaba/arthas 文档:https://alibaba.github.io/arthas 回顾Arthas Star数的历史,一直保持快速增长,目前已经突破160K。 感谢用户的支持,既是压力也是动力。在过去开源的一年里,Arthas发布了7个Release版本,我们一直坚持三点: 持续改进易用性 持续增加好用的命令 从开源社区中获取力量,回报社区 持续改进易用性 Arthas一直把易用性放在第一位,在开源之后,我们做了下面的改进: 开发arthas boot,支持Windows/Linux/Mac统一体验 丝滑的自动补全,参考了jshell的体验 高效的历史命令匹配,Up/Down直达 改进类搜索匹配功能,更好支持lambda和内部类 完善重定向机制 支持JDK 9/10/11 支持Docker 支持rpm/deb包安装 尽管我们在易用性下了很大的功夫,但是发现很多时候用户比较难入门,因此,我们参考了k8s的 Interactive Tutorial,推出了Arthas的在线教程: Arthas基础教程 Arthas进阶教程 通过基础教程,可以在交互终端里一步步入门,通过进阶教程可以深入理解Arthas排查问题的案例。 另外,为了方便用户大规模部署,我们实现了tunnel server和用户数据回报功能: 增加tunnel server,统一管理Agent连接 增加用户数据回报功能,方便做安全管控 持续增加好用的命令 Arthas号称是Java应用诊断利器,那么我们自己要对得起这个口号。在开源之后,Arthas持续增加了10多个命令。 ognl 命令任意代码执行 mc 线上内存编译器 redefine 命令线上热更新代码 logger 命令一键查看应用里的所有logger配置 sysprop 查看更新System Properties sysenv 查看环境变量 vmoption 查看更新VM option logger 查看logger配置,更新level mbean 查看JMX信息 heapdump 堆内存快照 下面重点介绍两个功能。 jad/mc/redefine 一条龙热更新线上代码 以 Arthas在线教程 里的UserController为例: 使用jad反编译代码 jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java 使用vim编译代码 当 user id 小于1时,也正常返回,不抛出异常: @GetMapping("/user/{id}") public User findUserById(@PathVariable Integer id) { logger.info("id: {}" , id); if (id != null && id < 1) { return new User(id, "name" + id); // throw new IllegalArgumentException("id < 1"); } else { return new User(id, "name" + id); } } 使用mc命令编译修改后的UserController.java $ mc /tmp/UserController.java -d /tmp Memory compiler output: /tmp/com/example/demo/arthas/user/UserController.class Affect(row-cnt:1) cost in 346 ms 使用redefine命令,因为可以热更新代码 $ redefine /tmp/com/example/demo/arthas/user/UserController.class redefine success, size: 1 通过logger命令查看配置,修改level 在网站压力大的时候(比如双11),有个缓解措施就是把应用的日志level修改为ERROR。 那么有两个问题: 复杂应用的日志系统可能会有多个,那么哪个日志系统配置真正生效了? 怎样在线上动态修改logger的level? 通过logger命令,可以查看应用里logger的详细配置信息,比如FileAppender输出的文件,AsyncAppender是否blocking。 [arthas@2062]$ logger name ROOT class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level INFO effectiveLevel INFO additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar appenders name CONSOLE class ch.qos.logback.core.ConsoleAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 target System.out name APPLICATION class ch.qos.logback.core.rolling.RollingFileAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 file app.log name ASYNC class ch.qos.logback.classic.AsyncAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 blocking false appenderRef [APPLICATION] 也可以在线修改logger的level: [arthas@2062]$ logger --name ROOT --level debug update logger level success. 从开源社区中获取力量,回报社区 感谢67位Contributors Arthas开源以来,一共有67位 Contributors,感谢他们贡献的改进: 社区提交了一系列的改进,下面列出一些点(不完整): 翻译了大部分英文文档的 trace命令支持行号 打包格式支持rpm/deb 改进命令行提示符为 arthas@pid 改进对windows的支持 增加mbean命令 改进webconsole的体验 另外,有83个公司/组织登记了他们的使用信息,欢迎更多的用户来登记: 洐生项目 基于Arthas,还产生了一些洐生项目,下面是其中两个: Bistoury: 去哪儿网开源的集成了Arthas的项目 arthas-mvel: 一个使用MVEL脚本的fork 用户案例分享 广大用户在使用Arthas排查问题过程中,分享了很多排查过程和心得,欢迎大家来分享。 回馈开源 Arthas本身使用了很多开源项目的代码,在开源过程中,我们给netty, ognl, cfr等都贡献了改进代码,回馈上游。 后记 在做Arthas宣传小册子时,Arthas的宣传语是: “赠人玫瑰之手,经久犹有余香” 希望Arthas未来能帮助到更多的用户解决问题,也希望广大的开发者对Arthas提出更多的改进和建议。
Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。 Github: https://github.com/alibaba/arthas 文档:https://alibaba.github.io/arthas Arthas 3.1.2版本持续增加新特性,下面重点介绍: logger/heapdump/vmoption/stop命令 通过tunnel server连接不同网络的arthas,方便统一管控 易用性持续提升:提示符修改为arthas@pid形式,支持ctrl + k清屏快捷键 logger/heapdump/vmoption/stop命令 logger命令 查看logger信息,更新logger level https://alibaba.github.io/arthas/logger.html 查看所有logger信息 以下面的logback.xml为例: <?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern> <maxFileSize>100MB</maxFileSize> <maxHistory>60</maxHistory> <totalSizeCap>2GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%logger{35} - %msg%n</pattern> </encoder> </appender> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="APPLICATION" /> </appender> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n </pattern> <charset>utf8</charset> </encoder> </appender> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="ASYNC" /> </root> </configuration> 使用logger命令打印的结果是: [arthas@2062]$ logger name ROOT class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level INFO effectiveLevel INFO additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar appenders name CONSOLE class ch.qos.logback.core.ConsoleAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 target System.out name APPLICATION class ch.qos.logback.core.rolling.RollingFileAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 file app.log name ASYNC class ch.qos.logback.classic.AsyncAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 appenderRef [APPLICATION] 从appenders的信息里,可以看到 CONSOLE logger的target是System.out APPLICATION logger是RollingFileAppender,它的file是app.log ASYNC它的appenderRef是APPLICATION,即异步输出到文件里 查看指定名字的logger信息 [arthas@2062]$ logger -n org.springframework.web name org.springframework.web class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level null effectiveLevel INFO additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar 更新logger level [arthas@2062]$ logger --name ROOT --level debug update logger level success. heapdump命令 dump java heap, 类似jmap命令的heap dump功能。 https://alibaba.github.io/arthas/heapdump.html dump到指定文件 [arthas@58205]$ heapdump /tmp/dump.hprof Dumping heap to /tmp/dump.hprof... Heap dump file created 只dump live对象 [arthas@58205]$ heapdump --live /tmp/dump.hprof Dumping heap to /tmp/dump.hprof... Heap dump file created vmoption命令 查看,更新VM诊断相关的参数 https://alibaba.github.io/arthas/vmoption.html 查看所有的option [arthas@56963]$ vmoption KEY VALUE ORIGIN WRITEABLE --------------------------------------------------------------------------------------------- HeapDumpBeforeFullGC false DEFAULT true HeapDumpAfterFullGC false DEFAULT true HeapDumpOnOutOfMemory false DEFAULT true Error HeapDumpPath DEFAULT true CMSAbortablePrecleanW 100 DEFAULT true aitMillis CMSWaitDuration 2000 DEFAULT true CMSTriggerInterval -1 DEFAULT true PrintGC false DEFAULT true PrintGCDetails true MANAGEMENT true PrintGCDateStamps false DEFAULT true PrintGCTimeStamps false DEFAULT true PrintGCID false DEFAULT true PrintClassHistogramBe false DEFAULT true foreFullGC PrintClassHistogramAf false DEFAULT true terFullGC PrintClassHistogram false DEFAULT true MinHeapFreeRatio 0 DEFAULT true MaxHeapFreeRatio 100 DEFAULT true PrintConcurrentLocks false DEFAULT true 查看指定的option [arthas@56963]$ vmoption PrintGCDetails KEY VALUE ORIGIN WRITEABLE --------------------------------------------------------------------------------------------- PrintGCDetails false MANAGEMENT true 更新指定的option [arthas@56963]$ vmoption PrintGCDetails true Successfully updated the vm option. PrintGCDetails=true stop命令 之前有用户吐槽,不小心退出Arthas console之后,shutdown会关闭系统,因此增加了stop命令来退出arthas,功能和shutdown命令一致。 通过tunnel server连接不同网络的arthas https://alibaba.github.io/arthas/web-console.html 在新版本里,增加了arthas tunnel server的功能,用户可以通过tunnel server很方便连接不同网络里的arthas agent,适合做统一管控。 启动arthas时连接到tunnel server 在启动arthas,可以传递--tunnel-server参数,比如: as.sh --tunnel-server 'ws://47.75.156.201:7777/ws' 目前47.75.156.201是一个测试服务器,用户可以自己搭建arthas tunnel server 如果有特殊需求,可以通过--agent-id参数里指定agentId。默认情况下,会生成随机ID。 attach成功之后,会打印出agentId,比如: ,---. ,------. ,--------.,--. ,--. ,---. ,---. / O \ | .--. ''--. .--'| '--' | / O \ ' .-' | .-. || '--'.' | | | .--. || .-. |`. `-. | | | || |\ \ | | | | | || | | |.-' | `--' `--'`--' '--' `--' `--' `--'`--' `--'`-----' wiki https://alibaba.github.io/arthas tutorials https://alibaba.github.io/arthas/arthas-tutorials version 3.1.2 pid 86183 time 2019-08-30 15:40:53 id URJZ5L48RPBR2ALI5K4V 如果是启动时没有连接到 tunnel server,也可以在后续自动重连成功之后,通过 session命令来获取 agentId: [arthas@86183]$ session Name Value ----------------------------------------------------- JAVA_PID 86183 SESSION_ID f7273eb5-e7b0-4a00-bc5b-3fe55d741882 AGENT_ID URJZ5L48RPBR2ALI5K4V TUNNEL_SERVER ws://47.75.156.201:7777/ws 以上面的为例,在浏览器里访问 http://47.75.156.201:8080/ ,输入 agentId,就可以连接到本机上的arthas了。 Arthas tunnel server的工作原理 browser <-> arthas tunnel server <-> arthas tunnel client <-> arthas agent https://github.com/alibaba/arthas/blob/master/tunnel-server/README.md 易用性持续提升 提示符修改为arthas@pid形式,用户可以确定当前进程ID,避免多个进程时误操作 [arthas@86183]$ help 增加ctrl + k清屏快捷键 总结 总之,3.1.2版本的Arthas新增加了logger/heapdump/vmoption/stop命令,增加了tunnel server,方便统一管控。另外还有一些bug修复等,可以参考 Release Note: https://github.com/alibaba/arthas/releases/tag/3.1.2 最后,Arthas的在线教程考虑重新组织,欢迎大家参与,提出建议: https://github.com/alibaba/arthas/issues/847
Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。 从Arthas上个版本发布,已经过去两个多月了,Arthas 3.1.0版本不仅带来大家投票出来的新LOGO,还带来强大的新功能和更好的易用性,下面一一介绍。 Github: https://github.com/alibaba/arthas 文档:https://alibaba.github.io/arthas 在线教程 在新版本Arthas里,增加了在线教程,用户可以在线运行Demo,一步步学习Arthas的各种用法,推荐新手尝试: Arthas基础教程 Arthas进阶教程 非常欢迎大家来完善这些教程。 增加内存编绎器支持,在线编辑热更新代码 3.1.0版本里新增命令mc,不是方块游戏mc,而是Memory Compiler。 在之前版本里,增加了redefine命令,可以热更新字节码。但是有个不方便的地方:需要把.class文件上传到服务器上。 在3.1.0版本里,结合jad/mc/redefine 可以完美实现热更新代码。 以 Arthas在线教程 里的UserController为例: 使用jad反编绎代码 jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java 使用vim编绎代码 当 user id 小于1时,也正常返回,不抛出异常: @GetMapping("/user/{id}") public User findUserById(@PathVariable Integer id) { logger.info("id: {}" , id); if (id != null && id < 1) { return new User(id, "name" + id); // throw new IllegalArgumentException("id < 1"); } else { return new User(id, "name" + id); } } 使用mc命令编绎修改后的UserController.java $ mc /tmp/UserController.java -d /tmp Memory compiler output: /tmp/com/example/demo/arthas/user/UserController.class Affect(row-cnt:1) cost in 346 ms 使用redefine命令,因为可以热更新代码 $ redefine /tmp/com/example/demo/arthas/user/UserController.class redefine success, size: 1 丝滑的自动补全 在新版本里,改进了很多命令的自动补全,比如 watch/trace/tt/monitor/stack等。 下面是watch命令的第一个Tab补全结果,用户可以很方便的一步步补全类名,函数名: $ watch com. sun. javax. ch. io. demo. jdk. org. java. 另外,新增加了 jad/sc/sm/redefine 等命令的自动补全支持,多按Tab有惊喜。 新版本的Web console 新版本的Web Console切换到了xtermd.js,更好地支持现代浏览器。 支持Ctrl + C复制 支持全屏 Docker镜像支持 Arthas支持Docker镜像了 用户可以很方便地诊断Docker/k8s里的Java进程 也可以很方便地把Arthas加到自己的基础镜像里 参考: https://alibaba.github.io/arthas/docker.html 重定向重新设计 之前的版本里,Arthas的重定向是会放到一个~/logs/arthas-cache/目录里,违反直觉。 在新版本里,重定向和Linux下面的一致,>/>>的行为也和Linux下一致。 并且,增加了 cat/pwd命令,可以配置使用。 总结 总之,3.1.0版本的Arthas带了非常多的新功能,改进了很多的用户体验,欢迎大家使用反馈。 Arthas在线教程可以学到很多技巧 jad/mc/redefine 一条龙非常强大 丝滑的自动补全值得尝试 新版本的Web Console有惊奇 Release Note: https://github.com/alibaba/arthas/releases/tag/3.1.0
Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。从Arthas上个版本发布,已经过去两个多月了,Arthas 3.1.0版本不仅带来大家投票出来的新LOGO,还带来强大的新功能和更好的易用性,下面一一介绍。 Github: https://github.com/alibaba/arthas 文档:https://alibaba.github.io/arthas 在线教程 在新版本Arthas里,增加了在线教程,用户可以在线运行Demo,一步步学习Arthas的各种用法,推荐新手尝试: Arthas基础教程 Arthas进阶教程 非常欢迎大家来完善这些教程。 增加内存编译器支持,在线编辑热更新代码 3.1.0版本里新增命令mc,不是方块游戏mc,而是Memory Compiler。 在之前版本里,增加了redefine命令,可以热更新字节码。但是有个不方便的地方:需要把.class文件上传到服务器上。 在3.1.0版本里,结合jad/mc/redefine 可以完美实现热更新代码。 以 Arthas在线教程 里的UserController为例: 使用jad反编译代码 jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java 使用vim编译代码 当 user id 小于1时,也正常返回,不抛出异常: @GetMapping("/user/{id}") public User findUserById(@PathVariable Integer id) { logger.info("id: {}" , id); if (id != null && id < 1) { return new User(id, "name" + id); // throw new IllegalArgumentException("id < 1"); } else { return new User(id, "name" + id); } } 使用mc命令编译修改后的UserController.java $ mc /tmp/UserController.java -d /tmp Memory compiler output: /tmp/com/example/demo/arthas/user/UserController.class Affect(row-cnt:1) cost in 346 ms 使用redefine命令,因为可以热更新代码 $ redefine /tmp/com/example/demo/arthas/user/UserController.class redefine success, size: 1 丝滑的自动补全 在新版本里,改进了很多命令的自动补全,比如 watch/trace/tt/monitor/stack等。 下面是watch命令的第一个Tab补全结果,用户可以很方便的一步步补全类名,函数名: $ watch com. sun. javax. ch. io. demo. jdk. org. java. 另外,新增加了 jad/sc/sm/redefine 等命令的自动补全支持,多按Tab有惊喜。 新版本的Web console 新版本的Web Console切换到了xtermd.js,更好地支持现代浏览器。 支持Ctrl + C复制 支持全屏 Docker镜像支持 Arthas支持Docker镜像了 用户可以很方便地诊断Docker/k8s里的Java进程 也可以很方便地把Arthas加到自己的基础镜像里 参考: https://alibaba.github.io/arthas/docker.html 重定向重新设计 之前的版本里,Arthas的重定向是会放到一个~/logs/arthas-cache/目录里,违反直觉。 在新版本里,重定向和Linux下面的一致,>/>>的行为也和Linux下一致。 并且,增加了 cat/pwd命令,可以配置使用。 总结 总之,3.1.0版本的Arthas带了非常多的新功能,改进了很多的用户体验,欢迎大家使用反馈。 Arthas在线教程可以学到很多技巧 jad/mc/redefine 一条龙非常强大 丝滑的自动补全值得尝试 新版本的Web Console有惊奇 Release Note: https://github.com/alibaba/arthas/releases/tag/3.1.0
背景 Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 https://github.com/alibaba/arthas Arthas提供了非常丰富的关于调用拦截的命令,比如 trace/watch/monitor/tt 。但是很多时候我们在排查问题时,需要更多的线索,并不只是函数的参数和返回值。比如在一个spring应用里,想获取到spring context里的其它bean。如果能随意获取到spring bean,那就可以“为所欲为”了。 下面介绍如何利用Arthas获取到spring context。 Demo: https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-arthas-spring-boot Arthas快速开始:https://alibaba.github.io/arthas/quick-start.html 使用tt命令获取到spring context Demo是一个spring mvc应用,请求会经过一系列的spring bean处理,那么我们可以在spring mvc的类里拦截到一些请求。 启动Demo: mvn spring-boot:run 使用Arthas Attach成功之后,执行tt命令来记录RequestMappingHandlerAdapter#invokeHandlerMethod的请求 tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod 然后访问一个网页: http://localhost:8080/ 可以看到Arthas会拦截到这个调用,index是1000,并且打印出: $ watch com.example.demo.Test * 'params[0]@sss' $ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 101 ms. INDEX TIMESTAMP COST(ms IS-RE IS-EX OBJECT CLASS METHOD ) T P ------------------------------------------------------------------------------------------------------------------ 1000 2019-01-27 16:31 3.66744 true false 0x4465cf70 RequestMappingHandlerAda invokeHandlerMethod :54 pter 那么怎样获取到spring context? 可以用tt命令的-i参数来指定index,并且用-w参数来执行ognl表达式来获取spring context: $ tt -i 1000 -w 'target.getApplicationContext()' @AnnotationConfigEmbeddedWebApplicationContext[ reader=@AnnotatedBeanDefinitionReader[org.springframework.context.annotation.AnnotatedBeanDefinitionReader@35dc90ec], scanner=@ClassPathBeanDefinitionScanner[org.springframework.context.annotation.ClassPathBeanDefinitionScanner@72078a14], annotatedClasses=null, basePackages=null, ] Affect(row-cnt:1) cost in 7 ms. 从spring context里获取任意bean 获取到spring context之后,就可以获取到任意的bean了,比如获取到helloWorldService,并调用getHelloMessage()函数: $ tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()' @String[Hello World] Affect(row-cnt:1) cost in 5 ms. 更多的思路 在很多代码里都有static函数或者static holder类,顺滕摸瓜,可以获取很多其它的对象。比如在Dubbo里通过SpringExtensionFactory获取spring context: $ ognl '#context=@com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory@contexts.iterator.next, #context.getBean("userServiceImpl").findUser(1)' @User[ id=@Integer[1], name=@String[Deanna Borer], ] 链接 Arthas: https://github.com/alibaba/arthas https://alibaba.github.io/arthas/tt.html https://alibaba.github.io/arthas/ognl.html