arthas 使用ognl表达式理解【整理】

  • Post author:
  • Post category:其他


参考:

【Arthas问题排查集】活用ognl表达式 · Issue #11 · alibaba/arthas · GitHub

欢迎大家多多体验牛逼的arthas

0. 背景

Arthas 3.0中使用ognl表达式替换了groovy来实现表达式的求值功能,解决了groovy潜在会出现内存泄露的问题。灵活运用ognl表达式,能够极大提升问题排查的效率。

ognl官方文档:

OGNL – Apache Commons OGNL – Language Guide

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.