文章目录
Arthas常见使用姿势
官网
https://arthas.aliyun.com/doc/
基本命令
java -jar arthas-boot.jar
通用参数解释
-n 限制打印的条数,如程序执行中,可能有些方法会疯狂打印。
-i 设置打印间隔时间,单位毫秒
-x 属性遍历深度,默认为1。
表达式核心变量说明
public class Advice {
private final ClassLoader loader;
private final Class<?> clazz;
private final ArthasMethod method;
private final Object target;
private final Object[] params;
private final Object returnObj;
private final Throwable throwExp;
private final boolean isBefore;
private final boolean isThrow;
private final boolean isReturn;
}
变量名 | 变量解释 |
---|---|
loader | 本次调用类所在的 ClassLoader |
clazz | 本次调用类的 Class 引用 |
method | 本次调用方法反射引用 |
target | 本次调用类的实例 |
params | 本次调用参数列表,这是一个数组,如果方法是无参方法则为空数组 |
returnObj | 本次调用返回的对象。当且仅当 isReturn==true 成立时候有效,表明方法调用是以正常返回的方式结束。如果当前方法无返回值 void,则值为 null |
throwExp | 本次调用抛出的异常。当且仅当 isThrow==true 成立时有效,表明方法调用是以抛出异常的方式结束。 |
isBefore | 辅助判断标记,当前的通知节点有可能是在方法一开始就通知,此时 isBeforetrue 成立,同时 isThrowfalse 和 isReturn==false,因为在方法刚开始时,还无法确定方法调用将会如何结束。 |
isThrow | 辅助判断标记,当前的方法调用以抛异常的形式结束。 |
isReturn | 辅助判断标记,当前的方法调用以正常返回的形式结束。 |
默认对象,即未设置观察表达式时的默认表达式
{params, target, returnObj}
常用命令
# ognl
ognl '@java.lang.System@out.println("hello")'
# watch
watch demo.MathGame primeFactors -x 2
# 默认观察 {params, target, returnObj}
# trace
trace java.util.Collections sort -n 5 --skipJDKMethod false
# 重载使用
watch Test hello params 'params[0].class.name=="java.lang.Integer"'
watch Test hello params 'params.length==2&&returnObj instanceof java.lang.String'
# stack 输出当前方法被调用的调用路径
stack site.tkgup.Demo test -n 5
# vmtool
vmtool --action getInstances --className site.tkgup.HelloWorld --express "instances[0].container" -x 3
# jad 反编译器(java decompiler java),可做些热替换操作
jad --source-only com.example.Demo > /tmp/Demo.java
# 1、jad得到原文件
jad site.tkgup.Demo
# 2、使用vi/vim修改
# 3、使用 mc 进行编译
mc /tmp/Demo.java
# 4、使用 redefine/retransform 进行加载
redefine /tmp/Demo.class
一些常用特殊案例举例
#1、观察 函数调用 返回时
watch demo.MathGame primeFactors -x 2
# -x 表示遍历深度,可以调整来打印具体的参数和结果内容,默认值是1
# 观察表达式,默认值是{params, target, returnObj}
#2、观察 函数调用 入口时
watch demo.MathGame primeFactors "{params,returnObj}" -x 2 -b
#3、同时观察 函数调用 入口时和返回后
watch demo.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s -n 2
# -n 2,表示只执行两次
#4、条件表达式
watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0"
#5、观察异常信息
watch demo.MathGame primeFactors "{params[0],throwExp}" -e -x 2
# -e 表示抛出异常时才触发
#6、按照耗时进行过滤
watch demo.MathGame primeFactors '{params, returnObj}' '#cost>200' -x 2
# #cost>200(单位是ms)表示只有当耗时大于200ms时才会输出,过滤掉执行时间小于200ms的调用
# watch/stack/trace这个三个命令都支持#cost
# 表示当执行时间超过100ms的时候,才会输出trace的结果:
trace *StringUtils isBlank '#cost>100'
#7、观察当前对象中的属性
watch demo.MathGame primeFactors 'target.illegalArgumentCount'
#8、获取类的静态字段、调用类的静态函数
watch demo.MathGame * '{params,@demo.MathGame@random.nextInt(100)}' -v -n 1 -x 2
# -v 参数打印更多信息,watch/trace/monitor/stack/tt 命令都支持 -v 参数
# 使用-v选项,会打印Condition express的具体值和执行结果,方便确认。
watch -v -x 2 demo.MathGame print 'params' 'params[0] > 100000'
#9、比较枚举值
watch demo.MathGame run 'params[0]==@demo.Outer$Inner@ONE' -x 2
#10、排除掉指定的类
watch javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter
# --exclude-class-pattern 参数可以排除掉指定的类,watch/trace/monitor/stack/tt 命令都支持
#11、访问静态变量
# 在watch命令中访问,会受到classloader的限制,不推荐使用:
watch com.taobao.container.Test test "@com.taobao.container.Test@m"
# 用新版getstatic命令,通过-c指定classloader,可查看任意static变量,同时支持ognl表达式处理
getstatic com.alibaba.arthas.Test n 'entrySet().iterator.{? #this.key.name()=="STOP"}'
#12、调用静态方法
watch com.taobao.container.Test test "@java.lang.Thread@currentThread().getContextClassLoader()"
#13、按条件过滤:
watch com.taobao.container.Test test "{params}" "params[0].{? #this.name == "tkg"}.size()>0" -x 2
#14、匹配线程&正则多个类多个方法
trace -E 'nio\.Thread|test\.Executor' 'select|runAllTasks' '@Thread@currentThread().getName().contains("tkg-thre")&&#cost>500'
#15、调用构造函数
watch demo.Test test '(#test=new java.util.ArrayList(), #test.add("abc"), #test)' -n 1
watch demo.MathGame <init> '{params,returnObj,throwExp}' -v
#16、访问Map中的元素
watch demo.Test test '@demo.Test@map.keys' -n 1
# ongl针对Map接口提供了keys, values这两个虚拟属性,可以像普通属性一样访问
watch demo.Test test '@demo.Test@map.get(@demo.TypeEnum@valueOf("RUN"))' -n 1
watch Test test '@Test@n.entrySet().iterator.{? #this.key.name() == "RUN"}' -n 1
其他技巧
& 后台运行,可 ctrl + z 挂起
jobs 查看后台(arthas)执行的任务
fg job-id 后台任务转前台
bg job-id 前台任务转后台
kill 停止异步执行的命令
关于OGNL
可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。
Ognl类:This class provides static methods for parsing and interpreting OGNL expressions.根据官方解释,这个类是提供一些静态方法去解析表达式。
OgnlContext:This class defines the execution context for an OGNL expression.该类定义OGNL表达式的执行上下文。
public class OgnlContext extends Object implements Map {}
OgnlContext类实现了Map接口,所以它也是一个Map,可以通过put方法往该上下文环境中放元素。该上下文环境中,有两种对象:根对象和普通对象。我们可以使用它的setRoot方法设置根对象。根对象只能有一个,而普通对象则可以有多个。因为它实现了java.utils.Map 的接口。OgnlContext(ognl上下文)=根对象(1个)+非根对象(n个),非根对象要通过"#key"访问,根对象可以省略"#key"格式,只能为"#root"。
获取根对象的属性值,可以直接使用属性名作为表达式,也可以使用#对象名.属性名的方式;获取普通对象的属性值,则必须使用#对象名.属性名的方式获取。(可以将普通对象设置为根对象,但只能有一个根对象,后面设置的根对象会覆盖前面的根对象
OGNL的常见使用
1) 支持对象方法调用,属性取值赋值:如xxx.doSomeSpecial(),"age=18"(操作根)
2) 支持类静态的方法|值调用:格式为 "@[类全名(包括包路径)]@[方法名|值名]" ,如:
@java.lang.String@format('foo%s','bar')--调用类静态方法
@java.lang.Thread$State@RUNNABLE --访问内部类枚举值
@java.lang.Thread@class --访问class对象(#obj.getClass())
@tutorial.MyConstant@APP_NAME--访问类的静态值
3) 支持赋值操作和表达式串联,如:price=100,discount=0.8,calculatePrice()
4) 访问OGNL上下文 (OGNL context)和ActionContext
5) 对于OGNL来说,数组与集合操作一样,获取数组、集合对象,如:#users[1]
6) 操作map对象,如:#users['tkg']
7) 可以使用 || && !
8) null可以直接使用,如 "#obj.do(null, new site.tkg.Boy(18))"
9) 链式调用 "#obj.do(null, (#boy=new site.tkg.Boy(18),#boy.setAge(18),#boy))"
10) 访问外部类对象 target.this$0
OGNL的一些特殊用法与说明
过滤:
? --获取集合中所有满足选择逻辑的对象
^ --获取集合中第一个满足选择逻辑的对象
$ --获取集合中最后一个满足选择逻辑的对象
使用注意:
1.创建集合不用加#,创建map要加#
2.创建类的一个对象,要使用类的完整路径
3.要创建带有初始化值的指定类型的List或Map,可以这样#@java.util.TreeMap@{‘key’:’value’,’key’:’value’,……}
OGNL内置的虚拟属性
Collection | List | Map | Set | Iterator | Enumeration |
---|---|---|---|---|---|
size isEmpty |
size iterator |
size keys values |
size iterator |
next hasNext |
next hasNext nextElement hasMoreElements |
OGNL的个人思考
request, #request.username都可以访问原因推测:
根对象有相应属性,上下文对象同时做了保存
书写转换问题:
watch site.tkgup.Demo traceE '{params,returnObj,throwExp}' -n 5 -x 3 'params[0]=="name"'
params.{#this[0]=='name'}
watch site.tkgup.Demo test '{params,returnObj,throwExp}' -n 5 -x 3 'throwExp != null'
throwExp.{#this != null}
OGNL的杂碎,收集未做验证
OGNL中的#、%和$符号
#用法
a. 访问非根对象属性,如:#session.userName
b. 用于引用当前对象,进行方法调用等,集合搭配(?、^、$)进行过滤、映射
过滤:persons.{?#this.age>20}
映射:persons.{name}
过滤是取行的操作,而投影是取列的操作
在使用过滤操作时,使用#this,用于代表当前正在迭代的集合中的对象,当前元素
c. 构造Map,如:#{'foo1':'bar1', 'foo2':'bar2'}
$用法:
a. 在国际化资源文件中,引用OGNL表达式
reg.agerange=国际化资源信息:年龄必须在${min}同${max}之间
b. 在Struts 2框架的配置文件中引用OGNL表达式
<validators>
<field name="intb">
<field-validator type="int">
<param name="min">10</param>
<param name="max">100</param>
<message>数字必须为${min}为${max}之间!</message>
</field-validator>
</field>
</validators>
%用法(不像是OGNL的操作符)
%符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值
如:
<h3>构造Map</h3>
<s:set name="foobar" value="#{'foo1':'bar1', 'foo2':'bar2'}"/>
<p>The value of key "foo1" is <s:property value="#foobar['foo1']"/></p>
<p>不用%:<s:url value="#foobar['foo1']"/></p>
<p>使用%:<s:url value="%{#foobar['foo1']}"/></p>
结果:
he value of key "foo1" is bar1
不使用%:#foobar['foo1']
使用%:bar1
Struts2标签库 属性值中的%与#号的关系:
1)如果标签的属性值是OGNL表达式,那么无需加上%{}。
2)如果标签的属性值是字符串类型,那么在字符串当中凡是出现的%{}都会被解析成OGNL表达式,解析完毕后再与其他的字符串进行拼接构造出最后的字符串值。
3)我们可以在所有的属性值上加%{},这样如果该属性值是OGNL表达式,那么标签处理类就会将%{}忽略掉