参考:
【Arthas问题排查集】活用ognl表达式 · Issue #11 · alibaba/arthas · GitHub
欢迎大家多多体验牛逼的arthas
0. 背景
Arthas 3.0中使用ognl表达式替换了groovy来实现表达式的求值功能,解决了groovy潜在会出现内存泄露的问题。灵活运用ognl表达式,能够极大提升问题排查的效率。
1. demo
https://github.com/bailuoxi66/snapUpDemo/tree/master/springBoot-Redis-Demo/ArthasQuestionOgnl
2. 服务启动
3. 启动arthas
4. 确认相关用法
0. 牛逼的用法
将test方法进行调整。加入list=null
也就是说:在方法结束之后打印相关的信息。
1. 查看第一个参数
params是参数列表,是一个数组,可以直接通过下标方式访问。如果方法只有一个的话,需要获取这个参数的话,就需要使用 params[0]
注:-n代表只输出一次
[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test params[0] -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 25 ms, listenerId: 2 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:13:27; [cost=0.074765ms] result=@ArrayList[ @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@443b7951], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@7e774085], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@3f8f9dd6], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@aec6354], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@1c655221], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@58d25a40], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@1b701da1], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@726f3b58], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@442d9b6e], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@ee7d9f1], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@15615099], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@1edf1c96], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@368102c8], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@6996db8], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@1963006a], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@7fbe847c], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@7dc5e7b4], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@1ee0005], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@75a1cd57], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@3d012ddd], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@6f2b958e], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@1eb44e46], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@6504e3b2], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@515f550a], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@626b2d4a], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@5e91993f], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@1c4af82c], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@379619aa], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@cac736f], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@5e265ba4], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@156643d4], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@123a439b], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@7de26db8], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@1175e2db], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@36aa7bc2], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@76ccd017], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@182decdb], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@26f0a63f], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@4361bd48], @Pojo[com.example.arthasquestionognl.ognlDemo.Test$Pojo@53bd815b], ]
2. 查看数组中的元素
第一个参数是一个List,想要看List中第一个Pojo对象,可以通过下标方式,也可以通过List的get方法访问。
[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test params[0][0] -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 13 ms, listenerId: 3 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:16:08; [cost=0.074507ms] result=@Pojo[ name=@String[name 0], age=@Integer[2], hobby=null, ] Command execution times exceed limit: 1, so command will exit. You can set it with -n option. [arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test params[0].get(0) -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 14 ms, listenerId: 4 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:16:29; [cost=0.093399ms] result=@Pojo[ name=@String[name 0], age=@Integer[2], hobby=null, ]
查看Pojo的属性
拿到这个Pojo可以,直接访问Pojo的属性,如age
[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test params[0].get(0).age -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 24 ms, listenerId: 5 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:20:32; [cost=0.110691ms] result=@Integer[2] Command execution times exceed limit: 1, so command will exit. You can set it with -n option.
还可以通过下标的方式访问
params[0][0]["age"]
,这个写法等效于
params[0][0].age
:[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test params[0][0]["age"] -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 19 ms, listenerId: 7 watch failed, condition is: null, express is: params[0][0][age], ognl.NoSuchPropertyException: com.taobao.arthas.core.advisor.Advice.age, visit /Users/luoyu/logs/arthas/arthas.log for more details.
这样会报错,这个时候需要加一个单引号
[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test 'params[0][0]["age"]' -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 20 ms, listenerId: 9 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:24:24; [cost=0.349835ms] result=@Integer[2] Command execution times exceed limit: 1, so command will exit. You can set it with -n option.
3. 集合投影
有时候我们只需要抽取对象数组中的某一个属性,这种情况可以通过投影来实现,比如要将Pojo对象列表中的name属性单独抽出来,可以通过
params[0].{name}
这个表达式来实现。 ognl会便利params[0]这个List取出每个对象的name属性,重新组装成一个新的数组。用法相当于Java stream中的map函数。[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test 'params[0].{name}' -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 20 ms, listenerId: 10 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:28:27; [cost=0.199219ms] result=@ArrayList[ @String[name 0], @String[name 1], @String[name 2], @String[name 3], @String[name 4], @String[name 5], @String[name 6], @String[name 7], @String[name 8], @String[name 9], @String[name 10], @String[name 11], @String[name 12], @String[name 13], @String[name 14], @String[name 15], @String[name 16], @String[name 17], @String[name 18], @String[name 19], @String[name 20], @String[name 21], @String[name 22], @String[name 23], @String[name 24], @String[name 25], @String[name 26], @String[name 27], @String[name 28], @String[name 29], @String[name 30], @String[name 31], @String[name 32], @String[name 33], null, @String[name 35], @String[name 36], @String[name 37], @String[name 38], @String[name 39], ]
4. 集合过滤
有时候还需要针对集合对象按某种条件进行过滤,比如想找出所有age大于5的Pojo的name,可以这样写
[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test "params[0].{? #this.age > 5}.{name}" -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 21 ms, listenerId: 11 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:30:04; [cost=0.326398ms] result=@ArrayList[ @String[name 4], @String[name 5], @String[name 6], @String[name 7], @String[name 8], @String[name 9], @String[name 10], @String[name 11], @String[name 12], @String[name 13], @String[name 14], @String[name 15], @String[name 16], @String[name 17], @String[name 18], @String[name 19], @String[name 20], @String[name 21], @String[name 22], @String[name 23], @String[name 24], @String[name 25], @String[name 26], @String[name 27], @String[name 28], @String[name 29], @String[name 30], @String[name 31], @String[name 32], @String[name 33], @String[name 34], @String[name 35], @String[name 36], @String[name 37], @String[name 38], @String[name 39], ]
其中
{? #this.age > 5}
相当于stream里面的filter,后面的
name
相当于stream里面的map那如果要找到第一个age大于5的Pojo的name,怎么办呢?可以用
^
或
$
来进行第一个或最后一个的匹配,像下面这样:[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test "params[0].{^ #this.age > 5}.{name}" -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 16 ms, listenerId: 12 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:31:09; [cost=0.263925ms] result=@ArrayList[ @String[name 4], ] Command execution times exceed limit: 1, so command will exit. You can set it with -n option. [arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test "params[0].{$ #this.age > 5}.{name}" -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 22 ms, listenerId: 13 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:31:16; [cost=0.654726ms] result=@ArrayList[ @String[name 39], ]
5. 多行表达式
有些表达式一行之内无法表达,需要多行才能表达,应该怎么写的?比如,假设我们要把所有Pojo的name拿出来,再往里面新加一个新的元素,在返回新的列表,应该如何写?可以通过中括号将多个表达式串联起来,最后一个表达式的返回值代表整个表达式的最终结果。临时变量可以用
#
来表示。[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test '(#test=params[0].{name}, #test.add("abc"), #test)' -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 25 ms, listenerId: 14 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:32:32; [cost=0.302864ms] result=@ArrayList[ @String[name 0], @String[name 1], @String[name 2], @String[name 3], @String[name 4], @String[name 5], @String[name 6], @String[name 7], @String[name 8], @String[name 9], @String[name 10], @String[name 11], @String[name 12], @String[name 13], @String[name 14], @String[name 15], @String[name 16], @String[name 17], @String[name 18], @String[name 19], @String[name 20], @String[name 21], @String[name 22], @String[name 23], @String[name 24], @String[name 25], @String[name 26], @String[name 27], @String[name 28], @String[name 29], @String[name 30], @String[name 31], @String[name 32], @String[name 33], @String[name 34], @String[name 35], @String[name 36], null, @String[name 38], @String[name 39], @String[abc], ]
6. 调用构造函数
调用构造函数,必须要指定要创建的类的
全类名
。比如下面的例子中,创建一个新的list,然后添加一个新的元素,然后返回添加后的list。[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test '(#test=new java.util.ArrayList(), #test.add("abc"), #test)' -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 22 ms, listenerId: 15 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:34:49; [cost=0.207831ms] result=@ArrayList[ @String[abc], ]
7. 调用静态变量
可以通过
@class@filed
方式访问,注意需要填写全类名[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test @com.example.arthasquestionognl.ognlDemo.Test@m -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 21 ms, listenerId: 17 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:36:43; [cost=0.195456ms] result=@HashMap[ @String[a]:@String[aaa], @String[b]:@String[bbb], ]
调用静态方法
[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test '@com.example.arthasquestionognl.ognlDemo.Test@invoke("luoyu test")' -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 22 ms, listenerId: 20 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:39:15; [cost=0.141093ms] result=null Command execution times exceed limit: 1, so command will exit. You can set it with -n option.
服务日志文件:【可以看到,给的入参,有打印出来】
8. 访问Map中的元素
Test.n是一个HashMap,假设要获取这个Map的所有key,ongl针对Map接口提供了
keys
,
values
这两个虚拟属性,可以像普通属性一样访问。[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test '@com.example.arthasquestionognl.ognlDemo.Test@n.keys' -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 23 ms, listenerId: 21 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:42:01; [cost=0.169008ms] result=@KeySet[ @Type[STOP], @Type[RUN], ]
通过迭代器+过滤的方式:
[arthas@30992]$ watch com.example.arthasquestionognl.ognlDemo.Test test '@com.example.arthasquestionognl.ognlDemo.Test@n.entrySet().iterator.{? #this.key.name() == "RUN"}' -n 1 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 24 ms, listenerId: 28 method=com.example.arthasquestionognl.ognlDemo.Test.test location=AtExit ts=2022-01-06 17:46:52; [cost=0.165398ms] result=@ArrayList[ @Node[RUN=aaa], ] Command execution times exceed limit: 1, so command will exit. You can set it with -n option.