Arthas watch 命令实战指南:函数执行数据观测的 4 个事件点与 OGNL 表达式全解析 📅 发布时间:2026/9/19 13:48:22 👁 浏览次数: Arthas watch 命令实战指南函数执行数据观测的 4 个事件点与 OGNL 表达式全解析【免费下载链接】arthasAlibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas项目地址: https://gitcode.com/gh_mirrors/ar/arthas本指南完整讲解 Alibaba Java 诊断利器 Arthas 的watch命令通过 4 个观察事件点-b调用前、-e异常后、-s返回后、-f结束后与 OGNL 观察表达式实时观测指定方法的入参、当前对象、返回值与抛出异常。阅读后你将掌握watch全部参数含义、表达式核心变量Advice的使用、条件过滤与耗时过滤技巧并能结合源码理解其底层监听器工作原理直接上手排查线上函数的调用行为。概述什么是 watchwatch是 Arthas 中用于函数执行数据观测的核心命令它让你能方便地观察到指定函数的调用情况。能观察到的范围包括入参params当前对象target返回值returnObj抛出异常throwExp观测的核心方式是通过编写 OGNL 表达式对上述变量进行查看。在 WatchCommand.java 中命令自身的描述即为“Display the input/output parameter, return object, and thrown exception of specified method invocation”源码中给出的典型用法示例包括watch org.apache.commons.lang.StringUtils isBlank watch org.apache.commons.lang.StringUtils isBlank {params, target, returnObj, throwExp} -x 2 watch *StringUtils isBlank params[0] params[0].length1 watch *StringUtils isBlank params #cost100 watch -f *StringUtils isBlank params watch -E -b org\.apache\.commons\.lang\.StringUtils isBlank params[0] watch javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter watch OuterClass$InnerClass参数说明watch的参数比较多主要是因为它能在 4 个不同的场景观察对象。完整参数如下参数名称参数说明class-pattern类名表达式匹配method-pattern函数名表达式匹配express观察表达式默认值{params, target, returnObj}condition-express条件表达式[b]在函数调用之前观察[e]在函数异常之后观察[s]在函数返回之后观察[f]在函数结束之后(正常返回和异常返回)观察[E]开启正则表达式匹配默认为通配符匹配[x:]指定输出结果的属性遍历深度默认为 1最大值是 4[c:]指定 classloader hash只增强该 classloader 加载的类[m arg]指定 Class 最大匹配数量默认值为 50。长格式为[maxMatch arg]。除上述文档列出的参数外从 WatchCommand.java 源码中还可以看到以下隐藏/扩展参数参数说明源码依据-n/--limits观察结果的最大输出次数达到次数后自动结束命令WatchCommand.java-M/--sizeLimit输出结果的大小上限字节必须大于 0默认值来自全局options object-size-limitWatchCommand.java-v/--verbose打印条件表达式Condition express的具体值和执行结果WatchCommand.java需要特别说明的是watch命令定义了 4 个观察事件点即-b函数调用前、-e函数异常后、-s函数返回后、-f函数结束后4 个观察事件点-b、-e、-s默认关闭-f默认打开当指定观察点被打开后在相应事件点会对观察表达式进行求值并输出这里要注意函数入参和函数出参的区别有可能在中间被修改导致前后不一致除了-b事件点params代表函数入参外其余事件都代表函数出参当使用-b时由于观察事件点是在函数调用前此时返回值或异常均不存在在watch命令的结果里会打印出location信息。location有三种可能值AtEnter、AtExit、AtExceptionExit分别对应函数入口、函数正常 return、函数抛出异常。观察表达式与条件表达式观察表达式的构成主要由 ognl 表达式组成所以你可以这样写{params,returnObj}只要是一个合法的 ognl 表达式都能被正常支持。在 Constants.java 中命令对观察表达式的动态求值变量有完整定义target : the object clazz : the objects class method : the constructor or method params : the parameters array of method params[0..n]: the element of parameters array returnObj : the returned object of method throwExp : the throw exception of method isReturn : the method ended by return isThrow : the method ended by throwing exception #cost : the execution time in ms of method invocation其中#cost是本次方法调用的耗时毫秒可用于耗时过滤。条件表达式同样是 OGNL 风格Constants.java 中给出的示例包括11、true、params.length0、#cost100等。表达式核心变量Advice观察的维度比较多主要体现在参数Advice的数据结构上。Advice参数最主要是封装了通知节点的所有信息详细描述请参考表达式核心变量。其源码结构位于 Advice.java核心字段如下变量名变量解释loader本次调用类所在的 ClassLoaderclazz本次调用类的 Class 引用method本次调用方法反射引用target本次调用类的实例params本次调用参数列表这是一个数组如果方法是无参方法则为空数组returnObj本次调用返回的对象。当且仅当isReturntrue成立时候有效表明方法调用是以正常返回的方式结束。如果当前方法无返回值void则值为 nullthrowExp本次调用抛出的异常。当且仅当isThrowtrue成立时有效表明方法调用是以抛出异常的方式结束。isBefore辅助判断标记当前的通知节点有可能是在方法一开始就通知此时isBeforetrue成立同时isThrowfalse和isReturnfalse因为在方法刚开始时还无法确定方法调用将会如何结束。isThrow辅助判断标记当前的方法调用以抛异常的形式结束。isReturn辅助判断标记当前的方法调用以正常返回的形式结束。所有变量都可以在表达式中直接使用如果在表达式中编写了不符合 OGNL 脚本语法或者引入了不在表格中的变量则退出命令的执行用户可以根据当前的异常信息修正条件表达式或观察表达式。从源码看Advice对象由三个工厂方法按事件点构造Advice.java 中newForBefore(...)函数调用前构造此时returnObj与throwExp均为 nullnewForAfterReturning(...)函数正常返回后构造携带returnObjnewForAfterThrowing(...)函数抛出异常后构造携带throwExp。这从实现层面印证了文档中“使用-b时返回值或异常均不存在”的说明。使用参考启动 Demo启动快速入门里的math-game。该示例程序的源码位于 MathGame.java其中primeFactors(int number)方法在入参小于 2 时会抛出IllegalArgumentException否则返回质因数分解结果的ArrayList是演示watch观察正常返回与异常抛出两种场景的理想对象。观察函数调用返回时的参数、this 对象和返回值::: tip 观察表达式默认值是{params, target, returnObj}:::$ watch demo.MathGame primeFactors -x 2 Press Q or CtrlC to abort. Affect(class count: 1 , method count: 1) cost in 32 ms, listenerId: 5 methoddemo.MathGame.primeFactors locationAtExceptionExit ts2021-08-31 15:22:57; [cost0.220625ms] resultArrayList[ Object[][ Integer[-179173], ], MathGame[ randomRandom[java.util.Random31cefde0], illegalArgumentCountInteger[44], ], null, ] methoddemo.MathGame.primeFactors locationAtExit ts2021-08-31 15:22:58; [cost1.020982ms] resultArrayList[ Object[][ Integer[1], ], MathGame[ randomRandom[java.util.Random31cefde0], illegalArgumentCountInteger[44], ], ArrayList[ Integer[2], Integer[2], Integer[26947], ], ]上面的结果里说明函数被执行了两次第一次结果是locationAtExceptionExit说明函数抛出异常了因此returnObj是 null在第二次结果里是locationAtExit说明函数正常返回因此可以看到returnObj结果是一个 ArrayList。指定 Class 最大匹配数量$ watch demo.MathGame primeFactors -m 1 Press Q or CtrlC to abort. Affect(class count: 1 , method count: 1) cost in 302 ms, listenerId: 3 methoddemo.MathGame.primeFactors locationAtExceptionExit ts2022-12-25 19:58:41; [cost0.222419ms] resultArrayList[ Object[][isEmptyfalse;size1], MathGame[demo.MathGame3bf400], null, ] methoddemo.MathGame.primeFactors locationAtExceptionExit ts2022-12-25 19:58:51; [cost0.046928ms] resultArrayList[ Object[][isEmptyfalse;size1], MathGame[demo.MathGame3bf400], null, ]-m长格式--maxMatch用于限制匹配到的 Class 数量上限默认值为 50可有效避免通配符过宽时增强过多类。从 EnhancerCommand 的继承关系看watch属于增强类命令-m在增强匹配阶段即生效。指定 ClassLoader 增强当同名类被多个 classloader 加载时可以先用sc -d查看 classloader hash然后用-c指定增强的 classloadersc -d com.example.Foo watch -c 3d4eac69 com.example.Foo bar {params,returnObj}-c参数在 WatchCommand.java 中通过继承父类EnhancerCommand的setHashCode实现含义为“The hash code of the special classs classLoader”。观察函数调用入口的参数和返回值$ watch demo.MathGame primeFactors {params,returnObj} -x 2 -b Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 50 ms. ts2018-12-03 19:23:23; [cost0.0353ms] resultArrayList[ Object[][ Integer[-1077465243], ], null, ]对比前一个例子返回值为空事件点为函数执行前因此获取不到返回值。同时观察函数调用前和函数返回后$ watch demo.MathGame primeFactors {params,target,returnObj} -x 2 -b -s -n 2 Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 46 ms. ts2018-12-03 19:29:54; [cost0.01696ms] resultArrayList[ Object[][ Integer[1], ], MathGame[ randomRandom[java.util.Random522b408a], illegalArgumentCountInteger[13038], ], null, ] ts2018-12-03 19:29:54; [cost4.277392ms] resultArrayList[ Object[][ Integer[1], ], MathGame[ randomRandom[java.util.Random522b408a], illegalArgumentCountInteger[13038], ], ArrayList[ Integer[2], Integer[2], Integer[2], Integer[5], Integer[5], Integer[73], Integer[241], Integer[439], ], ]参数里-n 2表示只执行两次这里输出结果中第一次输出的是函数调用前的观察表达式的结果第二次输出的是函数返回后的表达式的结果结果的输出顺序和事件发生的先后顺序一致和命令中-s -b的顺序无关。从 WatchAdviceListener.java 源码可以看到这一行为的具体实现监听器的before回调负责-b事件点输出afterReturning回调负责-s事件点输出两者均通过watching()方法构造WatchModel并按事件发生的先后顺序写入结果因此输出顺序天然与事件顺序一致。调整-x的值观察具体的函数参数值$ watch demo.MathGame primeFactors {params,target} -x 3 Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 58 ms. ts2018-12-03 19:34:19; [cost0.587833ms] resultArrayList[ Object[][ Integer[1], ], MathGame[ randomRandom[ serialVersionUIDLong[3905348978240129619], seedAtomicLong[3133719055989], multiplierLong[25214903917], addendLong[11], maskLong[281474976710655], DOUBLE_UNITDouble[1.1102230246251565E-16], BadBoundString[bound must be positive], BadRangeString[bound must be greater than origin], BadSizeString[size must be non-negative], seedUniquifierAtomicLong[-3282039941672302964], nextNextGaussianDouble[0.0], haveNextNextGaussianBoolean[false], serialPersistentFieldsObjectStreamField[][isEmptyfalse;size3], unsafeUnsafe[sun.misc.Unsafe2eaa1027], seedOffsetLong[24], ], illegalArgumentCountInteger[13159], ], ]-x表示遍历深度可以调整来打印具体的参数和结果内容默认值是 1-x最大值是 4防止展开结果占用太多内存。用户可以在ognl表达式里指定更具体的 field。这一上限在 ObjectView.java 中有直接证据public static final int MAX_DEEP 4;并且构造时this.deep deep MAX_DEEP ? MAX_DEEP : deep;即使命令行传入大于 4 的值也会被强制截断到 4。条件表达式的例子$ watch demo.MathGame primeFactors {params[0],target} params[0]0 Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 68 ms. ts2018-12-03 19:36:04; [cost0.530255ms] resultArrayList[ Integer[-18178089], MathGame[demo.MathGame41cf53f9], ]只有满足条件的调用才会有响应。条件表达式的判定在 WatchAdviceListener.java 的watching()方法中实现先调用isConditionMet(command.getConditionExpress(), advice, cost)判断只有结果为 true 时才执行观察表达式并输出结果。观察异常信息的例子$ watch demo.MathGame primeFactors {params[0],throwExp} -e -x 2 Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 62 ms. ts2018-12-03 19:38:00; [cost1.414993ms] resultArrayList[ Integer[-1120397038], java.lang.IllegalArgumentException: number is: -1120397038, need 2 at demo.MathGame.primeFactors(MathGame.java:46) at demo.MathGame.run(MathGame.java:24) at demo.MathGame.main(MathGame.java:16) , ]-e表示抛出异常时才触发express 中表示异常信息的变量是throwExp。按照耗时进行过滤$ watch demo.MathGame primeFactors {params, returnObj} #cost200 -x 2 Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 66 ms. ts2018-12-03 19:40:28; [cost2112.168897ms] resultArrayList[ Object[][ Integer[1], ], ArrayList[ Integer[5], Integer[428379493], ], ]#cost200单位是ms表示只有当耗时大于 200ms 时才会输出过滤掉执行时间小于 200ms 的调用。#cost的取值来自 WatchAdviceListener.java 中的ThreadLocalWatch在before回调中调用threadLocalWatch.start()开始计时在watching()中通过threadLocalWatch.costInMillis()得到本次调用的毫秒耗时随后作为变量注入条件表达式与观察表达式。观察当前对象中的属性如果想查看函数运行前后当前对象中的属性可以使用target关键字代表当前对象$ watch demo.MathGame primeFactors target Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 52 ms. ts2018-12-03 19:41:52; [cost0.477882ms] resultMathGame[ randomRandom[java.util.Random522b408a], illegalArgumentCountInteger[13355], ]然后使用target.field_name访问当前对象的某个属性$ watch demo.MathGame primeFactors target.illegalArgumentCount Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 67 ms. ts2018-12-03 20:04:34; [cost131.303498ms] resultInteger[8] ts2018-12-03 20:04:35; [cost0.961441ms] resultInteger[8]获取类的静态字段、调用类的静态函数的例子watch demo.MathGame * {params,demo.MathGamerandom.nextInt(100)} -v -n 1 -x 2 [arthas6527]$ watch demo.MathGame * {params,demo.MathGamerandom.nextInt(100)} -n 1 -x 2 Press Q or CtrlC to abort. Affect(class count: 1 , method count: 5) cost in 34 ms, listenerId: 3 ts2021-01-05 21:35:20; [cost0.173966ms] resultArrayList[ Object[][ Integer[-138282], ], Integer[89], ]注意这里使用Thread.currentThread().getContextClassLoader()加载使用精确classloaderognl 更好。这里演示了 OGNL 静态成员访问语法类全名静态字段/静态方法可以结合watch观察函数执行时的静态上下文。排除掉指定的类::: tip watch/trace/monitor/stack/tt 命令都支持--exclude-class-pattern参数 :::使用--exclude-class-pattern参数可以排除掉指定的类比如watch javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter当class-pattern使用通配符匹配到多个类、而其中部分类不需要增强时该参数非常实用。从 WatchCommand.java 的getClassNameExcludeMatcher()方法可以看到排除模式同样复用类名匹配器进行过滤。不匹配子类默认情况下 watch/trace/monitor/stack/tt 命令都会匹配子类。如果想不匹配可以通过全局参数关掉options disable-sub-class true使用 -v 参数打印更多信息::: tip watch/trace/monitor/stack/tt 命令都支持-v参数 :::当命令执行之后没有输出结果有两种可能匹配到的函数没有被执行条件表达式结果是 false。但用户区分不出是哪种情况。使用-v选项则会打印Condition express的具体值和执行结果方便确认。比如$ watch -v -x 2 demo.MathGame print params params[0] 100000 Press Q or CtrlC to abort. Affect(class count: 1 , method count: 1) cost in 29 ms, listenerId: 11 Condition express: params[0] 100000 , result: false Condition express: params[0] 100000 , result: false Condition express: params[0] 100000 , result: true ts2020-12-02 22:38:56; [cost0.060843ms] resultObject[][ Integer[200033], ArrayList[ Integer[200033], ], ] Condition express: params[0] 100000 , result: true ts2020-12-02 22:38:57; [cost0.052877ms] resultObject[][ Integer[123047], ArrayList[ Integer[29], Integer[4243], ], ]可以看到-v模式下每次调用都会打印Condition express: ... , result: true/false条件不满足的调用result 为 false不输出观察结果这就帮助用户区分了上述两种情况。该行为在 WatchAdviceListener.java 中有对应实现当开启 verbose 时即使条件表达式结果为 false也会先打印Condition express: ... , result: ...再决定是否输出观察结果。底层原理WatchAdviceListener 与 Advice 的协作结合源码可以梳理watch的完整调用链用户在控制台输入watch命令后WatchCommand.java 解析参数class-pattern、method-pattern、express、condition-express、-b/-e/-s/-f等并通过getAdviceListener创建 WatchAdviceListener.java 实例匹配到的类/方法被 Arthas 字节码增强后在方法调用的事件点回调监听器的before/afterReturning/afterThrowing方法回调中通过 Advice.java 的工厂方法构造通知对象封装loader、clazz、method、target、params、returnObj、throwExp等上下文watching()方法计算耗时#cost先判定条件表达式再对观察表达式求值最后封装为WatchModel输出并通过-n的次数限制决定是否结束命令。值得注意的是 WatchAdviceListener.java 中的isFinish()方法private boolean isFinish() { return command.isFinish() || !command.isBefore() !command.isException() !command.isSuccess(); }这从代码层面印证了“-f默认打开”的行为——当-b、-e、-s均未显式开启时afterReturning与afterThrowing回调都会经由finishing()输出观察结果。小结watch命令是 Arthas 排查线上函数行为的利器核心要点可归纳为4 个事件点-b调用前、-e异常后、-s返回后、-f结束后默认开启params在-b之外的事件点代表出参表达式体系观察表达式与条件表达式均为 OGNL 语法可直接使用Advice提供的params、target、returnObj、throwExp、clazz、method、loader以及#cost变量输出控制-x控制遍历深度14-M控制结果字节上限-n控制输出次数-v用于诊断条件不命中的原因匹配控制-E切换正则匹配、-c指定 classloader、-m限制匹配类数量、--exclude-class-pattern排除指定类全局options disable-sub-class true可关闭子类匹配。建议结合快速入门启动math-game逐一实践上述示例再对照 WatchCommand.java、WatchAdviceListener.java 与 Advice.java 深入理解其内部机制。【免费下载链接】arthasAlibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas项目地址: https://gitcode.com/gh_mirrors/ar/arthas创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考