Arthas教程

19 篇文章 1 订阅
13 篇文章 0 订阅

Arthas 介绍

什么是Arthas,它能为我们做什么

Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。

当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到JVM的实时运行状态?

Arthas支持JDK 6+,支持Linux/Mac/Winodws,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

Arthas相关地址

基础教程

启动arthas-boot

下载arthas-boot.jar,再用java -jar命令启动

wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar --target-ip 0.0.0.0

arthas-bootArthas的启动程序,它启动后,会列出所有的Java进程。

选择需要诊断的目标进程,再Enter/回车,Attach成功之后,会打印Arthas LOGO。

输入 help 获取到更多命令的帮助信息。输入对应命令+ --help 查看该命令帮助信息。如dashboard --help

常用命令

Dashboard

dashboard 命令可以查看当前系统的实时数据面板。

输入 Q 或者 Ctrl+C 可以退出dashboard命令。

Thread

thread 命令会打印线程的栈。

Arthas支持管道,可以用 thread 1 | grep main 查找到main class

可以看到main classdemo.MathGame

Sc

可以通过 sc 命令来查找JVM里已加载的类:

sc -d *MathGame

Jad

可以通过 jad 命令来反编译代码:

jad demo.MathGame

通过--source-only参数可以只打印出在反编译的源代码

jad --source-only com.example.demo.arthas.user.UserController

Watch

通过watch命令可以查看函数的参数/返回值/异常信息。

watch demo.MathGame primeFactors returnObj

输入 Q 或者 Ctrl+C 退出watch命令。

Exit/Shutdown

  • 退出Arthas

    exit 或者 quit 命令可以退出Arthas。

    退出Arthas之后,还可以再次用 java -jar arthas-boot.jar 来连接。

  • 彻底退出Arthas

    exit/quit命令只是退出当前session,arthas server还在目标进程中运行。

    想完全退出Arthas,可以执行 shutdown 命令。

进阶教程

常用命令

查看JVM信息

sysprop
  • 可以通过sysprop打印所有的System Properties信息

    sysprop
    
  • 也可以指定单个key:

    sysprop java.version
    
  • 也可以通过grep来过滤:

    sysprop | grep user
    
  • 可以设置新的value:sysprop key value 例如:

    sysprop testKey testValue
    

sysenv

可以sysenv命令获取到环境变量。和sysprop命令类似。

sysenv

jvm

jvm 命令会打印出JVM的各种详细信息。

jvm

查看已加载的类

sc

sc 命令可以查找到所有JVM已经加载到的类。

如果搜索的是接口,还会搜索所有的实现类。比如查看所有的Filter实现类:

sc javax.servlet.Filter

通过-d参数,可以打印出类加载的具体信息,很方便查找类加载问题。

sc -d javax.servlet.Filter

sc支持通配,比如搜索所有的StringUtils

sc *StringUtils
sm

sm命令则是查找类的具体函数。

sm java.math.RoundingMode

通过-d参数可以打印函数的具体属性

sm -d java.math.RoundingMode

也可以查找特定的函数,比如查找构造函数

sm java.math.RoundingMode <init>

使用技巧

自动补全

Arthas支持丰富的自动补全功能,在使用有疑惑时,可以输入Tab来获取更多信息。

比如输入 sysprop java. 之后,再输入Tab,会补全出对应的key:

readline的快捷键支持

Arthas支持常见的命令行快捷键,比如Ctrl + A跳转行首,Ctrl + E跳转行尾。

更多的快捷键可以用 keymap 命令查看。

历史命令的补全

如果想再执行之前的命令,可以在输入一半时,按Up/↑ 或者 Ddown/↓,来匹配到之前的命令。

比如之前执行过sysprop java.version,那么在输入sysprop ja之后,可以输入Up/↑,就会自动补全为sysprop java.version

如果想查看所有的历史命令,也可以通过 history 命令查看到。

pipeline

Arthas支持在pipeline之后,执行一些简单的命令,比如:

sysprop | grep java
sysprop | wc -l

Ognl

在Arthas里,有一个单独的ognl命令,可以动态执行代码。

  • 调用static函数

    ognl '@java.lang.System@out.println("hello ognl")'
    
  • 获取静态类的静态字段

    获取UserController类里的logger字段

    ognl -c 1be6f5c3 @com.example.demo.arthas.user.UserController@logger
    

    还可以通过-x参数控制返回值的展开层数。

    ognl -c 1be6f5c3 -x 2 @com.example.demo.arthas.user.UserController@logger
    
  • 执行多行表达式,赋值给临时变量,返回一个List

    ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
    

