Arthas的表达式太难了?在线调试Ognl表达式!

Arthas的表达式太难了?在线调试Ognl表达式!

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

Arthas里的条件表达式结果表达式

Arthas里的 watch/trace等命令支持条件表达式结果表达式

比如下面的watch命令:

watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0"
  • 结果表达式是: {params[0],target},即把 参数1 和 this 对象组装为一个数组,再打印结果
  • 条件表达式是: params[0]<0 ,即当参数1小于0时,才会触发

更多参考: https://arthas.aliyun.com/doc/watch

使用verbose参数

当我们执行完一个watch命令时,会挂起一直等待拦截到函数调用。但当我们使用了条件表达式时,面临一个困境:

  • 当一直没有打印结果时,是函数没有被调用,还是调用之后,条件表达式结果为false

这时可以使用-v参数,每次函数被调用时,都打印条件表达式的执行结果,例如:

$ watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0" -v
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 114 ms, listenerId: 1
Condition express: params[0]<0 , result: false
Condition express: params[0]<0 , result: true
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2021-04-19 19:48:08; [cost=0.215565ms] result=@ArrayList[
    @Integer[-93817],
    @MathGame[demo.MathGame@533ddba],
]

怎样调试表达式?

使用-v参数可以让我们知道条件表达式的执行结果,但对于复杂的表达式也无能为力。
因此我们增加了下面的在线教程,直接调试ognl表达式。用户也可以把示例工程clone到本地来实践。

下面我们在代码里模拟arthas watch命令执行过程。

首先,把代码clone到本地:

git clone https://github.com/hengyunabc/ognl-demo.git

再打开src/main/java/com/example/ognl/Demo.java

    /**
     * 
     * <pre>
     * watch com.example.ognl.TestService test "{target, params}" "params[0] > 1" -b -x 3
     * </pre>
     */
    public static void atBefore(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target,
            Object[] params) {
        threadLocalWatch.start();
        Advice advice = Advice.newForBefore(loader, clazz, method, target, params);
        Express express = ExpressFactory.threadLocalExpress(advice);

        String watchExpress = "{target, params}";
        String conditionExpress = "params[0] > 1";

        try {
            boolean conditionResult = express.is(conditionExpress);
            System.out.println(
                    "AtEnter, conditionExpress: " + conditionExpress + ", conditionResult: " + conditionResult);
            if (conditionResult) {
                Object object = express.get(watchExpress);
                ObjectView objectView = new ObjectView(object, 3);
                String draw = objectView.draw();
                System.out.println(draw);
            }
        } catch (ExpressException e) {
            e.printStackTrace();
        }
    }

最后,我们在命令行里执行mvn compile exec:java时,会打印出表达式执行结果:

AtEnter, conditionExpress: params[0] > 1, conditionResult: true
@ArrayList[
    @TestService[
    ],
    @Object[][
        @Integer[1000],
        @String[hello],
        @Student[
            id=@Long[1],
            name=@String[tom],
        ],
    ],
]

类似在arthas里执行下面的watch命令:

watch com.example.ognl.TestService test "{target, params, returnObj, #cost}" "params[0] > 1 && #cost > 0.1" -x 3

用户可以自己修改代码里的表达式,然后多次执行调试。

另外,执行下面的命令行会模拟抛出异常的情况:

mvn compile exec:java -DexceptionCase=true

类似在arthas里执行下面的watch命令:

watch com.example.ognl.TestService test "{target, params, throwExp}" "params[0] > 1" -e -x 2

题外话:为什么Arthas选择了ognl

  • ognl表达式基于反射,比较轻量
  • groovy库太大,并且很容易有内存泄露问题
  • JVM去掉了Nashorn JavaScript引擎

实践下来,ognl的确比较稳定,没有出过大问题。

总结

上一篇:asp.net页面事件执行顺序(轉)


下一篇:应用诊断利器Arthas 3.0.5版本发布:提升全平台用户体验