更多

在Arthas里ognl表达式是很重要的功能,在很多命令里都可以使用ognl表达式。

一些更复杂的用法,可以参考:

  • OGNL特殊用法请参考:https://github.com/alibaba/arthas/issues/71
  • OGNL表达式官方指南:https://commons.apache.org/proper/commons-ognl/language-guide.html

使用案例

排查函数调用异常

现象

目前,访问 http://localhost/user/0 ,会返回500异常

curl http://localhost/user/0

但请求的具体参数,异常栈是什么呢?

查看UserController的 参数/异常

在Arthas里执行

watch com.example.demo.arthas.user.UserController * '{params, throwExp}'
  1. 第一个参数是类名,支持通配
  2. 第二个参数是函数名,支持通配

访问 curl http://localhost/user/0 ,watch命令会打印调用的参数和异常

​ 可以看到实际抛出的异常是IllegalArgumentException

​ 可以输入 Q 或者 Ctrl+C 退出watch命令。

​ 如果想把获取到的结果展开,可以用-x参数

返回值表达式

在上面的例子里,第三个参数是返回值表达式,它实际上是一个ognl表达式,它支持一些内置对象:

  • loader
  • clazz
  • method
  • target
  • params
  • returnObj
  • throwExp
  • isBefore
  • isThrow
  • isReturn

你可以利用这些内置对象来组成不同的表达式。比如返回一个数组

watch com.example.demo.arthas.user.UserController * '{params[0], target, returnObj}'

更多参考: https://alibaba.github.io/arthas/advice-class.html

条件表达式

watch命令支持在第4个参数里写条件表达式,比如

watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'

当访问 http://localhost/user/1时,watch命令没有输出

当访问 http://localhost/user/111时,watch会打印出结果

当异常时捕获

watch命令支持-e选项,表示只捕获抛出异常时的请求

按照耗时进行过滤

watch命令支持按请求耗时进行过滤

watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'

热更新代码

通过jad/mc/redefine 命令实现动态更新代码的功能

现象

目前,访问 http://localhost/user/0 ,会返回500异常

下面通过热更新代码,修改这个逻辑。

jad反编译UserController
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

jad反编译的结果保存在 /tmp/UserController.java文件里了,然后用vim来编辑/tmp/UserController.java

vim /tmp/UserController.java

我们将代码修改为如下

sc查找加载UserController的ClassLoader
sc -d *UserController | grep classLoaderHash

可以发现是 spring boot LaunchedURLClassLoader@1be6f5c3 加载的。

mc编译UserController

保存好/tmp/UserController.java之后,使用mc(Memory Compiler)命令来编译,并且通过-c参数指定ClassLoader

mc -c 1be6f5c3 /tmp/UserController.java -d /tmp

redefine重新加载新编译好class文件

使用redefine命令重新加载新编译好的UserController.class

redefine /tmp/com/example/demo/arthas/user/UserController.class

查看修改后的结果

redefine成功之后,再次访问 http://localhost/user/0 ,结果如下:

动态更新应用日志级别

查找UserController的ClassLoader
sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash

用ognl获取logger
ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'

可以知道UserController@logger实际使用的是logback。可以看到level=null,则说明实际最终的level是从root logger里来的。

单独设置UserController的logger level
ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)'

再次获取UserController@logger,可以发现已经是DEBUG

修改logback的全局logger level

通过获取root logger,可以修改全局的logger level

ognl -c 1be6f5c3 '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'

排查logger冲突问题

确认应用使用的logger系统

UserController为例,它使用的是slf4j api,但实际使用到的logger系统是logback

ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'
获取logback实际加载的配置文件
ognl -c 1be6f5c3 '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'

查找可能存在的logger配置文件
classloader -c 1be6f5c3 -r logback-spring.xml

可以知道加载的配置的具体来源,尝试加载容易冲突的文件

获取Spring Context

案例步骤:获取spring context -> 获取bean -> 调用函数

获取spring context

使用tt命令获取到spring context

tt即 TimeTunnel,它可以记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。参考文档:https://alibaba.github.io/arthas/tt.html

tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod

访问

http://localhost/user/1

http://localhost/user/0

http://localhost/user/0

可以看到tt命令捕获到了如下请求

从调用记录里获取到spring context

使用tt命令从调用记录里获取到spring context

输入 Q 或者 Ctrl + C 退出上面的 tt -t命令。

输入如下命令查看index为1000的请求的信息

tt -i 1000 -w 'target.getApplicationContext()'

获取spring bean,并调用函数
tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'


  • 5
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿★永

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值